ForLoopsLowering: Reduce unnecessary temporary variables.

This commit is contained in:
Mark Punzalan
2020-04-29 16:40:12 -07:00
committed by Alexander Udalov
parent eaddd02e9b
commit e1120f49d8
3 changed files with 48 additions and 31 deletions
@@ -66,8 +66,13 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
override val consumesLoopVariableComponents = false
val inductionVariable: IrVariable
val stepVariable: IrVariable
val stepVariableForIncrement: IrVariable?
protected val stepVariable: IrVariable?
protected val stepVariableForIncrement: IrVariable?
val stepExpression: IrExpression
// Always copy `stepExpression` is it may be used multiple times.
get() = field.deepCopyWithSymbols()
protected val lastVariableIfCanCacheLast: IrVariable?
protected val lastExpression: IrExpression
// Always copy `lastExpression` is it may be used in multiple conditions.
@@ -89,7 +94,7 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
//
// In the above example, if first() is a Long and last() is an Int, this creates a
// LongProgression so last() should be cast to a Long.
inductionVariable = scope.createTemporaryVariable(
inductionVariable = scope.createTmpVariable(
headerInfo.progressionType.castElementIfNecessary(headerInfo.first, context),
nameHint = "inductionVariable",
isMutable = true
@@ -102,7 +107,7 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
val last = ensureNotNullable(headerInfo.progressionType.castElementIfNecessary(headerInfo.last, context))
lastVariableIfCanCacheLast = if (headerInfo.canCacheLast) {
scope.createTemporaryVariable(
scope.createTmpVariable(
last,
nameHint = "last"
)
@@ -110,25 +115,28 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
lastExpression = if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else last
stepVariable =
scope.createTemporaryVariable(
val stepType = headerInfo.progressionType.stepType(context.irBuiltIns)
val (tmpStepVar, tmpStepExpression) =
createTemporaryVariableIfNecessary(
ensureNotNullable(headerInfo.progressionType.castStepIfNecessary(headerInfo.step, context)),
nameHint = "step",
irType = headerInfo.progressionType.stepType(context.irBuiltIns)
irType = stepType
)
stepVariable = tmpStepVar
stepExpression = tmpStepExpression
stepVariableForIncrement = when (headerInfo.progressionType) {
ProgressionType.UINT_PROGRESSION -> {
val castFun = context.ir.symbols.toUIntByExtensionReceiver.getValue(stepVariable.type.toKotlinType())
scope.createTemporaryVariable(
irCall(castFun).apply { extensionReceiver = irGet(stepVariable) },
val castFun = context.ir.symbols.toUIntByExtensionReceiver.getValue(stepType.toKotlinType())
scope.createTmpVariable(
irCall(castFun).apply { extensionReceiver = stepExpression },
nameHint = "stepForIncrement"
)
}
ProgressionType.ULONG_PROGRESSION -> {
val castFun = context.ir.symbols.toULongByExtensionReceiver.getValue(stepVariable.type.toKotlinType())
scope.createTemporaryVariable(
irCall(castFun).apply { extensionReceiver = irGet(stepVariable) },
val castFun = context.ir.symbols.toULongByExtensionReceiver.getValue(stepType.toKotlinType())
scope.createTmpVariable(
irCall(castFun).apply { extensionReceiver = stepExpression },
nameHint = "stepForIncrement"
)
}
@@ -147,17 +155,20 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
/** Statement used to increment the induction variable. */
protected fun incrementInductionVariable(builder: DeclarationIrBuilder): IrStatement = with(builder) {
// inductionVariable = inductionVariable + step
val stepVariableToUse = stepVariableForIncrement ?: stepVariable
val stepExpressionToUse = stepVariableForIncrement?.let { irGet(stepVariableForIncrement) } ?: stepExpression
// NOTE: We cannot use `stepExpression.type` below because it may be of type `Nothing`. This happens in the case of an illegal step
// where the "step" is actually a `throw IllegalArgumentException(...)`.
val stepType = stepVariableForIncrement?.type ?: headerInfo.progressionType.stepType(context.irBuiltIns)
val plusFun = elementType.getClass()!!.functions.single {
it.name == OperatorNameConventions.PLUS &&
it.valueParameters.size == 1 &&
it.valueParameters[0].type == stepVariableToUse.type
it.valueParameters[0].type == stepType
}
irSetVar(
inductionVariable.symbol, irCallOp(
plusFun.symbol, plusFun.returnType,
irGet(inductionVariable),
irGet(stepVariableToUse)
stepExpressionToUse
)
)
}
@@ -230,18 +241,17 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
// // (use `<` if last is exclusive)
// (step > 0 && inductionVar <= last) || (step < 0 || last <= inductionVar)
val stepTypeClassifier = progressionType.stepClassifier(builtIns)
val isLong = progressionType == ProgressionType.LONG_PROGRESSION
context.oror(
context.andand(
irCall(builtIns.greaterFunByOperandType.getValue(stepTypeClassifier)).apply {
putValueArgument(0, irGet(stepVariable))
putValueArgument(0, stepExpression)
putValueArgument(1, if (progressionType.isLong) irLong(0) else irInt(0))
},
conditionForIncreasing()
),
context.andand(
irCall(builtIns.lessFunByOperandType.getValue(stepTypeClassifier)).apply {
putValueArgument(0, irGet(stepVariable))
putValueArgument(0, stepExpression)
putValueArgument(1, if (progressionType.isLong) irLong(0) else irInt(0))
},
conditionForDecreasing()
@@ -287,7 +297,7 @@ internal class ProgressionLoopHeader(
with(builder) {
// loopVariable is used in the loop condition if it can overflow. If no loopVariable was provided, create one.
this@ProgressionLoopHeader.loopVariable = if (headerInfo.canOverflow && loopVariable == null) {
scope.createTemporaryVariable(
scope.createTmpVariable(
irGet(inductionVariable),
nameHint = "loopVariable",
isMutable = true
@@ -443,13 +453,13 @@ internal class WithIndexLoopHeader(
if (nestedLoopHeader is NumericForLoopHeader<*> &&
nestedLoopHeader.inductionVariable.type.isInt() &&
nestedLoopHeader.inductionVariable.initializer?.constLongValue == 0L &&
nestedLoopHeader.stepVariable.initializer?.constLongValue == 1L
nestedLoopHeader.stepExpression.constLongValue == 1L
) {
indexVariable = nestedLoopHeader.inductionVariable
ownsIndexVariable = false
incrementIndexStatement = null
} else {
indexVariable = scope.createTemporaryVariable(
indexVariable = scope.createTmpVariable(
irInt(0),
nameHint = "index",
isMutable = true
@@ -609,24 +609,24 @@ internal class DefaultProgressionHandler(private val context: CommonBackendConte
override fun build(expression: IrExpression, scopeOwner: IrSymbol): HeaderInfo? =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
// Directly use the `first/last/step` properties of the progression.
val progression = scope.createTmpVariable(expression, nameHint = "progression")
val progressionClass = progression.type.getClass()!!
val (progressionVar, progressionExpression) = createTemporaryVariableIfNecessary(expression, nameHint = "progression")
val progressionClass = progressionExpression.type.getClass()!!
val first = irCall(progressionClass.symbol.getPropertyGetter("first")!!).apply {
dispatchReceiver = irGet(progression)
dispatchReceiver = progressionExpression
}
val last = irCall(progressionClass.symbol.getPropertyGetter("last")!!).apply {
dispatchReceiver = irGet(progression)
dispatchReceiver = progressionExpression.deepCopyWithSymbols()
}
val step = irCall(progressionClass.symbol.getPropertyGetter("step")!!).apply {
dispatchReceiver = irGet(progression)
dispatchReceiver = progressionExpression.deepCopyWithSymbols()
}
ProgressionHeaderInfo(
ProgressionType.fromIrType(progression.type, symbols)!!,
ProgressionType.fromIrType(progressionExpression.type, symbols)!!,
first,
last,
step,
additionalVariables = listOf(progression),
additionalVariables = listOfNotNull(progressionVar),
direction = ProgressionDirection.UNKNOWN
)
}
@@ -8,12 +8,16 @@ package org.jetbrains.kotlin.backend.common.lower.loops
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.ir.builders.createTmpVariable
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
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.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
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.functions
@@ -75,9 +79,12 @@ internal val IrExpression.constLongValue: Long?
*
* This helps reduce local variable usage.
*/
internal fun DeclarationIrBuilder.createTemporaryVariableIfNecessary(expression: IrExpression, nameHint: String? = null) =
internal fun DeclarationIrBuilder.createTemporaryVariableIfNecessary(
expression: IrExpression, nameHint: String? = null,
irType: IrType? = null
): Pair<IrVariable?, IrExpression> =
if (expression.canHaveSideEffects) {
scope.createTmpVariable(expression, nameHint = nameHint).let { Pair(it, irGet(it)) }
scope.createTmpVariable(expression, nameHint = nameHint, irType = irType).let { Pair(it, irGet(it)) }
} else {
Pair(null, expression)
}