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"); 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 @Test
@TestMetadata("forIntInDownTo.kt") @TestMetadata("forIntInDownTo.kt")
public void testForIntInDownTo() throws Exception { public void testForIntInDownTo() throws Exception {
@@ -112,7 +112,7 @@ abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
val last = headerInfo.last.asElementType() val last = headerInfo.last.asElementType()
if (headerInfo.canCacheLast) { if (headerInfo.canCacheLast) {
val (variable, expression) = createTemporaryVariableIfNecessary(last, nameHint = "last") val (variable, expression) = createLoopTemporaryVariableIfNecessary(last, nameHint = "last")
lastVariableIfCanCacheLast = variable lastVariableIfCanCacheLast = variable
lastExpression = expression.shallowCopy() lastExpression = expression.shallowCopy()
} else { } else {
@@ -121,7 +121,7 @@ abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
} }
val (tmpStepVar, tmpStepExpression) = val (tmpStepVar, tmpStepExpression) =
createTemporaryVariableIfNecessary( createLoopTemporaryVariableIfNecessary(
ensureNotNullable(headerInfo.step.asStepType()), ensureNotNullable(headerInfo.step.asStepType()),
nameHint = "step", nameHint = "step",
irType = stepClass.defaultType 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.builders.irGet
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.*
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.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.isNothing import org.jetbrains.kotlin.ir.types.isNothing
import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.*
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.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions 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 internal val IrExpression.canHaveSideEffects: Boolean
get() = !isTrivial() get() = !isTrivial()
@@ -94,8 +100,10 @@ internal val IrExpression.constLongValue: Long?
* This helps reduce local variable usage. * This helps reduce local variable usage.
*/ */
internal fun DeclarationIrBuilder.createTemporaryVariableIfNecessary( internal fun DeclarationIrBuilder.createTemporaryVariableIfNecessary(
expression: IrExpression, nameHint: String? = null, expression: IrExpression,
irType: IrType? = null, isMutable: Boolean = false nameHint: String? = null,
irType: IrType? = null,
isMutable: Boolean = false
): Pair<IrVariable?, IrExpression> = ): Pair<IrVariable?, IrExpression> =
if (expression.canHaveSideEffects) { if (expression.canHaveSideEffects) {
scope.createTmpVariable(expression, nameHint = nameHint, irType = irType, isMutable = isMutable).let { Pair(it, irGet(it)) } 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) 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) = internal fun IrExpression.castIfNecessary(targetClass: IrClass) =
// This expression's type could be Nothing from an exception throw. // This expression's type could be Nothing from an exception throw.
if (type == targetClass.defaultType || type.isNothing()) { 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. // 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). // 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: // 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` // 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. // A temporary variable is created only if necessary, so we can preserve the evaluation order.
val nestedStep = nestedInfo.step val nestedStep = nestedInfo.step
val (tmpNestedStepVar, nestedStepExpression) = createTemporaryVariableIfNecessary(nestedStep, "nestedStep") val (tmpNestedStepVar, nestedStepExpression) = createLoopTemporaryVariableIfNecessary(nestedStep, "nestedStep")
nestedStepVar = tmpNestedStepVar nestedStepVar = tmpNestedStepVar
val nestedStepNonPositiveCheck = irCall(stepCompFun).apply { val nestedStepNonPositiveCheck = irCall(stepCompFun).apply {
putValueArgument(0, nestedStepExpression.shallowCopy()) 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 // Store the nested "first" and "last" and final "step" in temporary variables only if necessary, so we can preserve the
// evaluation order. // evaluation order.
val (nestedFirstVar, nestedFirstExpression) = createTemporaryVariableIfNecessary(nestedInfo.first, "nestedFirst") val (nestedFirstVar, nestedFirstExpression) = createLoopTemporaryVariableIfNecessary(nestedInfo.first, "nestedFirst")
val (nestedLastVar, nestedLastExpression) = createTemporaryVariableIfNecessary(nestedInfo.last, "nestedLast") 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 // 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. // "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 IF_ICMPEQ
// 1 IFGT // 1 IFGT
// 3 IF // 3 IF
// 9 ILOAD // 10 ILOAD
// 4 ISTORE // 5 ISTORE
// 1 IADD // 1 IADD
// 0 ISUB // 0 ISUB
// 0 IINC // 0 IINC
@@ -42,8 +42,8 @@ fun box(): String {
// 1 IFGE // 1 IFGE
// 7 IF // 7 IF
// 1 INEG // 1 INEG
// 18 ILOAD // 19 ILOAD
// 8 ISTORE // 9 ISTORE
// 1 IADD // 1 IADD
// 0 ISUB // 0 ISUB
// 0 IINC // 0 IINC
@@ -35,8 +35,8 @@ fun box(): String {
// 1 IF_ICMPEQ // 1 IF_ICMPEQ
// 2 IFGT // 2 IFGT
// 4 IF // 4 IF
// 13 ILOAD // 14 ILOAD
// 6 ISTORE // 7 ISTORE
// 1 IADD // 1 IADD
// 0 ISUB // 0 ISUB
// 0 IINC // 0 IINC
@@ -42,8 +42,8 @@ fun box(): String {
// 1 INEG // 1 INEG
// 0 INVOKESTATIC kotlin/UInt.constructor-impl // 0 INVOKESTATIC kotlin/UInt.constructor-impl
// 0 INVOKE\w+ kotlin/UInt.(un)?box-impl // 0 INVOKE\w+ kotlin/UInt.(un)?box-impl
// 19 ILOAD // 20 ILOAD
// 9 ISTORE // 10 ISTORE
// 1 IADD // 1 IADD
// 0 ISUB // 0 ISUB
// 0 IINC // 0 IINC
@@ -31709,6 +31709,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInDownToWithPossibleUnderflow.kt"); 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 @Test
@TestMetadata("forIntInDownTo.kt") @TestMetadata("forIntInDownTo.kt")
public void testForIntInDownTo() throws Exception { public void testForIntInDownTo() throws Exception {
@@ -31847,6 +31847,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInDownToWithPossibleUnderflow.kt"); 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 @Test
@TestMetadata("forIntInDownTo.kt") @TestMetadata("forIntInDownTo.kt")
public void testForIntInDownTo() throws Exception { public void testForIntInDownTo() throws Exception {
@@ -27001,6 +27001,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/ranges/forInDownTo/forInDownToWithPossibleUnderflow.kt"); 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") @TestMetadata("forIntInDownTo.kt")
public void testForIntInDownTo() throws Exception { public void testForIntInDownTo() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownTo.kt"); 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"); 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") @TestMetadata("forIntInDownTo.kt")
public void testForIntInDownTo() throws Exception { public void testForIntInDownTo() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownTo.kt"); 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"); 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") @TestMetadata("forIntInDownTo.kt")
public void testForIntInDownTo() throws Exception { public void testForIntInDownTo() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownTo.kt"); 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"); 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") @TestMetadata("forIntInDownTo.kt")
public void testForIntInDownTo() throws Exception { public void testForIntInDownTo() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInDownTo/forIntInDownTo.kt"); 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"); 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 @Test
@TestMetadata("forIntInDownTo.kt") @TestMetadata("forIntInDownTo.kt")
public void testForIntInDownTo() throws Exception { public void testForIntInDownTo() throws Exception {