diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index 343eb3aba8e..46829b795f2 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -31847,6 +31847,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInDownToWithPossibleUnderflow.kt"); } + @Test + @TestMetadata("forInSumDownToSum.kt") + public void testForInSumDownToSum() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInSumDownToSum.kt"); + } + @Test @TestMetadata("forIntInDownTo.kt") public void testForIntInDownTo() throws Exception { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt index ceb99900666..36d1dd842ab 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt @@ -112,7 +112,7 @@ abstract class NumericForLoopHeader( val last = headerInfo.last.asElementType() if (headerInfo.canCacheLast) { - val (variable, expression) = createTemporaryVariableIfNecessary(last, nameHint = "last") + val (variable, expression) = createLoopTemporaryVariableIfNecessary(last, nameHint = "last") lastVariableIfCanCacheLast = variable lastExpression = expression.shallowCopy() } else { @@ -121,7 +121,7 @@ abstract class NumericForLoopHeader( } val (tmpStepVar, tmpStepExpression) = - createTemporaryVariableIfNecessary( + createLoopTemporaryVariableIfNecessary( ensureNotNullable(headerInfo.step.asStepType()), nameHint = "step", irType = stepClass.defaultType diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/Utils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/Utils.kt index 7c4a9b4d6ee..9d7c9e7bd1f 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/Utils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/Utils.kt @@ -10,19 +10,13 @@ import org.jetbrains.kotlin.ir.builders.createTmpVariable import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrVariable -import org.jetbrains.kotlin.ir.expressions.IrConst -import org.jetbrains.kotlin.ir.expressions.IrConstKind -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.IrGetValue +import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.types.isNothing -import org.jetbrains.kotlin.ir.util.defaultType -import org.jetbrains.kotlin.ir.util.functions -import org.jetbrains.kotlin.ir.util.isTrivial -import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.util.OperatorNameConventions @@ -74,6 +68,18 @@ internal fun IrExpression.decrement(): IrExpression { } } +internal val IrExpression.canChangeValueDuringExecution: Boolean + get() = when (this) { + is IrGetValue -> + !this.symbol.owner.isImmutable + is IrConst<*>, + is IrGetObjectValue, + is IrGetEnumValue -> + false + else -> + true + } + internal val IrExpression.canHaveSideEffects: Boolean get() = !isTrivial() @@ -94,8 +100,10 @@ internal val IrExpression.constLongValue: Long? * This helps reduce local variable usage. */ internal fun DeclarationIrBuilder.createTemporaryVariableIfNecessary( - expression: IrExpression, nameHint: String? = null, - irType: IrType? = null, isMutable: Boolean = false + expression: IrExpression, + nameHint: String? = null, + irType: IrType? = null, + isMutable: Boolean = false ): Pair = if (expression.canHaveSideEffects) { scope.createTmpVariable(expression, nameHint = nameHint, irType = irType, isMutable = isMutable).let { Pair(it, irGet(it)) } @@ -103,6 +111,27 @@ internal fun DeclarationIrBuilder.createTemporaryVariableIfNecessary( Pair(null, expression) } +/** + * If [expression] can change value during execution ([IrExpression.canChangeValueDuringExecution]), + * this function creates a temporary local variable for that expression and returns that variable and an [IrGetValue] for it. + * Otherwise, it returns no variable and [expression]. + * Note that a variable expression doesn't have side effects per se, but can change value during execution, + * so if it's denotes a value that would be used in a loop (say, a loop bound), it should be cached in a temporary at the loop header. + * + * This helps reduce local variable usage. + */ +internal fun DeclarationIrBuilder.createLoopTemporaryVariableIfNecessary( + expression: IrExpression, + nameHint: String? = null, + irType: IrType? = null, + isMutable: Boolean = false +): Pair = + if (expression.canChangeValueDuringExecution) { + scope.createTmpVariable(expression, nameHint = nameHint, irType = irType, isMutable = isMutable).let { Pair(it, irGet(it)) } + } else { + Pair(null, expression) + } + internal fun IrExpression.castIfNecessary(targetClass: IrClass) = // This expression's type could be Nothing from an exception throw. if (type == targetClass.defaultType || type.isNothing()) { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/StepHandler.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/StepHandler.kt index 2b260737ac6..a9047cba8d5 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/StepHandler.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/StepHandler.kt @@ -67,7 +67,7 @@ internal class StepHandler( // To reduce local variable usage, we create and use temporary variables only if necessary. // This temporary variable for step needs to be mutable for certain cases (see below). - val (stepArgVar, stepArgExpression) = createTemporaryVariableIfNecessary(stepArg, "stepArg", isMutable = true) + val (stepArgVar, stepArgExpression) = createLoopTemporaryVariableIfNecessary(stepArg, "stepArg", isMutable = true) // The `step` standard library function only accepts positive values, and performs the following check: // @@ -131,7 +131,7 @@ internal class StepHandler( // Check value of nested step and negate step arg if needed: `if (nestedStep <= 0) -step else step` // A temporary variable is created only if necessary, so we can preserve the evaluation order. val nestedStep = nestedInfo.step - val (tmpNestedStepVar, nestedStepExpression) = createTemporaryVariableIfNecessary(nestedStep, "nestedStep") + val (tmpNestedStepVar, nestedStepExpression) = createLoopTemporaryVariableIfNecessary(nestedStep, "nestedStep") nestedStepVar = tmpNestedStepVar val nestedStepNonPositiveCheck = irCall(stepCompFun).apply { putValueArgument(0, nestedStepExpression.shallowCopy()) @@ -163,8 +163,8 @@ internal class StepHandler( // Store the nested "first" and "last" and final "step" in temporary variables only if necessary, so we can preserve the // evaluation order. - val (nestedFirstVar, nestedFirstExpression) = createTemporaryVariableIfNecessary(nestedInfo.first, "nestedFirst") - val (nestedLastVar, nestedLastExpression) = createTemporaryVariableIfNecessary(nestedInfo.last, "nestedLast") + val (nestedFirstVar, nestedFirstExpression) = createLoopTemporaryVariableIfNecessary(nestedInfo.first, "nestedFirst") + val (nestedLastVar, nestedLastExpression) = createLoopTemporaryVariableIfNecessary(nestedInfo.last, "nestedLast") // Creating a progression with a step value != 1 may result in a "last" value that is smaller than the given "last". The new // "last" value is such that iterating over the progression (by incrementing by "step") does not go over the "last" value. diff --git a/compiler/testData/codegen/box/ranges/forInDownTo/forInSumDownToSum.kt b/compiler/testData/codegen/box/ranges/forInDownTo/forInSumDownToSum.kt new file mode 100644 index 00000000000..73fb25ab124 --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInDownTo/forInSumDownToSum.kt @@ -0,0 +1,16 @@ +// WITH_RUNTIME + +fun test(): Int { + var sum = 0 + for (i in sum downTo sum) { + sum += 1 + i + } + return sum +} + +fun box(): String { + val t1 = test() + if (t1 != 1) return "Failed: t1=$t1" + + return "OK" +} diff --git a/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConst.kt b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConst.kt index f6476ca0510..f8c689a1762 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConst.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConst.kt @@ -35,8 +35,8 @@ fun box(): String { // 1 IF_ICMPEQ // 1 IFGT // 3 IF -// 9 ILOAD -// 4 ISTORE +// 10 ILOAD +// 5 ISTORE // 1 IADD // 0 ISUB // 0 IINC \ 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 index a06da1137e6..fec28bab22e 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConstOnNonLiteralProgression.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepNonConstOnNonLiteralProgression.kt @@ -42,8 +42,8 @@ fun box(): String { // 1 IFGE // 7 IF // 1 INEG -// 18 ILOAD -// 8 ISTORE +// 19 ILOAD +// 9 ISTORE // 1 IADD // 0 ISUB // 0 IINC \ 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 index dbf6a0a8dd8..cd81d9fe585 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenStepNonConst.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/stepped/stepThenStepNonConst.kt @@ -35,8 +35,8 @@ fun box(): String { // 1 IF_ICMPEQ // 2 IFGT // 4 IF -// 13 ILOAD -// 6 ISTORE +// 14 ILOAD +// 7 ISTORE // 1 IADD // 0 ISUB // 0 IINC diff --git a/compiler/testData/codegen/bytecodeText/forLoop/unsigned/stepNonConstOnNonLiteralProgression.kt b/compiler/testData/codegen/bytecodeText/forLoop/unsigned/stepNonConstOnNonLiteralProgression.kt index 21c1165ae07..f0153827623 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/unsigned/stepNonConstOnNonLiteralProgression.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/unsigned/stepNonConstOnNonLiteralProgression.kt @@ -42,8 +42,8 @@ fun box(): String { // 1 INEG // 0 INVOKESTATIC kotlin/UInt.constructor-impl // 0 INVOKE\w+ kotlin/UInt.(un)?box-impl -// 19 ILOAD -// 9 ISTORE +// 20 ILOAD +// 10 ISTORE // 1 IADD // 0 ISUB // 0 IINC \ No newline at end of file diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index f39ffbd49ff..cdb489f9651 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java @@ -31709,6 +31709,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInDownToWithPossibleUnderflow.kt"); } + @Test + @TestMetadata("forInSumDownToSum.kt") + public void testForInSumDownToSum() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInSumDownToSum.kt"); + } + @Test @TestMetadata("forIntInDownTo.kt") public void testForIntInDownTo() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 40c5e5ddb60..7507c40e459 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -31847,6 +31847,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInDownToWithPossibleUnderflow.kt"); } + @Test + @TestMetadata("forInSumDownToSum.kt") + public void testForInSumDownToSum() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInSumDownToSum.kt"); + } + @Test @TestMetadata("forIntInDownTo.kt") public void testForIntInDownTo() throws Exception { diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 33c9ab873e9..7e1e607bea9 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -27001,6 +27001,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInDownToWithPossibleUnderflow.kt"); } + @TestMetadata("forInSumDownToSum.kt") + public void testForInSumDownToSum() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInSumDownToSum.kt"); + } + @TestMetadata("forIntInDownTo.kt") public void testForIntInDownTo() throws Exception { runTest("compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownTo.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java index e1b6b6bbfe7..e11fe98357a 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java @@ -21405,6 +21405,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInDownToWithPossibleUnderflow.kt"); } + @TestMetadata("forInSumDownToSum.kt") + public void testForInSumDownToSum() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInSumDownToSum.kt"); + } + @TestMetadata("forIntInDownTo.kt") public void testForIntInDownTo() throws Exception { runTest("compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownTo.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 5c1cdd2e3b9..bfe290efc52 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -20811,6 +20811,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInDownToWithPossibleUnderflow.kt"); } + @TestMetadata("forInSumDownToSum.kt") + public void testForInSumDownToSum() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInSumDownToSum.kt"); + } + @TestMetadata("forIntInDownTo.kt") public void testForIntInDownTo() throws Exception { runTest("compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownTo.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java index 78c3794c1b4..1d7b5e6ad72 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java @@ -19951,6 +19951,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInDownToWithPossibleUnderflow.kt"); } + @TestMetadata("forInSumDownToSum.kt") + public void testForInSumDownToSum() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInSumDownToSum.kt"); + } + @TestMetadata("forIntInDownTo.kt") public void testForIntInDownTo() throws Exception { runTest("compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownTo.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testNew/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testNew/JsCodegenBoxTestGenerated.java index d4be04d4656..a2bf1b9f552 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testNew/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testNew/JsCodegenBoxTestGenerated.java @@ -23311,6 +23311,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInDownToWithPossibleUnderflow.kt"); } + @Test + @TestMetadata("forInSumDownToSum.kt") + public void testForInSumDownToSum() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInSumDownToSum.kt"); + } + @Test @TestMetadata("forIntInDownTo.kt") public void testForIntInDownTo() throws Exception {