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 override val consumesLoopVariableComponents = false
val inductionVariable: IrVariable 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 lastVariableIfCanCacheLast: IrVariable?
protected val lastExpression: IrExpression protected val lastExpression: IrExpression
// Always copy `lastExpression` is it may be used in multiple conditions. // 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 // 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. // LongProgression so last() should be cast to a Long.
inductionVariable = scope.createTemporaryVariable( inductionVariable = scope.createTmpVariable(
headerInfo.progressionType.castElementIfNecessary(headerInfo.first, context), headerInfo.progressionType.castElementIfNecessary(headerInfo.first, context),
nameHint = "inductionVariable", nameHint = "inductionVariable",
isMutable = true isMutable = true
@@ -102,7 +107,7 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
val last = ensureNotNullable(headerInfo.progressionType.castElementIfNecessary(headerInfo.last, context)) val last = ensureNotNullable(headerInfo.progressionType.castElementIfNecessary(headerInfo.last, context))
lastVariableIfCanCacheLast = if (headerInfo.canCacheLast) { lastVariableIfCanCacheLast = if (headerInfo.canCacheLast) {
scope.createTemporaryVariable( scope.createTmpVariable(
last, last,
nameHint = "last" nameHint = "last"
) )
@@ -110,25 +115,28 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
lastExpression = if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else last lastExpression = if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else last
stepVariable = val stepType = headerInfo.progressionType.stepType(context.irBuiltIns)
scope.createTemporaryVariable( val (tmpStepVar, tmpStepExpression) =
createTemporaryVariableIfNecessary(
ensureNotNullable(headerInfo.progressionType.castStepIfNecessary(headerInfo.step, context)), ensureNotNullable(headerInfo.progressionType.castStepIfNecessary(headerInfo.step, context)),
nameHint = "step", nameHint = "step",
irType = headerInfo.progressionType.stepType(context.irBuiltIns) irType = stepType
) )
stepVariable = tmpStepVar
stepExpression = tmpStepExpression
stepVariableForIncrement = when (headerInfo.progressionType) { stepVariableForIncrement = when (headerInfo.progressionType) {
ProgressionType.UINT_PROGRESSION -> { ProgressionType.UINT_PROGRESSION -> {
val castFun = context.ir.symbols.toUIntByExtensionReceiver.getValue(stepVariable.type.toKotlinType()) val castFun = context.ir.symbols.toUIntByExtensionReceiver.getValue(stepType.toKotlinType())
scope.createTemporaryVariable( scope.createTmpVariable(
irCall(castFun).apply { extensionReceiver = irGet(stepVariable) }, irCall(castFun).apply { extensionReceiver = stepExpression },
nameHint = "stepForIncrement" nameHint = "stepForIncrement"
) )
} }
ProgressionType.ULONG_PROGRESSION -> { ProgressionType.ULONG_PROGRESSION -> {
val castFun = context.ir.symbols.toULongByExtensionReceiver.getValue(stepVariable.type.toKotlinType()) val castFun = context.ir.symbols.toULongByExtensionReceiver.getValue(stepType.toKotlinType())
scope.createTemporaryVariable( scope.createTmpVariable(
irCall(castFun).apply { extensionReceiver = irGet(stepVariable) }, irCall(castFun).apply { extensionReceiver = stepExpression },
nameHint = "stepForIncrement" nameHint = "stepForIncrement"
) )
} }
@@ -147,17 +155,20 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
/** Statement used to increment the induction variable. */ /** Statement used to increment the induction variable. */
protected fun incrementInductionVariable(builder: DeclarationIrBuilder): IrStatement = with(builder) { protected fun incrementInductionVariable(builder: DeclarationIrBuilder): IrStatement = with(builder) {
// inductionVariable = inductionVariable + step // 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 { val plusFun = elementType.getClass()!!.functions.single {
it.name == OperatorNameConventions.PLUS && it.name == OperatorNameConventions.PLUS &&
it.valueParameters.size == 1 && it.valueParameters.size == 1 &&
it.valueParameters[0].type == stepVariableToUse.type it.valueParameters[0].type == stepType
} }
irSetVar( irSetVar(
inductionVariable.symbol, irCallOp( inductionVariable.symbol, irCallOp(
plusFun.symbol, plusFun.returnType, plusFun.symbol, plusFun.returnType,
irGet(inductionVariable), irGet(inductionVariable),
irGet(stepVariableToUse) stepExpressionToUse
) )
) )
} }
@@ -230,18 +241,17 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
// // (use `<` if last is exclusive) // // (use `<` if last is exclusive)
// (step > 0 && inductionVar <= last) || (step < 0 || last <= inductionVar) // (step > 0 && inductionVar <= last) || (step < 0 || last <= inductionVar)
val stepTypeClassifier = progressionType.stepClassifier(builtIns) val stepTypeClassifier = progressionType.stepClassifier(builtIns)
val isLong = progressionType == ProgressionType.LONG_PROGRESSION
context.oror( context.oror(
context.andand( context.andand(
irCall(builtIns.greaterFunByOperandType.getValue(stepTypeClassifier)).apply { irCall(builtIns.greaterFunByOperandType.getValue(stepTypeClassifier)).apply {
putValueArgument(0, irGet(stepVariable)) putValueArgument(0, stepExpression)
putValueArgument(1, if (progressionType.isLong) irLong(0) else irInt(0)) putValueArgument(1, if (progressionType.isLong) irLong(0) else irInt(0))
}, },
conditionForIncreasing() conditionForIncreasing()
), ),
context.andand( context.andand(
irCall(builtIns.lessFunByOperandType.getValue(stepTypeClassifier)).apply { irCall(builtIns.lessFunByOperandType.getValue(stepTypeClassifier)).apply {
putValueArgument(0, irGet(stepVariable)) putValueArgument(0, stepExpression)
putValueArgument(1, if (progressionType.isLong) irLong(0) else irInt(0)) putValueArgument(1, if (progressionType.isLong) irLong(0) else irInt(0))
}, },
conditionForDecreasing() conditionForDecreasing()
@@ -287,7 +297,7 @@ internal class ProgressionLoopHeader(
with(builder) { with(builder) {
// loopVariable is used in the loop condition if it can overflow. If no loopVariable was provided, create one. // 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) { this@ProgressionLoopHeader.loopVariable = if (headerInfo.canOverflow && loopVariable == null) {
scope.createTemporaryVariable( scope.createTmpVariable(
irGet(inductionVariable), irGet(inductionVariable),
nameHint = "loopVariable", nameHint = "loopVariable",
isMutable = true isMutable = true
@@ -443,13 +453,13 @@ internal class WithIndexLoopHeader(
if (nestedLoopHeader is NumericForLoopHeader<*> && if (nestedLoopHeader is NumericForLoopHeader<*> &&
nestedLoopHeader.inductionVariable.type.isInt() && nestedLoopHeader.inductionVariable.type.isInt() &&
nestedLoopHeader.inductionVariable.initializer?.constLongValue == 0L && nestedLoopHeader.inductionVariable.initializer?.constLongValue == 0L &&
nestedLoopHeader.stepVariable.initializer?.constLongValue == 1L nestedLoopHeader.stepExpression.constLongValue == 1L
) { ) {
indexVariable = nestedLoopHeader.inductionVariable indexVariable = nestedLoopHeader.inductionVariable
ownsIndexVariable = false ownsIndexVariable = false
incrementIndexStatement = null incrementIndexStatement = null
} else { } else {
indexVariable = scope.createTemporaryVariable( indexVariable = scope.createTmpVariable(
irInt(0), irInt(0),
nameHint = "index", nameHint = "index",
isMutable = true isMutable = true
@@ -609,24 +609,24 @@ internal class DefaultProgressionHandler(private val context: CommonBackendConte
override fun build(expression: IrExpression, scopeOwner: IrSymbol): HeaderInfo? = override fun build(expression: IrExpression, scopeOwner: IrSymbol): HeaderInfo? =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
// Directly use the `first/last/step` properties of the progression. // Directly use the `first/last/step` properties of the progression.
val progression = scope.createTmpVariable(expression, nameHint = "progression") val (progressionVar, progressionExpression) = createTemporaryVariableIfNecessary(expression, nameHint = "progression")
val progressionClass = progression.type.getClass()!! val progressionClass = progressionExpression.type.getClass()!!
val first = irCall(progressionClass.symbol.getPropertyGetter("first")!!).apply { val first = irCall(progressionClass.symbol.getPropertyGetter("first")!!).apply {
dispatchReceiver = irGet(progression) dispatchReceiver = progressionExpression
} }
val last = irCall(progressionClass.symbol.getPropertyGetter("last")!!).apply { val last = irCall(progressionClass.symbol.getPropertyGetter("last")!!).apply {
dispatchReceiver = irGet(progression) dispatchReceiver = progressionExpression.deepCopyWithSymbols()
} }
val step = irCall(progressionClass.symbol.getPropertyGetter("step")!!).apply { val step = irCall(progressionClass.symbol.getPropertyGetter("step")!!).apply {
dispatchReceiver = irGet(progression) dispatchReceiver = progressionExpression.deepCopyWithSymbols()
} }
ProgressionHeaderInfo( ProgressionHeaderInfo(
ProgressionType.fromIrType(progression.type, symbols)!!, ProgressionType.fromIrType(progressionExpression.type, symbols)!!,
first, first,
last, last,
step, step,
additionalVariables = listOf(progression), additionalVariables = listOfNotNull(progressionVar),
direction = ProgressionDirection.UNKNOWN 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.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.ir.builders.createTmpVariable 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.IrValueDeclaration
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstKind import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetValue 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.symbols.IrVariableSymbol
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.functions import org.jetbrains.kotlin.ir.util.functions
@@ -75,9 +79,12 @@ internal val IrExpression.constLongValue: Long?
* *
* This helps reduce local variable usage. * 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) { 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 { } else {
Pair(null, expression) Pair(null, expression)
} }