IR KT-49372 cache progression loop parameters if their values can change

This commit is contained in:
Dmitry Petrov
2021-10-22 16:44:13 +03:00
committed by teamcityserver
parent 7fb82232cd
commit 45a4cea655
16 changed files with 113 additions and 24 deletions
@@ -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 {
@@ -112,7 +112,7 @@ abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
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<T : NumericHeaderInfo>(
}
val (tmpStepVar, tmpStepExpression) =
createTemporaryVariableIfNecessary(
createLoopTemporaryVariableIfNecessary(
ensureNotNullable(headerInfo.step.asStepType()),
nameHint = "step",
irType = stepClass.defaultType
@@ -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<IrVariable?, IrExpression> =
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<IrVariable?, IrExpression> =
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()) {
@@ -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.
@@ -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"
}
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 {
@@ -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 {
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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 {