Eliminated the redundant ForLoopHeader.needsEmptinessCheck property, cast "bound" to progression type if necessary, fixed variable declaration order.

This commit is contained in:
Mark Punzalan
2019-03-26 11:20:01 -07:00
committed by max-kammerer
parent 6b83c04472
commit 7afe121238
5 changed files with 45 additions and 59 deletions
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.util.OperatorNameConventions
val forLoopsPhase = makeIrFilePhase(
::ForLoopsLowering,
@@ -171,12 +170,7 @@ private class RangeLoopTransformer(
oldLoopToNewLoop[loop] = newLoop
// Surround the new loop with a check for an empty loop, if necessary.
if (loopHeader.needsEmptinessCheck) {
val notEmptyCondition = loopHeader.buildNotEmptyCondition(this@with)
if (notEmptyCondition != null)
return irIfThen(notEmptyCondition, newLoop)
}
return newLoop
return loopHeader.buildNotEmptyConditionIfNecessary(this@with)?.let { irIfThen(it, newLoop) } ?: newLoop
}
}
@@ -30,7 +30,6 @@ internal sealed class ForLoopHeader(
val last: IrVariable,
val step: IrVariable,
val progressionType: ProgressionType,
val needsEmptinessCheck: Boolean,
var loopVariable: IrVariable? = null
) {
/** Expression used to initialize the loop variable at the beginning of the loop. */
@@ -48,7 +47,7 @@ internal sealed class ForLoopHeader(
*
* Returns null if no check is needed for the for-loop.
*/
abstract fun buildNotEmptyCondition(builder: DeclarationIrBuilder): IrExpression?
abstract fun buildNotEmptyConditionIfNecessary(builder: DeclarationIrBuilder): IrExpression?
}
internal class ProgressionLoopHeader(
@@ -56,7 +55,7 @@ internal class ProgressionLoopHeader(
inductionVariable: IrVariable,
last: IrVariable,
step: IrVariable
) : ForLoopHeader(inductionVariable, last, step, headerInfo.progressionType, needsEmptinessCheck = true) {
) : ForLoopHeader(inductionVariable, last, step, headerInfo.progressionType) {
override fun initializeLoopVariable(symbols: Symbols<CommonBackendContext>, builder: DeclarationIrBuilder) = with(builder) {
// loopVariable = inductionVariable
@@ -64,17 +63,17 @@ internal class ProgressionLoopHeader(
}
override val declarations: List<IrStatement>
get() = headerInfo.additionalVariables + listOf(inductionVariable, step, last)
get() = headerInfo.additionalVariables + listOf(inductionVariable, last, step)
override fun buildInnerLoop(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) {
// Condition: loopVariable != last
assert(loopVariable != null)
val newCondition = irCall(context.irBuiltIns.booleanNotSymbol).apply {
putValueArgument(0, irCall(context.irBuiltIns.eqeqSymbol).apply {
putValueArgument(0, irGet(loopVariable!!))
putValueArgument(1, irGet(last))
})
}
val booleanNotFun = context.irBuiltIns.booleanClass.functions.first { it.owner.name.asString() == "not" }
val newCondition = irCallOp(booleanNotFun, booleanNotFun.owner.returnType, irCall(context.irBuiltIns.eqeqSymbol).apply {
putValueArgument(0, irGet(loopVariable!!))
putValueArgument(1, irGet(last))
})
// TODO: Build while loop (instead of do-while) where possible, e.g., in "until" ranges, or when "last" is known to be < MAX_VALUE
IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply {
@@ -84,12 +83,13 @@ internal class ProgressionLoopHeader(
}
}
override fun buildNotEmptyCondition(builder: DeclarationIrBuilder): IrExpression? =
override fun buildNotEmptyConditionIfNecessary(builder: DeclarationIrBuilder): IrExpression? =
with(builder) {
val builtIns = context.irBuiltIns
val progressionKotlinType = progressionType.elementType(builtIns).toKotlinType()
val lessOrEqualFun = builtIns.lessOrEqualFunByOperandType[progressionKotlinType]!!
// The default "not empty" condition depends on the direction.
val notEmptyCondition = when (headerInfo.direction) {
ProgressionDirection.DECREASING ->
// last <= inductionVariable
@@ -143,7 +143,7 @@ internal class ArrayLoopHeader(
inductionVariable: IrVariable,
last: IrVariable,
step: IrVariable
) : ForLoopHeader(inductionVariable, last, step, ProgressionType.INT_PROGRESSION, needsEmptinessCheck = false) {
) : ForLoopHeader(inductionVariable, last, step, ProgressionType.INT_PROGRESSION) {
override fun initializeLoopVariable(symbols: Symbols<CommonBackendContext>, builder: DeclarationIrBuilder) = with(builder) {
// loopVariable = array[inductionVariable]
@@ -155,7 +155,7 @@ internal class ArrayLoopHeader(
}
override val declarations: List<IrStatement>
get() = listOf(headerInfo.arrayVariable, inductionVariable, step, last)
get() = listOf(headerInfo.arrayVariable, inductionVariable, last, step)
override fun buildInnerLoop(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) {
// Condition: loopVariable != last
@@ -174,7 +174,7 @@ internal class ArrayLoopHeader(
}
// No surrounding emptiness check is needed with a while loop.
override fun buildNotEmptyCondition(builder: DeclarationIrBuilder): IrExpression? = null
override fun buildNotEmptyConditionIfNecessary(builder: DeclarationIrBuilder): IrExpression? = null
}
/**
@@ -277,22 +277,21 @@ internal class HeaderProcessor(
}
}
}
}
private fun IrExpression.castIfNecessary(targetType: IrType, numberCastFunctionName: Name): IrExpression {
return if (type.toKotlinType() == targetType.toKotlinType()) {
this
} else {
val function = type.getClass()!!.functions.first { it.name == numberCastFunctionName }
IrCallImpl(startOffset, endOffset, function.returnType, function.symbol)
.apply { dispatchReceiver = this@castIfNecessary }
}
internal fun DeclarationIrBuilder.ensureNotNullable(expression: IrExpression) =
if (expression.type is IrSimpleType && expression.type.isNullable()) {
irImplicitCast(expression, expression.type.makeNotNull())
} else {
expression
}
private fun DeclarationIrBuilder.ensureNotNullable(expression: IrExpression) =
if (expression.type is IrSimpleType && expression.type.isNullable()) {
irImplicitCast(expression, expression.type.makeNotNull())
} else {
expression
}
internal fun IrExpression.castIfNecessary(targetType: IrType, numberCastFunctionName: Name): IrExpression {
return if (type.toKotlinType() == targetType.toKotlinType()) {
this
} else {
val function = type.getClass()!!.functions.first { it.name == numberCastFunctionName }
IrCallImpl(startOffset, endOffset, function.returnType, function.symbol)
.apply { dispatchReceiver = this@castIfNecessary }
}
}
@@ -81,46 +81,41 @@ internal class UntilHandler(private val context: CommonBackendContext, private v
with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) {
// `last = bound - 1` for the loop `for (i in first until bound)`.
val bound = scope.createTemporaryVariable(
call.getValueArgument(0)!!, nameHint = "bound",
ensureNotNullable(
call.getValueArgument(0)!!.castIfNecessary(
data.elementType(context.irBuiltIns),
data.elementCastFunctionName
)
), nameHint = "bound",
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE
)
val decFun = data.decFun(context.irBuiltIns)
val last = irCallOp(decFun.symbol, bound.type, call.getValueArgument(0)!!)
val last = irCallOp(decFun.symbol, bound.type, irGet(bound))
// An additional emptiness condition is required for the corner case:
// The default "not empty" check cannot be used for the required for the corner case:
//
// ```
// for (i in a until MIN_VALUE) {}
// ```
// for (i in a until MIN_VALUE) {}
//
// ...which should always be considered an empty range. When the given bound is MIN_VALUE, and because `last = bound - 1`,
// "last" will underflow to MAX_VALUE, therefore the default emptiness check:
// "last" will underflow to MAX_VALUE, therefore the default "not empty" check:
//
// ```
// if (first <= last) { /* loop */ }
// ```
// if (first <= last) { /* loop */ }
//
// ...will always be true and won't consider the range as empty. Therefore, we need to add an additional condition to the
// emptiness check so that it becomes:
//
// ```
// if (first <= last && bound > MIN_VALUE) { /* loop */ }
// ```
// TODO: Do not add additionalEmptinessCondition if "bound" is const and > MIN_VALUE
// if (first <= last && bound > MIN_VALUE) { /* loop */ }
ProgressionHeaderInfo(
data,
first = call.extensionReceiver!!,
last = last,
step = irInt(1),
additionalVariables = listOf(bound),
additionalNotEmptyCondition = buildMinValueConditionIfNecessary(data, irGet(bound))
additionalNotEmptyCondition = buildMinValueCondition(data, irGet(bound))
)
}
private fun DeclarationIrBuilder.buildMinValueConditionIfNecessary(
progressionType: ProgressionType,
bound: IrExpression
): IrExpression? {
private fun DeclarationIrBuilder.buildMinValueCondition(progressionType: ProgressionType, bound: IrExpression): IrExpression {
val irBuiltIns = context.irBuiltIns
val minConst = when (progressionType) {
ProgressionType.INT_PROGRESSION -> irInt(Int.MIN_VALUE)
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
fun test(): Int {
var sum = 0
for (i in 1 until 6) {
@@ -12,4 +11,4 @@ fun test(): Int {
// 0 getEnd
// 0 getFirst
// 0 getLast
// 1 IF
// 0 getStep
@@ -7,5 +7,4 @@ fun f(r: IntRange) {
// 0 getStart
// 0 getEnd
// 1 getFirst
// 1 getLast
// 1 getStep
// 1 getLast