From 9bb9ab67a7e725d6988c374586cf65fb3a45dd43 Mon Sep 17 00:00:00 2001 From: Mark Punzalan Date: Fri, 21 Jun 2019 23:06:26 -0700 Subject: [PATCH] Add bytecode text tests for ForLoopsLowering. --- .../common/lower/loops/ProgressionHandlers.kt | 6 +- .../emptyUntilProgressionToMinValue.kt | 37 ++++++++ .../forLoop/stepped/illegalStepConst.kt | 40 +++++++++ .../forLoop/stepped/reversedThenStep.kt | 42 +++++++++ .../bytecodeText/forLoop/stepped/stepConst.kt | 39 ++++++++ .../stepConstOnNonLiteralProgression.kt | 52 +++++++++++ .../forLoop/stepped/stepNonConst.kt | 46 ++++++++++ .../stepNonConstOnNonLiteralProgression.kt | 56 ++++++++++++ .../bytecodeText/forLoop/stepped/stepOne.kt | 37 ++++++++ .../forLoop/stepped/stepOneThenStepOne.kt | 37 ++++++++ .../forLoop/stepped/stepThenDifferentStep.kt | 43 +++++++++ .../forLoop/stepped/stepThenReversed.kt | 40 +++++++++ .../forLoop/stepped/stepThenSameStep.kt | 40 +++++++++ .../forLoop/stepped/stepThenStepNonConst.kt | 50 +++++++++++ .../forLoop/stepped/stepThenStepOne.kt | 43 +++++++++ .../stepped/untilProgressionToNonConst.kt | 44 ++++++++++ .../codegen/BytecodeTextTestGenerated.java | 13 +++ .../ir/IrBytecodeTextTestGenerated.java | 88 +++++++++++++++++++ 18 files changed, 750 insertions(+), 3 deletions(-) create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/stepped/emptyUntilProgressionToMinValue.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/stepped/illegalStepConst.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/stepped/reversedThenStep.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/stepped/stepConst.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/stepped/stepConstOnNonLiteralProgression.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConst.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConstOnNonLiteralProgression.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/stepped/stepOne.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/stepped/stepOneThenStepOne.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenDifferentStep.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenReversed.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenSameStep.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenStepNonConst.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenStepOne.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/stepped/untilProgressionToNonConst.kt 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 e285f2020e4..e146b0b7cc0 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 @@ -408,7 +408,7 @@ internal class StepHandler( // // No nested step var because nested step `innerNewStep` is a local variable get (cannot have side-effects) // val outerStepArg = D // val outerNewStep = if (outerStepArg > 0) outerStepArg - // else throw IllegalArgumentException("Step must be positive, was: outerStepArg.") + // else throw IllegalArgumentException("Step must be positive, was: $outerStepArg.") // // // Standard form of loop over progression // var inductionVar = innerNestedFirst @@ -438,7 +438,7 @@ internal class StepHandler( // val nestedStep = progression.step // val stepArg = C // val checkedStep = if (stepArg > 0) stepArg - // else throw IllegalArgumentException("Step must be positive, was: stepArg.") + // else throw IllegalArgumentException("Step must be positive, was: $stepArg.") // val newStep = // Direction of P is unknown so we check its step to determine whether to negate // if (nestedStep > 0) checkedStep else -checkedStep // @@ -446,7 +446,7 @@ internal class StepHandler( // var inductionVar = nestedFirst // val last = getProgressionLastElement(nestedFirst, nestedLast, newStep) // val step = newStep - // if ((nestedStep > 0 && inductionVar <= last) || (nestedStep < 0 && last <= inductionVar)) { + // if ((step > 0 && inductionVar <= last) || (step < 0 && last <= inductionVar)) { // // Loop is not empty // do { // val i = inductionVar diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/emptyUntilProgressionToMinValue.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/emptyUntilProgressionToMinValue.kt new file mode 100644 index 00000000000..93ef1674acf --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/emptyUntilProgressionToMinValue.kt @@ -0,0 +1,37 @@ +// TARGET_BACKEND: JVM_IR +fun box(): String { + for (i in 2 until Int.MIN_VALUE step 2) { + } + + return "OK" +} + +// 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. +// Calls to getProgressionLastElement() are still present because these happen before the eliminated loop. +// +// Expected lowered form of loop (before bytecode optimizations): +// +// // Standard form of loop over progression +// var inductionVar = 2 +// val last = getProgressionLastElement(1, Int.MIN_VALUE - 1, 2) // `(Int.MIN_VALUE - 1)` underflows to 2147483647 +// val step = 2 +// if (false && inductionVar <= last) { // `false` comes from constant folding of `Int.MIN_VALUE != Int.MIN_VALUE` +// // Loop is not empty +// do { +// val i = inductionVar +// inductionVar += step +// // Loop body +// } while (i != last) +// } + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 INVOKESTATIC kotlin/internal/ProgressionUtilKt.getProgressionLastElement +// 0 NEW java/lang/IllegalArgumentException +// 0 ATHROW +// 0 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/illegalStepConst.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/illegalStepConst.kt new file mode 100644 index 00000000000..cb7e708f71c --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/illegalStepConst.kt @@ -0,0 +1,40 @@ +// TARGET_BACKEND: JVM_IR +fun box(): String { + for (i in 1..6 step 0) { + } + + return "OK" +} + +// For "step" progressions in JVM IR, if the step is constant and <= 0, the expression for step is replaced with an +// IllegalArgumentException. The backend can then eliminate the entire loop and the rest of the function as dead code. +// +// Expected lowered form of loop (before bytecode optimization): +// +// // Additional variables: +// val newStep = throw IllegalArgumentException("Step must be positive, was: 0.") +// +// // Standard form of loop over progression +// var inductionVar = 1 +// val last = getProgressionLastElement(1, 6, newStep) +// val step = newStep +// if (inductionVar <= last) { +// // Loop is not empty +// do { +// val i = inductionVar +// inductionVar += step +// // Loop body +// } while (i != last) +// } + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 0 INVOKESTATIC kotlin/internal/ProgressionUtilKt.getProgressionLastElement +// 1 NEW java/lang/IllegalArgumentException +// 1 ATHROW +// 0 IF +// 0 ARETURN \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/reversedThenStep.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/reversedThenStep.kt new file mode 100644 index 00000000000..69ad73d3a2b --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/reversedThenStep.kt @@ -0,0 +1,42 @@ +// TARGET_BACKEND: JVM_IR +fun box(): String { + for (i in (1..8).reversed() step 2) { + } + + return "OK" +} + +// For "step" progressions in JVM IR, a call to getProgressionLastElement() is made to compute the "last" value. +// If the step is non-constant, there is a check that it is > 0, and if not, an IllegalArgumentException is thrown. However, when the +// step is constant and > 0, this check does not need to be added. +// +// Expected lowered form of loop: +// +// // Standard form of loop over progression +// val last = getProgressionLastElement(8, 1, -2) +// var inductionVar = 8 +// val step = -2 +// if (last <= inductionVar) { +// // Loop is not empty +// do { +// val i = inductionVar +// inductionVar += step +// // Loop body +// } while (i != last) +// } +// +// We can't use "0 reversed" in the regex below because "reversed" is in the test filename. + +// 0 INVOKE.*reversed +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 INVOKESTATIC kotlin/internal/ProgressionUtilKt.getProgressionLastElement +// 0 NEW java/lang/IllegalArgumentException +// 0 ATHROW +// 1 IF_ICMPGT +// 1 IF_ICMPNE +// 2 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepConst.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepConst.kt new file mode 100644 index 00000000000..f03acf76cd5 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepConst.kt @@ -0,0 +1,39 @@ +// TARGET_BACKEND: JVM_IR +fun box(): String { + for (i in 1..7 step 2) { + } + + return "OK" +} + +// For "step" progressions in JVM IR, a call to getProgressionLastElement() is made to compute the "last" value. +// If the step is non-constant, there is a check that it is > 0, and if not, an IllegalArgumentException is thrown. However, when the +// step is constant and > 0, this check does not need to be added. +// +// Expected lowered form of loop: +// +// // Standard form of loop over progression +// var inductionVar = 1 +// val last = getProgressionLastElement(1, 7, 2) +// val step = 2 +// if (inductionVar <= last) { +// // Loop is not empty +// do { +// val i = inductionVar +// inductionVar += step +// // Loop body +// } while (i != last) +// } + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 INVOKESTATIC kotlin/internal/ProgressionUtilKt.getProgressionLastElement +// 0 NEW java/lang/IllegalArgumentException +// 0 ATHROW +// 1 IF_ICMPGT +// 1 IF_ICMPNE +// 2 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepConstOnNonLiteralProgression.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepConstOnNonLiteralProgression.kt new file mode 100644 index 00000000000..60094b93173 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepConstOnNonLiteralProgression.kt @@ -0,0 +1,52 @@ +// TARGET_BACKEND: JVM_IR +fun box(): String { + val intProgression = 1..7 + for (i in intProgression step 2) { + } + + return "OK" +} + +// For "step" progressions in JVM IR, a call to getProgressionLastElement() is made to compute the "last" value. +// If "step" is called on a non-literal progression, there is a check to see if that progression's step value is < 0. +// If the step is non-constant, there is a check that it is > 0, and if not, an IllegalArgumentException is thrown. However, when the +// step is constant and > 0, this check does not need to be added. +// +// Expected lowered form of loop: +// +// // Additional variables: +// val progression = intProgression +// val nestedFirst = progression.first +// val nestedLast = progression.last +// val nestedStep = progression.step +// val newStep = if (nestedStep > 0) 2 else -2 +// +// // Standard form of loop over progression +// var inductionVar = nestedFirst +// val last = getProgressionLastElement(nestedFirst, nestedLast, newStep) +// val step = newStep +// if ((step > 0 && inductionVar <= last) || (step < 0 && last <= inductionVar)) { +// // Loop is not empty +// do { +// val i = inductionVar +// inductionVar += step +// // Loop body +// } while (i != last) +// } + +// 0 iterator +// 0 getStart +// 0 getEnd +// 1 getFirst +// 1 getLast +// 1 getStep +// 1 INVOKESTATIC kotlin/internal/ProgressionUtilKt.getProgressionLastElement +// 0 NEW java/lang/IllegalArgumentException +// 0 ATHROW +// 1 IF_ICMPGT +// 1 IF_ICMPLE +// 1 IF_ICMPNE +// 2 IFLE +// 1 IFGE +// 6 IF +// 0 INEG \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConst.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConst.kt new file mode 100644 index 00000000000..90918c7d2d5 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConst.kt @@ -0,0 +1,46 @@ +// TARGET_BACKEND: JVM_IR +fun one() = 1 + +fun box(): String { + for (i in 1..7 step one()) { + } + + return "OK" +} + +// For "step" progressions in JVM IR, a call to getProgressionLastElement() is made to compute the "last" value. +// If the step is non-constant, there is a check that it is > 0, and if not, an IllegalArgumentException is thrown. +// +// Expected lowered form of loop: +// +// // Additional variables: +// val stepArg = one() +// val newStep = if (stepArg > 0) stepArg +// else throw IllegalArgumentException("Step must be positive, was: $stepArg.") +// +// // Standard form of loop over progression +// var inductionVar = 1 +// val last = getProgressionLastElement(1, 7, newStep) +// val step = newStep +// if (inductionVar <= last) { +// // Loop is not empty +// do { +// val i = inductionVar +// inductionVar += step +// // Loop body +// } while (i != last) +// } + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 INVOKESTATIC kotlin/internal/ProgressionUtilKt.getProgressionLastElement +// 1 NEW java/lang/IllegalArgumentException +// 1 ATHROW +// 1 IF_ICMPGT +// 1 IF_ICMPNE +// 1 IFLE +// 3 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConstOnNonLiteralProgression.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConstOnNonLiteralProgression.kt new file mode 100644 index 00000000000..1a1620d1ff4 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConstOnNonLiteralProgression.kt @@ -0,0 +1,56 @@ +// TARGET_BACKEND: JVM_IR +fun one() = 1 + +fun box(): String { + val intProgression = 1..7 + for (i in intProgression step one()) { + } + + return "OK" +} + +// For "step" progressions in JVM IR, a call to getProgressionLastElement() is made to compute the "last" value. +// If "step" is called on a non-literal progression, there is a check to see if that progression's step value is < 0. +// If the step is non-constant, there is a check that it is > 0, and if not, an IllegalArgumentException is thrown. +// +// Expected lowered form of loop: +// +// // Additional variables: +// val progression = intProgression +// val nestedFirst = progression.first +// val nestedLast = progression.last +// val nestedStep = progression.step +// val stepArg = one() +// val checkedStep = if (stepArg > 0) stepArg +// else throw IllegalArgumentException("Step must be positive, was: $stepArg.") +// val newStep = if (nestedStep > 0) checkedStep else -checkedStep +// +// // Standard form of loop over progression +// var inductionVar = nestedFirst +// val last = getProgressionLastElement(nestedFirst, nestedLast, newStep) +// val step = newStep +// if ((step > 0 && inductionVar <= last) || (step < 0 && last <= inductionVar)) { +// // Loop is not empty +// do { +// val i = inductionVar +// inductionVar += step +// // Loop body +// } while (i != last) +// } + +// 0 iterator +// 0 getStart +// 0 getEnd +// 1 getFirst +// 1 getLast +// 1 getStep +// 1 INVOKESTATIC kotlin/internal/ProgressionUtilKt.getProgressionLastElement +// 1 NEW java/lang/IllegalArgumentException +// 1 ATHROW +// 1 IF_ICMPGT +// 1 IF_ICMPLE +// 1 IF_ICMPNE +// 3 IFLE +// 1 IFGE +// 7 IF +// 1 INEG \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepOne.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepOne.kt new file mode 100644 index 00000000000..da34afd18bb --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepOne.kt @@ -0,0 +1,37 @@ +// TARGET_BACKEND: JVM_IR +fun box(): String { + for (i in 1..4 step 1) { + } + + return "OK" +} + +// For "step" progressions in JVM IR, when the step is constant and == 1, and "step" is called on a literal progression which we already +// know to have a step whose absolute value is 1, we can essentially ignore the "step" call. +// +// Expected lowered form of loop: +// +// // Standard form of loop over progression +// var inductionVar = 1 +// val last = 4 +// val step = 1 +// if (inductionVar <= last) { +// // Loop is not empty +// do { +// val i = inductionVar +// inductionVar += step +// // Loop body +// } while (inductionVar <= last) +// } + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 0 INVOKESTATIC kotlin/internal/ProgressionUtilKt.getProgressionLastElement +// 0 NEW java/lang/IllegalArgumentException +// 0 ATHROW +// 1 IF_ICMPLE +// 1 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepOneThenStepOne.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepOneThenStepOne.kt new file mode 100644 index 00000000000..94d8a1b7cea --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepOneThenStepOne.kt @@ -0,0 +1,37 @@ +// TARGET_BACKEND: JVM_IR +fun box(): String { + for (i in 1..4 step 1 step 1) { + } + + return "OK" +} + +// For "step" progressions in JVM IR, when the step is constant and == 1, and "step" is called on a literal progression which we already +// know to have a step whose absolute value is 1, we can essentially ignore the "step" call. +// +// Expected lowered form of loop: +// +// // Standard form of loop over progression +// var inductionVar = 1 +// val last = 4 +// val step = 1 +// if (inductionVar <= last) { +// // Loop is not empty +// do { +// val i = inductionVar +// inductionVar += step +// // Loop body +// } while (inductionVar <= last) +// } + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 0 INVOKESTATIC kotlin/internal/ProgressionUtilKt.getProgressionLastElement +// 0 NEW java/lang/IllegalArgumentException +// 0 ATHROW +// 1 IF_ICMPLE +// 1 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenDifferentStep.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenDifferentStep.kt new file mode 100644 index 00000000000..7a364f7db64 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenDifferentStep.kt @@ -0,0 +1,43 @@ +// TARGET_BACKEND: JVM_IR +fun box(): String { + for (i in 1..7 step 3 step 2) { + } + + return "OK" +} + +// For "step" progressions in JVM IR, a call to getProgressionLastElement() is made to compute the "last" value. This is done for each +// nested call to "step". +// If the step is non-constant, there is a check that it is > 0, and if not, an IllegalArgumentException is thrown. However, when the +// step is constant and > 0, this check does not need to be added. +// +// Expected lowered form of loop: +// +// // Additional variables: +// val outerNestedLast = getProgressionLastElement(1, 7, 3) +// +// // Standard form of loop over progression +// var inductionVar = 1 +// val last = getProgressionLastElement(1, outerNestedLast, 2) +// val step = 2 +// if (inductionVar <= last) { +// // Loop is not empty +// do { +// val i = inductionVar +// inductionVar += step +// // Loop body +// } while (i != last) +// } + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 2 INVOKESTATIC kotlin/internal/ProgressionUtilKt.getProgressionLastElement +// 0 NEW java/lang/IllegalArgumentException +// 0 ATHROW +// 1 IF_ICMPGT +// 1 IF_ICMPNE +// 2 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenReversed.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenReversed.kt new file mode 100644 index 00000000000..9892dc76b82 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenReversed.kt @@ -0,0 +1,40 @@ +// TARGET_BACKEND: JVM_IR +fun box(): String { + for (i in (1..8 step 2).reversed()) { + } + + return "OK" +} + +// For "step" progressions in JVM IR, a call to getProgressionLastElement() is made to compute the "last" value. +// If the step is non-constant, there is a check that it is > 0, and if not, an IllegalArgumentException is thrown. However, when the +// step is constant and > 0, this check does not need to be added. +// +// Expected lowered form of loop: +// +// // Standard form of loop over progression +// val last = 1 +// var inductionVar = getProgressionLastElement(1, 8, 2) +// val step = -2 +// if (last <= inductionVar) { +// // Loop is not empty +// do { +// val i = inductionVar +// inductionVar += step +// // Loop body +// } while (last <= inductionVar) +// } + +// 0 reversed +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 INVOKESTATIC kotlin/internal/ProgressionUtilKt.getProgressionLastElement +// 0 NEW java/lang/IllegalArgumentException +// 0 ATHROW +// 1 IF_ICMPGT +// 1 IF_ICMPLE +// 2 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenSameStep.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenSameStep.kt new file mode 100644 index 00000000000..ed96201cca2 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenSameStep.kt @@ -0,0 +1,40 @@ +// TARGET_BACKEND: JVM_IR +fun box(): String { + for (i in 1..8 step 2 step 2) { + } + + return "OK" +} + +// For "step" progressions in JVM IR, a call to getProgressionLastElement() is made to compute the "last" value. This is normally done for +// each nested call to "step", except when the step value is the same as the preceding step value. +// If the step is non-constant, there is a check that it is > 0, and if not, an IllegalArgumentException is thrown. However, when the +// step is constant and > 0, this check does not need to be added. +// +// Expected lowered form of loop: +// +// // Standard form of loop over progression +// var inductionVar = 1 +// val last = getProgressionLastElement(1, 8, 2) +// val step = 2 +// if (inductionVar <= last) { +// // Loop is not empty +// do { +// val i = inductionVar +// inductionVar += step +// // Loop body +// } while (i != last) +// } + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 INVOKESTATIC kotlin/internal/ProgressionUtilKt.getProgressionLastElement +// 0 NEW java/lang/IllegalArgumentException +// 0 ATHROW +// 1 IF_ICMPGT +// 1 IF_ICMPNE +// 2 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenStepNonConst.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenStepNonConst.kt new file mode 100644 index 00000000000..34973136ad2 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenStepNonConst.kt @@ -0,0 +1,50 @@ +// TARGET_BACKEND: JVM_IR +fun one() = 1 + +fun box(): String { + for (i in 1..6 step one() step one()) { + } + + return "OK" +} + +// For "step" progressions in JVM IR, if the step is non-constant, there is a check that it is > 0, and if not, an IllegalArgumentException +// is thrown. +// +// Expected lowered form of loop: +// +// // Additional variables: +// val innerStepArg = one() +// val innerNewStep = if (innerStepArg > 0) innerStepArg +// else throw IllegalArgumentException("Step must be positive, was: $innerStepArg.") +// val outerNestedLast = getProgressionLastElement(1, 6, innerNewStep) +// val outerStepArg = one() +// val outerNewStep = if (outerStepArg > 0) outerStepArg +// else throw IllegalArgumentException("Step must be positive, was: $outerStepArg.") +// +// // Standard form of loop over progression +// var inductionVar = 1 +// val last = getProgressionLastElement(1, outerNestedLast, outerNewStep) +// val step = outerNewStep +// if (inductionVar <= last) { +// // Loop is not empty +// do { +// val i = inductionVar +// inductionVar += step +// // Loop body +// } while (i != last) +// } + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 2 INVOKESTATIC kotlin/internal/ProgressionUtilKt.getProgressionLastElement +// 2 NEW java/lang/IllegalArgumentException +// 2 ATHROW +// 1 IF_ICMPGT +// 1 IF_ICMPNE +// 2 IFLE +// 4 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenStepOne.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenStepOne.kt new file mode 100644 index 00000000000..ed626efb7b4 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenStepOne.kt @@ -0,0 +1,43 @@ +// TARGET_BACKEND: JVM_IR +fun box(): String { + for (i in 1..5 step 2 step 1) { + } + + return "OK" +} + +// For "step" progressions in JVM IR, a call to getProgressionLastElement() is made to compute the "last" value. This is normally done for +// each nested call to "step", except when the step value is constant and == 1. +// If the step is non-constant, there is a check that it is > 0, and if not, an IllegalArgumentException is thrown. However, when the +// step is constant and > 0, this check does not need to be added. +// +// Expected lowered form of loop: +// +// // Additional variables: +// val innerNestedLast = getProgressionLastElement(1, 5, 2) +// +// // Standard form of loop over progression +// var inductionVar = 1 +// val last = innerNestedLast +// val step = 1 +// if (inductionVar <= last) { +// // Loop is not empty +// do { +// val i = inductionVar +// inductionVar += step +// // Loop body +// } while (i != last) +// } + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 INVOKESTATIC kotlin/internal/ProgressionUtilKt.getProgressionLastElement +// 0 NEW java/lang/IllegalArgumentException +// 0 ATHROW +// 1 IF_ICMPGT +// 1 IF_ICMPNE +// 2 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/untilProgressionToNonConst.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/untilProgressionToNonConst.kt new file mode 100644 index 00000000000..b105e7ac89c --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/untilProgressionToNonConst.kt @@ -0,0 +1,44 @@ +// TARGET_BACKEND: JVM_IR +fun nine() = 9 + +fun box(): String { + for (i in 1 until nine() step 2) { + } + + return "OK" +} + +// For "until" progressions in JVM IR, there is a check that the range is not empty: upper bound != MIN_VALUE. +// +// Expected lowered form of loop (before bytecode optimizations): +// +// // Additional variables: +// val untilArg = nine() +// val nestedLast = untilArg - 1 +// +// // Standard form of loop over progression +// var inductionVar = 1 +// val last = getProgressionLastElement(1, nestedLast, 2) +// val step = 2 +// if (untilArg != Int.MIN_VALUE && inductionVar <= last) { +// // Loop is not empty +// do { +// val i = inductionVar +// inductionVar += step +// // Loop body +// } while (i != last) +// } + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 INVOKESTATIC kotlin/internal/ProgressionUtilKt.getProgressionLastElement +// 0 NEW java/lang/IllegalArgumentException +// 1 LDC -2147483648 +// 1 IF_ICMPEQ +// 1 IF_ICMPGT +// 1 IF_ICMPNE +// 3 IF \ 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 e07d238bdfc..522f77b2a55 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -2157,6 +2157,19 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInUntil/forInUntilLongMinValue.kt"); } } + + @TestMetadata("compiler/testData/codegen/bytecodeText/forLoop/stepped") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Stepped extends AbstractBytecodeTextTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInStepped() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/forLoop/stepped"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + } } @TestMetadata("compiler/testData/codegen/bytecodeText/hashCode") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java index f271e12f7c5..ae5fe9262a4 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java @@ -2127,6 +2127,94 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInUntil/forInUntilWithMixedTypeBoundsNoBoundCheckNeededForLongRangeIR.kt"); } } + + @TestMetadata("compiler/testData/codegen/bytecodeText/forLoop/stepped") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Stepped extends AbstractIrBytecodeTextTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInStepped() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/forLoop/stepped"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true); + } + + @TestMetadata("emptyUntilProgressionToMinValue.kt") + public void testEmptyUntilProgressionToMinValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/stepped/emptyUntilProgressionToMinValue.kt"); + } + + @TestMetadata("illegalStepConst.kt") + public void testIllegalStepConst() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/stepped/illegalStepConst.kt"); + } + + @TestMetadata("reversedThenStep.kt") + public void testReversedThenStep() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/stepped/reversedThenStep.kt"); + } + + @TestMetadata("stepConst.kt") + public void testStepConst() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/stepped/stepConst.kt"); + } + + @TestMetadata("stepConstOnNonLiteralProgression.kt") + public void testStepConstOnNonLiteralProgression() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/stepped/stepConstOnNonLiteralProgression.kt"); + } + + @TestMetadata("stepNonConst.kt") + public void testStepNonConst() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConst.kt"); + } + + @TestMetadata("stepNonConstOnNonLiteralProgression.kt") + public void testStepNonConstOnNonLiteralProgression() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConstOnNonLiteralProgression.kt"); + } + + @TestMetadata("stepOne.kt") + public void testStepOne() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/stepped/stepOne.kt"); + } + + @TestMetadata("stepOneThenStepOne.kt") + public void testStepOneThenStepOne() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/stepped/stepOneThenStepOne.kt"); + } + + @TestMetadata("stepThenDifferentStep.kt") + public void testStepThenDifferentStep() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenDifferentStep.kt"); + } + + @TestMetadata("stepThenReversed.kt") + public void testStepThenReversed() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenReversed.kt"); + } + + @TestMetadata("stepThenSameStep.kt") + public void testStepThenSameStep() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenSameStep.kt"); + } + + @TestMetadata("stepThenStepNonConst.kt") + public void testStepThenStepNonConst() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenStepNonConst.kt"); + } + + @TestMetadata("stepThenStepOne.kt") + public void testStepThenStepOne() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenStepOne.kt"); + } + + @TestMetadata("untilProgressionToNonConst.kt") + public void testUntilProgressionToNonConst() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/stepped/untilProgressionToNonConst.kt"); + } + } } @TestMetadata("compiler/testData/codegen/bytecodeText/hashCode")