Make all progression headers inclusive, and decrement last for

last-exclusive progressions (i.e., "until" progressions and loop over
array indices).

This change makes it possible to correctly implement the handling of
"step" progressions. Computing the last element of a stepped progression
requires that the last is inclusive.

Also invert the while loop (into if + do-while) that is used when
lowering for-loops over progressions that cannot overflow. This keeps
the performance characteristics closer to the ForLoopsLowering in
kotlin-native, since the goal is to converge to this shared version.

Also used IrType instead of KotlinType, where possible.

 https://github.com/JetBrains/kotlin/pull/2390
 https://github.com/JetBrains/kotlin/pull/2305
This commit is contained in:
Mark Punzalan
2019-06-14 00:59:47 -07:00
committed by Mikhael Bogdanov
parent 39f6416757
commit de1e27c584
75 changed files with 1873 additions and 377 deletions
@@ -0,0 +1,20 @@
// IGNORE_BACKEND: JVM_IR
fun test(a: Char, b: Char): String {
var s = ""
for (i in a until b) {
s += i
}
return s
}
// JVM non-IR uses while.
// JVM IR uses if + do-while. In addition, for "until" progressions, there is a check that the range is not empty: upper bound != MIN_VALUE.
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 1 IF_ICMPGE
// 1 IF
@@ -0,0 +1,22 @@
// TARGET_BACKEND: JVM_IR
fun test(a: Char, b: Char): String {
var s = ""
for (i in a until b) {
s += i
}
return s
}
// JVM non-IR uses while.
// JVM IR uses if + do-while. In addition, for "until" progressions, there is a check that the range is not empty: upper bound != MIN_VALUE.
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 1 IFEQ
// 1 IF_ICMPGT
// 1 IF_ICMPLE
// 3 IF
@@ -0,0 +1,22 @@
// IGNORE_BACKEND: JVM_IR
const val M = Char.MAX_VALUE
fun f(a: Char): Int {
var n = 0
for (i in a until M) {
n++
}
return n
}
// JVM non-IR uses while.
// JVM IR uses if + do-while.
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 1 IF_ICMPGE
// 1 IF
@@ -0,0 +1,23 @@
// TARGET_BACKEND: JVM_IR
const val M = Char.MAX_VALUE
fun f(a: Char): Int {
var n = 0
for (i in a until M) {
n++
}
return n
}
// JVM non-IR uses while.
// JVM IR uses if + do-while.
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 1 IF_ICMPGT
// 1 IF_ICMPLE
// 2 IF
@@ -0,0 +1,22 @@
// TARGET_BACKEND: JVM_IR
const val M = Char.MIN_VALUE
fun f(a: Char): Int {
var n = 0
for (i in a until M) {
n++
}
return n
}
// For "until" progressions in JVM IR, there is a check that the range is not empty: upper bound != MIN_VALUE.
// When the upper bound == const MIN_VALUE, the backend can eliminate the entire loop as dead code.
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 0 IF
// 0 LINENUMBER 7
@@ -0,0 +1,20 @@
// IGNORE_BACKEND: JVM_IR
fun test(a: Int, b: Int): Int {
var sum = 0
for (i in a until b) {
sum = sum * 10 + i
}
return sum
}
// JVM non-IR uses while.
// JVM IR uses if + do-while. In addition, for "until" progressions, there is a check that the range is not empty: upper bound != MIN_VALUE.
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 1 IF_ICMPGE
// 1 IF
@@ -0,0 +1,23 @@
// TARGET_BACKEND: JVM_IR
fun test(a: Int, b: Int): Int {
var sum = 0
for (i in a until b) {
sum = sum * 10 + i
}
return sum
}
// JVM non-IR uses while.
// JVM IR uses if + do-while. In addition, for "until" progressions, there is a check that the range is not empty: upper bound != MIN_VALUE.
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 1 LDC -2147483648
// 1 IF_ICMPEQ
// 1 IF_ICMPGT
// 1 IF_ICMPLE
// 3 IF
@@ -0,0 +1,22 @@
// IGNORE_BACKEND: JVM_IR
const val M = Int.MAX_VALUE
fun f(a: Int): Int {
var n = 0
for (i in a until M) {
n++
}
return n
}
// JVM non-IR uses while.
// JVM IR uses if + do-while.
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 1 IF_ICMPGE
// 1 IF
@@ -0,0 +1,23 @@
// TARGET_BACKEND: JVM_IR
const val M = Int.MAX_VALUE
fun f(a: Int): Int {
var n = 0
for (i in a until M) {
n++
}
return n
}
// JVM non-IR uses while.
// JVM IR uses if + do-while.
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 1 IF_ICMPGT
// 1 IF_ICMPLE
// 2 IF
@@ -0,0 +1,22 @@
// TARGET_BACKEND: JVM_IR
const val M = Int.MIN_VALUE
fun f(a: Int): Int {
var n = 0
for (i in a until M) {
n++
}
return n
}
// For "until" progressions in JVM IR, there is a check that the range is not empty: upper bound != MIN_VALUE.
// When the upper bound == const MIN_VALUE, the backend can eliminate the entire loop as dead code.
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 0 IF
// 0 LINENUMBER 7
@@ -0,0 +1,21 @@
// IGNORE_BACKEND: JVM_IR
fun test(a: Long, b: Long): Long {
var sum = 0L
for (i in a until b) {
sum = sum * 10L + i
}
return sum
}
// JVM non-IR uses while.
// JVM IR uses if + do-while. In addition, for "until" progressions, there is a check that the range is not empty: upper bound != MIN_VALUE.
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 1 LCMP
// 1 IFGE
// 1 IF
@@ -0,0 +1,24 @@
// TARGET_BACKEND: JVM_IR
fun test(a: Long, b: Long): Long {
var sum = 0L
for (i in a until b) {
sum = sum * 10L + i
}
return sum
}
// JVM non-IR uses while.
// JVM IR uses if + do-while. In addition, for "until" progressions, there is a check that the range is not empty: upper bound != MIN_VALUE.
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 1 LDC -9223372036854775808
// 3 LCMP
// 1 IFEQ
// 1 IFGT
// 1 IFLE
// 3 IF
@@ -0,0 +1,23 @@
// IGNORE_BACKEND: JVM_IR
const val M = Long.MAX_VALUE
fun f(a: Long): Int {
var n = 0
for (i in a until M) {
n++
}
return n
}
// JVM non-IR uses while.
// JVM IR uses if + do-while.
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 1 LCMP
// 1 IFGE
// 1 IF
@@ -0,0 +1,24 @@
// TARGET_BACKEND: JVM_IR
const val M = Long.MAX_VALUE
fun f(a: Long): Int {
var n = 0
for (i in a until M) {
n++
}
return n
}
// JVM non-IR uses while.
// JVM IR uses if + do-while.
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 2 LCMP
// 1 IFGT
// 1 IFLE
// 2 IF
@@ -0,0 +1,22 @@
// TARGET_BACKEND: JVM_IR
const val M = Long.MIN_VALUE
fun f(a: Long): Int {
var n = 0
for (i in a until M) {
n++
}
return n
}
// For "until" progressions in JVM IR, there is a check that the range is not empty: upper bound != MIN_VALUE.
// When the upper bound == const MIN_VALUE, the backend can eliminate the entire loop as dead code.
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 0 IF
// 0 LINENUMBER 7
@@ -0,0 +1,41 @@
// TARGET_BACKEND: JVM_IR
fun testByteUntilInt(a: Byte, b: Int): Int {
var sum = 0
for (i in a until b) {
sum = sum * 10 + i
}
return sum
}
fun testShortUntilInt(a: Short, b: Int): Int {
var sum = 0
for (i in a until b) {
sum = sum * 10 + i
}
return sum
}
// For "until" progressions in JVM IR, there is typically a check that the range is not empty: upper bound != MIN_VALUE.
// However, this check is not needed when the upper bound is smaller than the range element type.
// Here are the available `until` extension functions with mixed bounds that return IntRange:
//
// infix fun Byte.until(to: Byte): IntRange
// infix fun Byte.until(to: Short): IntRange
// infix fun Byte.until(to: Int): IntRange // Bound check needed
// infix fun Short.until(to: Byte): IntRange
// infix fun Short.until(to: Short): IntRange
// infix fun Short.until(to: Int): IntRange // Bound check needed
// infix fun Int.until(to: Byte): IntRange
// infix fun Int.until(to: Short): IntRange
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 2 LDC -2147483648
// 2 IF_ICMPEQ
// 2 IF_ICMPGT
// 2 IF_ICMPLE
// 6 IF
@@ -0,0 +1,72 @@
// TARGET_BACKEND: JVM_IR
fun testByteUntilByte(a: Byte, b: Byte): Int {
var sum = 0
for (i in a until b) {
sum = sum * 10 + i
}
return sum
}
fun testByteUntilShort(a: Byte, b: Short): Int {
var sum = 0
for (i in a until b) {
sum = sum * 10 + i
}
return sum
}
fun testShortUntilByte(a: Short, b: Byte): Int {
var sum = 0
for (i in a until b) {
sum = sum * 10 + i
}
return sum
}
fun testShortUntilShort(a: Short, b: Short): Int {
var sum = 0
for (i in a until b) {
sum = sum * 10 + i
}
return sum
}
fun testIntUntilByte(a: Int, b: Byte): Int {
var sum = 0
for (i in a until b) {
sum = sum * 10 + i
}
return sum
}
fun testIntUntilShort(a: Int, b: Short): Int {
var sum = 0
for (i in a until b) {
sum = sum * 10 + i
}
return sum
}
// For "until" progressions in JVM IR, there is typically a check that the range is not empty: upper bound != MIN_VALUE.
// However, this check is not needed when the upper bound is smaller than the range element type.
// Here are the available `until` extension functions with mixed bounds that return IntRange:
//
// infix fun Byte.until(to: Byte): IntRange // NO bound check needed
// infix fun Byte.until(to: Short): IntRange // NO bound check needed
// infix fun Byte.until(to: Int): IntRange
// infix fun Short.until(to: Byte): IntRange // NO bound check needed
// infix fun Short.until(to: Short): IntRange // NO bound check needed
// infix fun Short.until(to: Int): IntRange
// infix fun Int.until(to: Byte): IntRange // NO bound check needed
// infix fun Int.until(to: Short): IntRange // NO bound check needed
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 0 LDC -2147483648
// 6 IF_ICMPGT
// 6 IF_ICMPLE
// 12 IF
@@ -0,0 +1,44 @@
// TARGET_BACKEND: JVM_IR
fun testLongUntilByte(a: Long, b: Byte): Long {
var sum = 0L
for (i in a until b) {
sum = sum * 10L + i
}
return sum
}
fun testLongUntilShort(a: Long, b: Short): Long {
var sum = 0L
for (i in a until b) {
sum = sum * 10L + i
}
return sum
}
fun testLongUntilInt(a: Long, b: Int): Long {
var sum = 0L
for (i in a until b) {
sum = sum * 10L + i
}
return sum
}
// For "until" progressions in JVM IR, there is typically a check that the range is not empty: upper bound != MIN_VALUE.
// However, this check is not needed when the upper bound is smaller than the range element type.
// Here are the available `until` extension functions with mixed bounds that return LongRange:
//
// infix fun Long.until(to: Byte): LongRange // NO bound check needed
// infix fun Long.until(to: Short): LongRange // NO bound check needed
// infix fun Long.until(to: Int): LongRange // NO bound check needed
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 0 LDC -9223372036854775808
// 6 LCMP
// 3 IFGT
// 3 IFLE
// 6 IF