Encapsulate variable construction and declaration order logic within

ForLoopHeader instead of in HeaderProcessor.
This commit is contained in:
Mark Punzalan
2019-11-01 04:26:52 -07:00
committed by max-kammerer
parent 60c05362d2
commit 2ab539c5eb
@@ -53,31 +53,98 @@ internal interface ForLoopHeader {
} }
internal abstract class NumericForLoopHeader( internal abstract class NumericForLoopHeader(
protected open val headerInfo: HeaderInfo, headerInfo: HeaderInfo,
val inductionVariable: IrVariable, builder: DeclarationIrBuilder,
lastExpression: IrExpression, protected val isLastInclusive: Boolean
val step: IrVariable,
val isLastInclusive: Boolean,
override val loopInitStatements: List<IrStatement>
) : ForLoopHeader { ) : ForLoopHeader {
val lastExpression: IrExpression = lastExpression protected val inductionVariable: IrVariable
protected val lastVariableIfCanCacheLast: IrVariable?
protected val stepVariable: IrVariable
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.
get() = field.deepCopyWithSymbols() get() = field.deepCopyWithSymbols()
init {
with(builder) {
// For this loop:
//
// for (i in first()..last() step step())
//
// We need to cast first(), last(). and step() to conform to the progression type so
// that operations on the induction variable within the loop are more efficient.
//
// 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(
headerInfo.first.castIfNecessary(
headerInfo.progressionType.elementType(context.irBuiltIns),
headerInfo.progressionType.elementCastFunctionName
),
nameHint = "inductionVariable",
isMutable = true
)
// Due to features of PSI2IR we can obtain nullable arguments here while actually
// they are non-nullable (the frontend takes care about this). So we need to cast
// them to non-nullable.
// TODO: Confirm if casting to non-nullable is still necessary
val last = ensureNotNullable(
headerInfo.last.castIfNecessary(
headerInfo.progressionType.elementType(context.irBuiltIns),
headerInfo.progressionType.elementCastFunctionName
)
)
lastVariableIfCanCacheLast = if (headerInfo.canCacheLast) {
scope.createTemporaryVariable(
last,
nameHint = "last"
)
} else null
lastExpression = if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else last
stepVariable = headerInfo.progressionType.stepType(context.irBuiltIns).let {
scope.createTemporaryVariable(
ensureNotNullable(
headerInfo.step.castIfNecessary(
it,
headerInfo.progressionType.stepCastFunctionName
)
),
nameHint = "step",
irType = it
)
}
}
}
// This cannot be declared and initialized directly in the constructor. At the time of the base class (ForLoopHeader) constructor
// execution, the `headerInfo` property overridden in the derived class (e.g., NumericForLoopHeader) is not yet initialized.
// Therefore, we must use the `headerInfo` constructor parameter in the "init" block above instead of using the property.
protected open val headerInfo: HeaderInfo = headerInfo
private fun DeclarationIrBuilder.ensureNotNullable(expression: IrExpression) =
if (expression.type is IrSimpleType && expression.type.isNullable()) {
irImplicitCast(expression, expression.type.makeNotNull())
} else {
expression
}
/** 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 plusFun = inductionVariable.type.getClass()!!.functions.single { val plusFun = inductionVariable.type.getClass()!!.functions.single {
it.name == OperatorNameConventions.PLUS && it.name == OperatorNameConventions.PLUS &&
it.valueParameters.size == 1 && it.valueParameters.size == 1 &&
it.valueParameters[0].type == step.type it.valueParameters[0].type == stepVariable.type
} }
irSetVar( irSetVar(
inductionVariable.symbol, irCallOp( inductionVariable.symbol, irCallOp(
plusFun.symbol, plusFun.returnType, plusFun.symbol, plusFun.returnType,
irGet(inductionVariable), irGet(inductionVariable),
irGet(step) irGet(stepVariable)
) )
) )
} }
@@ -114,7 +181,7 @@ internal abstract class NumericForLoopHeader(
context.oror( context.oror(
context.andand( context.andand(
irCall(builtIns.greaterFunByOperandType[stepType.classifierOrFail]!!).apply { irCall(builtIns.greaterFunByOperandType[stepType.classifierOrFail]!!).apply {
putValueArgument(0, irGet(step)) putValueArgument(0, irGet(stepVariable))
putValueArgument(1, if (isLong) irLong(0) else irInt(0)) putValueArgument(1, if (isLong) irLong(0) else irInt(0))
}, },
irCall(compFun).apply { irCall(compFun).apply {
@@ -123,7 +190,7 @@ internal abstract class NumericForLoopHeader(
}), }),
context.andand( context.andand(
irCall(builtIns.lessFunByOperandType[stepType.classifierOrFail]!!).apply { irCall(builtIns.lessFunByOperandType[stepType.classifierOrFail]!!).apply {
putValueArgument(0, irGet(step)) putValueArgument(0, irGet(stepVariable))
putValueArgument(1, if (isLong) irLong(0) else irInt(0)) putValueArgument(1, if (isLong) irLong(0) else irInt(0))
}, },
irCall(compFun).apply { irCall(compFun).apply {
@@ -138,17 +205,25 @@ internal abstract class NumericForLoopHeader(
internal class ProgressionLoopHeader( internal class ProgressionLoopHeader(
override val headerInfo: ProgressionHeaderInfo, override val headerInfo: ProgressionHeaderInfo,
inductionVariable: IrVariable, builder: DeclarationIrBuilder
lastExpression: IrExpression, ) : NumericForLoopHeader(headerInfo, builder, isLastInclusive = true) {
step: IrVariable,
loopInitStatements: List<IrStatement> // For this loop:
) : NumericForLoopHeader( //
headerInfo, inductionVariable, // for (i in first()..last() step step())
lastExpression = lastExpression, //
step = step, // ...the functions may have side-effects so we need to call them in the following order: first() (inductionVariable), last(), step().
isLastInclusive = true, // Additional variables come first as they may be needed to the subsequent variables.
loopInitStatements = loopInitStatements //
) { // In the case of a reversed range, the `inductionVariable` and `last` variables are swapped, therefore the declaration order must be
// swapped to preserve the correct evaluation order.
override val loopInitStatements = headerInfo.additionalVariables + (
if (headerInfo.isReversed)
listOfNotNull(lastVariableIfCanCacheLast, inductionVariable)
else
listOfNotNull(inductionVariable, lastVariableIfCanCacheLast)
) +
stepVariable
private var loopVariable: IrVariable? = null private var loopVariable: IrVariable? = null
@@ -209,15 +284,10 @@ internal class ProgressionLoopHeader(
internal class IndexedGetLoopHeader( internal class IndexedGetLoopHeader(
override val headerInfo: IndexedGetHeaderInfo, override val headerInfo: IndexedGetHeaderInfo,
inductionVariable: IrVariable, builder: DeclarationIrBuilder
lastExpression: IrExpression, ) : NumericForLoopHeader(headerInfo, builder, isLastInclusive = false) {
step: IrVariable,
loopInitStatements: List<IrStatement> override val loopInitStatements = listOfNotNull(headerInfo.objectVariable, inductionVariable, lastVariableIfCanCacheLast, stepVariable)
) : NumericForLoopHeader(
headerInfo, inductionVariable, lastExpression, step,
isLastInclusive = false,
loopInitStatements = loopInitStatements
) {
override fun initializeIteration(loopVariable: IrVariable, symbols: Symbols<CommonBackendContext>, builder: DeclarationIrBuilder) = override fun initializeIteration(loopVariable: IrVariable, symbols: Symbols<CommonBackendContext>, builder: DeclarationIrBuilder) =
with(builder) { with(builder) {
@@ -292,97 +362,9 @@ internal class HeaderProcessor(
?: return null // If the iterable is not supported. ?: return null // If the iterable is not supported.
val builder = context.createIrBuilder(scopeOwnerSymbol(), variable.startOffset, variable.endOffset) val builder = context.createIrBuilder(scopeOwnerSymbol(), variable.startOffset, variable.endOffset)
with(builder) { return when (headerInfo) {
// For this loop: is IndexedGetHeaderInfo -> IndexedGetLoopHeader(headerInfo, builder)
// is ProgressionHeaderInfo -> ProgressionLoopHeader(headerInfo, builder)
// for (i in first()..last() step step())
//
// We need to cast first(), last(). and step() to conform to the progression type so
// that operations on the induction variable within the loop are more efficient.
//
// 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.
val inductionVariable = scope.createTemporaryVariable(
headerInfo.first.castIfNecessary(
headerInfo.progressionType.elementType(context.irBuiltIns),
headerInfo.progressionType.elementCastFunctionName
),
nameHint = "inductionVariable",
isMutable = true
)
// Due to features of PSI2IR we can obtain nullable arguments here while actually
// they are non-nullable (the frontend takes care about this). So we need to cast
// them to non-nullable.
// TODO: Confirm if casting to non-nullable is still necessary
val lastExpression = ensureNotNullable(
headerInfo.last.castIfNecessary(
headerInfo.progressionType.elementType(context.irBuiltIns),
headerInfo.progressionType.elementCastFunctionName
)
)
val lastVariableIfCanCacheLast = if (headerInfo.canCacheLast) {
scope.createTemporaryVariable(
lastExpression,
nameHint = "last"
)
} else null
val stepVariable = headerInfo.progressionType.stepType(context.irBuiltIns).let {
scope.createTemporaryVariable(
ensureNotNullable(
headerInfo.step.castIfNecessary(
it,
headerInfo.progressionType.stepCastFunctionName
)
),
nameHint = "step",
irType = it
)
}
return when (headerInfo) {
is IndexedGetHeaderInfo -> IndexedGetLoopHeader(
headerInfo,
inductionVariable,
if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else lastExpression,
stepVariable,
listOfNotNull(headerInfo.objectVariable, inductionVariable, lastVariableIfCanCacheLast, stepVariable)
)
is ProgressionHeaderInfo -> {
// For this loop:
//
// for (i in first()..last() step step())
//
// ...the functions may have side-effects so we need to call them in the following order: first() (inductionVariable), last(), step().
// Additional variables come first as they may be needed to the subsequent variables.
//
// In the case of a reversed range, the `inductionVariable` and `last` variables are swapped, therefore the declaration order must be
// swapped to preserve the correct evaluation order.
val declarations = headerInfo.additionalVariables + (
if (headerInfo.isReversed)
listOfNotNull(lastVariableIfCanCacheLast, inductionVariable)
else
listOfNotNull(inductionVariable, lastVariableIfCanCacheLast)
) +
stepVariable
ProgressionLoopHeader(
headerInfo,
inductionVariable,
if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else lastExpression,
stepVariable,
declarations
)
}
}
} }
} }
private fun DeclarationIrBuilder.ensureNotNullable(expression: IrExpression) =
if (expression.type is IrSimpleType && expression.type.isNullable()) {
irImplicitCast(expression, expression.type.makeNotNull())
} else {
expression
}
} }