Move building of "not empty" condition (used to check if loop is empty)

from ForLoopsLowering into ForLoopHeader, reducing the building logic in
ForLoopsLowering.
This commit is contained in:
Mark Punzalan
2019-03-19 22:57:21 -07:00
committed by max-kammerer
parent a2e3d3635e
commit 7b153b2b68
9 changed files with 94 additions and 93 deletions
@@ -130,7 +130,8 @@ private class RangeLoopTransformer(
if (loop.origin != IrStatementOrigin.FOR_LOOP_INNER_WHILE) {
return super.visitWhileLoop(loop)
}
return with(context.createIrBuilder(getScopeOwnerSymbol(), loop.startOffset, loop.endOffset)) {
with(context.createIrBuilder(getScopeOwnerSymbol(), loop.startOffset, loop.endOffset)) {
val newBody = loop.body?.transform(this@RangeLoopTransformer, null)?.let {
if (it is IrContainerExpression && !it.isTransparentScope) {
IrCompositeImpl(startOffset, endOffset, it.type, it.origin, it.statements)
@@ -140,72 +141,16 @@ private class RangeLoopTransformer(
}
val loopHeader = getLoopHeader(loop.condition)
?: return super.visitWhileLoop(loop)
val newLoop = loopHeader.buildBody(this, loop, newBody)
val newLoop = loopHeader.buildInnerLoop(this, loop, newBody)
oldLoopToNewLoop[loop] = newLoop
// Build a check for an empty progression before the loop.
if (loopHeader is ProgressionLoopHeader) {
buildEmptinessCheck(newLoop, loopHeader)
} else {
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)
}
}
}
private fun DeclarationIrBuilder.buildMinValueCondition(forLoopHeader: ForLoopHeader): IrExpression {
// Condition for a corner case: for (i in a until Int.MIN_VALUE) {}.
// Check if forLoopHeader.bound > MIN_VALUE.
val progressionType = forLoopHeader.progressionType
val irBuiltIns = context.irBuiltIns
val minConst = when (progressionType) {
ProgressionType.INT_PROGRESSION -> IrConstImpl
.int(startOffset, endOffset, irBuiltIns.intType, Int.MIN_VALUE)
ProgressionType.CHAR_PROGRESSION -> IrConstImpl
.char(startOffset, endOffset, irBuiltIns.charType, 0.toChar())
ProgressionType.LONG_PROGRESSION -> IrConstImpl
.long(startOffset, endOffset, irBuiltIns.longType, Long.MIN_VALUE)
}
val compareTo = symbols.getBinaryOperator(
OperatorNameConventions.COMPARE_TO,
forLoopHeader.bound.type.toKotlinType(),
minConst.type.toKotlinType()
)
return irCall(irBuiltIns.greaterFunByOperandType[irBuiltIns.int]?.symbol!!).apply {
val compareToCall = irCall(compareTo).apply {
dispatchReceiver = irGet(forLoopHeader.bound)
putValueArgument(0, minConst)
}
putValueArgument(0, compareToCall)
putValueArgument(1, irInt(0))
}
}
private fun DeclarationIrBuilder.buildEmptinessCheck(loop: IrLoop, loopHeader: ProgressionLoopHeader): IrExpression {
val builtIns = context.irBuiltIns
val comparingBuiltIn = loopHeader.comparingFunction(builtIns)
// Check if inductionVariable <= last (or >= in case of downTo).
// TODO: Use comparingBuiltIn directly
val compareTo = symbols.getBinaryOperator(
OperatorNameConventions.COMPARE_TO,
loopHeader.inductionVariable.type.toKotlinType(),
loopHeader.last.type.toKotlinType()
)
val check = irCall(comparingBuiltIn).apply {
putValueArgument(
0,
irCallOp(compareTo, compareTo.owner.returnType, irGet(loopHeader.inductionVariable), irGet(loopHeader.last))
)
putValueArgument(1, irInt(0))
}
// Process closed and open ranges in different manners.
return if (loopHeader.closed) {
irIfThen(check, loop) // if (inductionVariable <= last) { loop }
} else {
// Take into account a corner case: for (i in a until Int.MIN_VALUE) {}.
// if (inductionVariable <= last && bound > MIN_VALUE) { loop }
irIfThen(check, irIfThen(buildMinValueCondition(loopHeader), loop))
return newLoop
}
}
@@ -37,6 +37,12 @@ enum class ProgressionType(val numberCastFunctionName: Name) {
ProgressionType.CHAR_PROGRESSION -> builtIns.charType
}
/** Returns the [IrType] of the `step` property in the progression. */
fun stepType(builtIns: IrBuiltIns): IrType = when (this) {
ProgressionType.INT_PROGRESSION, ProgressionType.CHAR_PROGRESSION -> builtIns.intType
ProgressionType.LONG_PROGRESSION -> builtIns.longType
}
companion object {
fun fromIrType(irType: IrType, symbols: Symbols<CommonBackendContext>): ProgressionType? = when {
irType.isSubtypeOfClass(symbols.charProgression) -> ProgressionType.CHAR_PROGRESSION
@@ -10,12 +10,9 @@ import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irImplicitCast
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrLoop
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
@@ -34,33 +31,26 @@ internal sealed class ForLoopHeader(
val bound: IrVariable,
val last: IrVariable,
val step: IrVariable,
val progressionType: ProgressionType
val progressionType: ProgressionType,
val needsEmptinessCheck: Boolean
) {
abstract fun initializeLoopVariable(symbols: Symbols<CommonBackendContext>, builder: DeclarationIrBuilder): IrExpression
abstract val declarations: List<IrStatement>
abstract fun buildBody(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop
abstract fun buildInnerLoop(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop
abstract fun buildNotEmptyCondition(builder: DeclarationIrBuilder): IrExpression?
}
internal class ProgressionLoopHeader(
headerInfo: ProgressionHeaderInfo,
private val headerInfo: ProgressionHeaderInfo,
inductionVariable: IrVariable,
bound: IrVariable,
last: IrVariable,
step: IrVariable,
var loopVariable: IrVariable? = null
) : ForLoopHeader(inductionVariable, bound, last, step, headerInfo.progressionType) {
val closed = headerInfo.closed
private val direction = headerInfo.direction
// TODO: Handle UNKNOWN direction, which requires a complex expression for the emptiness check
fun comparingFunction(builtIns: IrBuiltIns) = if (direction == ProgressionDirection.INCREASING)
builtIns.lessOrEqualFunByOperandType[builtIns.int]?.symbol!!
else
builtIns.greaterOrEqualFunByOperandType[builtIns.int]?.symbol!!
) : ForLoopHeader(inductionVariable, bound, last, step, headerInfo.progressionType, needsEmptinessCheck = true) {
override fun initializeLoopVariable(symbols: Symbols<CommonBackendContext>, builder: DeclarationIrBuilder) = with(builder) {
irGet(inductionVariable)
@@ -69,7 +59,7 @@ internal class ProgressionLoopHeader(
override val declarations: List<IrStatement>
get() = listOf(inductionVariable, step, bound, last)
override fun buildBody(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) {
override fun buildInnerLoop(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) {
assert(loopVariable != null)
val newCondition = irCall(context.irBuiltIns.booleanNotSymbol).apply {
putValueArgument(0, irCall(context.irBuiltIns.eqeqSymbol).apply {
@@ -83,31 +73,78 @@ internal class ProgressionLoopHeader(
body = newBody
}
}
override fun buildNotEmptyCondition(builder: DeclarationIrBuilder): IrExpression? =
with(builder) {
val builtIns = context.irBuiltIns
val progressionKotlinType = progressionType.elementType(builtIns).toKotlinType()
val lessOrEqualFun = builtIns.lessOrEqualFunByOperandType[progressionKotlinType]!!
// TODO: Additional condition for `for (i in a until MIN_VALUE)` corner case
when (headerInfo.direction) {
ProgressionDirection.DECREASING ->
// last <= inductionVariable
irCall(lessOrEqualFun).apply {
putValueArgument(0, irGet(last))
putValueArgument(1, irGet(inductionVariable))
}
ProgressionDirection.INCREASING ->
// inductionVariable <= last
irCall(lessOrEqualFun).apply {
putValueArgument(0, irGet(inductionVariable))
putValueArgument(1, irGet(last))
}
ProgressionDirection.UNKNOWN -> {
// If the direction is unknown, we check depending on the "step" value:
// (step > 0 && inductionVariable <= last) || (step < 0 || last <= inductionVariable)
val stepKotlinType = progressionType.stepType(builtIns).toKotlinType()
val zero = if (progressionType == ProgressionType.LONG_PROGRESSION) irLong(0) else irInt(0)
context.oror(
context.andand(
irCall(builtIns.greaterFunByOperandType[stepKotlinType]!!).apply {
putValueArgument(0, irGet(step))
putValueArgument(1, zero)
},
irCall(lessOrEqualFun).apply {
putValueArgument(0, irGet(inductionVariable))
putValueArgument(1, irGet(last))
}),
context.andand(
irCall(builtIns.lessFunByOperandType[stepKotlinType]!!).apply {
putValueArgument(0, irGet(step))
putValueArgument(1, zero)
},
irCall(lessOrEqualFun).apply {
putValueArgument(0, irGet(last))
putValueArgument(1, irGet(inductionVariable))
})
)
}
}
}
}
internal class ArrayLoopHeader(
headerInfo: ArrayHeaderInfo,
private val headerInfo: ArrayHeaderInfo,
inductionVariable: IrVariable,
bound: IrVariable,
last: IrVariable,
step: IrVariable
) : ForLoopHeader(inductionVariable, bound, last, step, ProgressionType.INT_PROGRESSION) {
private val arrayDeclaration = headerInfo.arrayVariable
) : ForLoopHeader(inductionVariable, bound, last, step, ProgressionType.INT_PROGRESSION, needsEmptinessCheck = false) {
override fun initializeLoopVariable(symbols: Symbols<CommonBackendContext>, builder: DeclarationIrBuilder) = with(builder) {
val arrayClass = (arrayDeclaration.type.classifierOrNull) as IrClassSymbol
val arrayClass = (headerInfo.arrayVariable.type.classifierOrNull) as IrClassSymbol
val arrayGetFun = arrayClass.owner.functions.find { it.name.toString() == "get" }!!
irCall(arrayGetFun).apply {
dispatchReceiver = irGet(arrayDeclaration)
dispatchReceiver = irGet(headerInfo.arrayVariable)
putValueArgument(0, irGet(inductionVariable))
}
}
override val declarations: List<IrStatement>
get() = listOf(arrayDeclaration, inductionVariable, step, bound, last)
get() = listOf(headerInfo.arrayVariable, inductionVariable, step, bound, last)
override fun buildBody(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) {
override fun buildInnerLoop(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) {
val builtIns = context.irBuiltIns
val callee = builtIns.lessOrEqualFunByOperandType[builtIns.int]?.symbol!!
val newCondition = irCall(callee).apply {
@@ -120,6 +157,9 @@ internal class ArrayLoopHeader(
body = newBody
}
}
// No surrounding emptiness check is needed with a while loop.
override fun buildNotEmptyCondition(builder: DeclarationIrBuilder): IrExpression? = null
}
// Given the for loop iterator variable, extract information about iterable subject
@@ -279,6 +279,9 @@ fun IrBuilderWithScope.irImplicitCast(argument: IrExpression, type: IrType) =
fun IrBuilderWithScope.irInt(value: Int) =
IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, value)
fun IrBuilderWithScope.irLong(value: Long) =
IrConstImpl.long(startOffset, endOffset, context.irBuiltIns.longType, value)
fun IrBuilderWithScope.irString(value: String) =
IrConstImpl.string(startOffset, endOffset, context.irBuiltIns.stringType, value)
@@ -181,7 +181,7 @@ class IrBuiltIns(
// TODO switch to IrType
val primitiveTypes = listOf(bool, char, byte, short, int, long, float, double)
val primitiveTypesWithComparisons = listOf(int, long, float, double)
val primitiveTypesWithComparisons = listOf(char, byte, short, int, long, float, double)
val primitiveFloatingPointTypes = listOf(float, double)
val lessFunByOperandType = primitiveTypesWithComparisons.defineComparisonOperatorForEachType(OperatorNames.LESS)
@@ -1,3 +1,5 @@
// TODO: Enable after corner case is addressed
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
import kotlin.test.assertEquals
@@ -1,3 +1,5 @@
// TODO: Enable after corner case is addressed
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
import kotlin.test.assertEquals
@@ -1,3 +1,5 @@
// TODO: Enable after corner case is addressed
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
import kotlin.test.assertEquals
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
const val N = 42L
fun test(): Long {