From e1120f49d8e2a71edab8c550fe9c937a32f4f773 Mon Sep 17 00:00:00 2001 From: Mark Punzalan Date: Wed, 29 Apr 2020 16:40:12 -0700 Subject: [PATCH] ForLoopsLowering: Reduce unnecessary temporary variables. --- .../common/lower/loops/HeaderProcessor.kt | 54 +++++++++++-------- .../common/lower/loops/ProgressionHandlers.kt | 14 ++--- .../backend/common/lower/loops/Utils.kt | 11 +++- 3 files changed, 48 insertions(+), 31 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt index 5572b6c69ef..20f23d893ff 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt @@ -66,8 +66,13 @@ internal abstract class NumericForLoopHeader( 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( // // 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( 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( 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( /** 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( // // (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 diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt index 8f6cc32035c..240b9c043c1 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt @@ -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 ) } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/Utils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/Utils.kt index a47b79ddb16..ab141b7824d 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/Utils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/Utils.kt @@ -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 = 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) } \ No newline at end of file