IR KT-49372 cache progression loop parameters if their values can change
This commit is contained in:
committed by
teamcityserver
parent
7fb82232cd
commit
45a4cea655
+2
-2
@@ -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
|
||||
|
||||
+39
-10
@@ -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()) {
|
||||
|
||||
+4
-4
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user