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
@@ -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.