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:
committed by
max-kammerer
parent
a2e3d3635e
commit
7b153b2b68
+10
-65
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -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
|
||||
|
||||
+67
-27
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user