ForLoopsLowering: Convert ProgressionType from enum to sealed class and
move more logic into the class. This refactoring simplifies their usage with fewer `when` switches and passing around of Symbols.
This commit is contained in:
committed by
Alexander Udalov
parent
177967258b
commit
b85da8411d
+9
-175
@@ -10,163 +10,13 @@ import org.jetbrains.kotlin.backend.common.ir.Symbols
|
|||||||
import org.jetbrains.kotlin.backend.common.lower.matchers.IrCallMatcher
|
import org.jetbrains.kotlin.backend.common.lower.matchers.IrCallMatcher
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.defaultType
|
||||||
import org.jetbrains.kotlin.ir.util.functions
|
|
||||||
import org.jetbrains.kotlin.ir.util.isUnsigned
|
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||||
|
|
||||||
/** Represents a progression type in the Kotlin stdlib. */
|
|
||||||
internal enum class ProgressionType(
|
|
||||||
private val elementCastFunctionName: Name,
|
|
||||||
private val stepCastFunctionName: Name,
|
|
||||||
val isLong: Boolean = false,
|
|
||||||
val isUnsigned: Boolean = false
|
|
||||||
) {
|
|
||||||
INT_PROGRESSION(Name.identifier("toInt"), Name.identifier("toInt")),
|
|
||||||
LONG_PROGRESSION(Name.identifier("toLong"), Name.identifier("toLong"), isLong = true),
|
|
||||||
CHAR_PROGRESSION(Name.identifier("toChar"), Name.identifier("toInt")),
|
|
||||||
UINT_PROGRESSION(Name.identifier("toInt"), Name.identifier("toInt"), isUnsigned = true),
|
|
||||||
ULONG_PROGRESSION(Name.identifier("toLong"), Name.identifier("toLong"), isLong = true, isUnsigned = true);
|
|
||||||
|
|
||||||
/** Returns the [IrType] of the `first`/`last` properties and elements in the progression. */
|
|
||||||
fun elementType(symbols: Symbols<CommonBackendContext>): IrType = when (this) {
|
|
||||||
INT_PROGRESSION, UINT_PROGRESSION -> symbols.int
|
|
||||||
LONG_PROGRESSION, ULONG_PROGRESSION -> symbols.long
|
|
||||||
CHAR_PROGRESSION -> symbols.char
|
|
||||||
}.defaultType
|
|
||||||
|
|
||||||
/** Returns the [IrClassSymbol] of the `first`/`last` properties and elements in the progression. */
|
|
||||||
fun elementClassifier(symbols: Symbols<CommonBackendContext>): IrClassSymbol = when (this) {
|
|
||||||
INT_PROGRESSION -> symbols.int
|
|
||||||
LONG_PROGRESSION -> symbols.long
|
|
||||||
CHAR_PROGRESSION -> symbols.char
|
|
||||||
UINT_PROGRESSION -> symbols.uInt!!
|
|
||||||
ULONG_PROGRESSION -> symbols.uLong!!
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the [IrType] of the `step` property in the progression. */
|
|
||||||
fun stepType(builtIns: IrBuiltIns): IrType = when (this) {
|
|
||||||
INT_PROGRESSION, CHAR_PROGRESSION, UINT_PROGRESSION -> builtIns.intType
|
|
||||||
LONG_PROGRESSION, ULONG_PROGRESSION -> builtIns.longType
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the [IrClassSymbol] of the `step` property type constructor in the progression. */
|
|
||||||
fun stepClassifier(builtIns: IrBuiltIns): IrClassSymbol = when(this) {
|
|
||||||
INT_PROGRESSION, CHAR_PROGRESSION , UINT_PROGRESSION-> builtIns.intClass
|
|
||||||
LONG_PROGRESSION, ULONG_PROGRESSION -> builtIns.longClass
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the [IrType] used in loop conditions (`buildLoopCondition()`) and when calling `getProgressionLastElement()`. */
|
|
||||||
fun compareType(symbols: Symbols<CommonBackendContext>): IrType = when (this) {
|
|
||||||
INT_PROGRESSION -> symbols.int
|
|
||||||
LONG_PROGRESSION -> symbols.long
|
|
||||||
CHAR_PROGRESSION -> symbols.char
|
|
||||||
UINT_PROGRESSION -> symbols.uInt!!
|
|
||||||
ULONG_PROGRESSION -> symbols.uLong!!
|
|
||||||
}.defaultType
|
|
||||||
|
|
||||||
fun castElementIfNecessary(element: IrExpression, context: CommonBackendContext) =
|
|
||||||
element.castIfNecessary(elementType(context.ir.symbols), elementCastFunctionName)
|
|
||||||
|
|
||||||
fun castStepIfNecessary(step: IrExpression, context: CommonBackendContext) =
|
|
||||||
step.castIfNecessary(stepType(context.irBuiltIns), stepCastFunctionName)
|
|
||||||
|
|
||||||
private fun IrExpression.castIfNecessary(targetType: IrType, numberCastFunctionName: Name) =
|
|
||||||
// This expression's type could be Nothing from an exception throw.
|
|
||||||
if (type == targetType || type.isNothing()) {
|
|
||||||
this
|
|
||||||
} else {
|
|
||||||
val castFun = type.getClass()!!.functions.single {
|
|
||||||
it.name == numberCastFunctionName &&
|
|
||||||
it.dispatchReceiverParameter != null && it.extensionReceiverParameter == null && it.valueParameters.isEmpty()
|
|
||||||
}
|
|
||||||
IrCallImpl(startOffset, endOffset, castFun.returnType, castFun.symbol)
|
|
||||||
.apply { dispatchReceiver = this@castIfNecessary }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun coerceToUnsigned(value: IrExpression, symbols: Symbols<CommonBackendContext>): IrExpression {
|
|
||||||
if (!isUnsigned || value.type.isUnsigned()) return value
|
|
||||||
|
|
||||||
val unsafeCoerceIntrinsic = symbols.unsafeCoerceIntrinsic
|
|
||||||
return if (unsafeCoerceIntrinsic != null) {
|
|
||||||
val from = when (this) {
|
|
||||||
UINT_PROGRESSION -> symbols.int.defaultType
|
|
||||||
ULONG_PROGRESSION -> symbols.long.defaultType
|
|
||||||
else -> error("Unexpected progression type")
|
|
||||||
}
|
|
||||||
val to = when (this) {
|
|
||||||
UINT_PROGRESSION -> symbols.uInt!!.defaultType
|
|
||||||
ULONG_PROGRESSION -> symbols.uLong!!.defaultType
|
|
||||||
else -> error("Unexpected progression type")
|
|
||||||
}
|
|
||||||
IrCallImpl(value.startOffset, value.endOffset, to, unsafeCoerceIntrinsic).apply {
|
|
||||||
putTypeArgument(0, from)
|
|
||||||
putTypeArgument(1, to)
|
|
||||||
putValueArgument(0, value)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val conversionFunctionMap = when (this) {
|
|
||||||
UINT_PROGRESSION -> symbols.toUIntByExtensionReceiver
|
|
||||||
ULONG_PROGRESSION -> symbols.toULongByExtensionReceiver
|
|
||||||
else -> error("Unexpected progression type")
|
|
||||||
}
|
|
||||||
val from = when (this) {
|
|
||||||
UINT_PROGRESSION -> symbols.int.defaultType
|
|
||||||
ULONG_PROGRESSION -> symbols.long.defaultType
|
|
||||||
else -> error("Unexpected progression type")
|
|
||||||
}.toKotlinType()
|
|
||||||
val castFun = conversionFunctionMap.getValue(from)
|
|
||||||
IrCallImpl(value.startOffset, value.endOffset, castFun.owner.returnType, castFun).apply {
|
|
||||||
extensionReceiver = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun coerceToSigned(value: IrExpression, symbols: Symbols<CommonBackendContext>): IrExpression {
|
|
||||||
if (!isUnsigned || !value.type.isUnsigned()) return value
|
|
||||||
|
|
||||||
val unsafeCoerceIntrinsic = symbols.unsafeCoerceIntrinsic
|
|
||||||
return if (unsafeCoerceIntrinsic != null) {
|
|
||||||
val from = when (this) {
|
|
||||||
UINT_PROGRESSION -> symbols.uInt!!.defaultType
|
|
||||||
ULONG_PROGRESSION -> symbols.uLong!!.defaultType
|
|
||||||
else -> error("Unexpected progression type")
|
|
||||||
}
|
|
||||||
val to = when (this) {
|
|
||||||
UINT_PROGRESSION -> symbols.int.defaultType
|
|
||||||
ULONG_PROGRESSION -> symbols.long.defaultType
|
|
||||||
else -> error("Unexpected progression type")
|
|
||||||
}
|
|
||||||
IrCallImpl(value.startOffset, value.endOffset, to, unsafeCoerceIntrinsic).apply {
|
|
||||||
putTypeArgument(0, from)
|
|
||||||
putTypeArgument(1, to)
|
|
||||||
putValueArgument(0, value)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
castElementIfNecessary(value, symbols.context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromIrType(irType: IrType, symbols: Symbols<CommonBackendContext>): ProgressionType? = when {
|
|
||||||
irType.isSubtypeOfClass(symbols.charProgression) -> CHAR_PROGRESSION
|
|
||||||
irType.isSubtypeOfClass(symbols.intProgression) -> INT_PROGRESSION
|
|
||||||
irType.isSubtypeOfClass(symbols.longProgression) -> LONG_PROGRESSION
|
|
||||||
symbols.uIntProgression != null && irType.isSubtypeOfClass(symbols.uIntProgression) -> UINT_PROGRESSION
|
|
||||||
symbols.uLongProgression != null && irType.isSubtypeOfClass(symbols.uLongProgression) -> ULONG_PROGRESSION
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum class ProgressionDirection {
|
internal enum class ProgressionDirection {
|
||||||
DECREASING {
|
DECREASING {
|
||||||
override fun asReversed() = INCREASING
|
override fun asReversed() = INCREASING
|
||||||
@@ -252,24 +102,16 @@ internal class ProgressionHeaderInfo(
|
|||||||
// - `0..someLast()` CAN overflow (we don't know the direction)
|
// - `0..someLast()` CAN overflow (we don't know the direction)
|
||||||
// - `someProgression()` CAN overflow (we don't know the direction)
|
// - `someProgression()` CAN overflow (we don't know the direction)
|
||||||
|
|
||||||
if (progressionType.isUnsigned) {
|
if (progressionType is UnsignedProgressionType) {
|
||||||
// "step" is still signed for unsigned progressions.
|
// "step" is still signed for unsigned progressions.
|
||||||
val lastValueAsULong = last.constLongValue?.toULong() ?: return@lazy true // If "last" is not a const Number or Char.
|
val lastValueAsULong = last.constLongValue?.toULong() ?: return@lazy true // If "last" is not a const Number or Char.
|
||||||
when (direction) {
|
when (direction) {
|
||||||
ProgressionDirection.DECREASING -> {
|
ProgressionDirection.DECREASING -> {
|
||||||
val constLimitAsULong = when (progressionType) {
|
val constLimitAsULong = progressionType.minValueAsLong.toULong()
|
||||||
ProgressionType.UINT_PROGRESSION -> UInt.MIN_VALUE.toULong()
|
|
||||||
ProgressionType.ULONG_PROGRESSION -> ULong.MIN_VALUE
|
|
||||||
else -> error("Unexpected progression type")
|
|
||||||
}
|
|
||||||
lastValueAsULong < (constLimitAsULong - stepValueAsLong.toULong())
|
lastValueAsULong < (constLimitAsULong - stepValueAsLong.toULong())
|
||||||
}
|
}
|
||||||
ProgressionDirection.INCREASING -> {
|
ProgressionDirection.INCREASING -> {
|
||||||
val constLimitAsULong = when (progressionType) {
|
val constLimitAsULong = progressionType.maxValueAsLong.toULong()
|
||||||
ProgressionType.UINT_PROGRESSION -> UInt.MAX_VALUE.toULong()
|
|
||||||
ProgressionType.ULONG_PROGRESSION -> ULong.MAX_VALUE
|
|
||||||
else -> error("Unexpected progression type")
|
|
||||||
}
|
|
||||||
lastValueAsULong > (constLimitAsULong - stepValueAsLong.toULong())
|
lastValueAsULong > (constLimitAsULong - stepValueAsLong.toULong())
|
||||||
}
|
}
|
||||||
else -> error("Unexpected progression direction")
|
else -> error("Unexpected progression direction")
|
||||||
@@ -278,21 +120,11 @@ internal class ProgressionHeaderInfo(
|
|||||||
val lastValueAsLong = last.constLongValue ?: return@lazy true // If "last" is not a const Number or Char.
|
val lastValueAsLong = last.constLongValue ?: return@lazy true // If "last" is not a const Number or Char.
|
||||||
when (direction) {
|
when (direction) {
|
||||||
ProgressionDirection.DECREASING -> {
|
ProgressionDirection.DECREASING -> {
|
||||||
val constLimitAsLong = when (progressionType) {
|
val constLimitAsLong = progressionType.minValueAsLong
|
||||||
ProgressionType.INT_PROGRESSION -> Int.MIN_VALUE.toLong()
|
|
||||||
ProgressionType.CHAR_PROGRESSION -> Char.MIN_VALUE.toLong()
|
|
||||||
ProgressionType.LONG_PROGRESSION -> Long.MIN_VALUE
|
|
||||||
else -> error("Unexpected progression type")
|
|
||||||
}
|
|
||||||
lastValueAsLong < (constLimitAsLong - stepValueAsLong)
|
lastValueAsLong < (constLimitAsLong - stepValueAsLong)
|
||||||
}
|
}
|
||||||
ProgressionDirection.INCREASING -> {
|
ProgressionDirection.INCREASING -> {
|
||||||
val constLimitAsLong = when (progressionType) {
|
val constLimitAsLong = progressionType.maxValueAsLong
|
||||||
ProgressionType.INT_PROGRESSION -> Int.MAX_VALUE.toLong()
|
|
||||||
ProgressionType.CHAR_PROGRESSION -> Char.MAX_VALUE.toLong()
|
|
||||||
ProgressionType.LONG_PROGRESSION -> Long.MAX_VALUE
|
|
||||||
else -> error("Unexpected progression type")
|
|
||||||
}
|
|
||||||
lastValueAsLong > (constLimitAsLong - stepValueAsLong)
|
lastValueAsLong > (constLimitAsLong - stepValueAsLong)
|
||||||
}
|
}
|
||||||
else -> error("Unexpected progression direction")
|
else -> error("Unexpected progression direction")
|
||||||
@@ -317,6 +149,7 @@ internal class ProgressionHeaderInfo(
|
|||||||
* The internal induction variable used is an Int.
|
* The internal induction variable used is an Int.
|
||||||
*/
|
*/
|
||||||
internal class IndexedGetHeaderInfo(
|
internal class IndexedGetHeaderInfo(
|
||||||
|
symbols: Symbols<CommonBackendContext>,
|
||||||
first: IrExpression,
|
first: IrExpression,
|
||||||
last: IrExpression,
|
last: IrExpression,
|
||||||
step: IrExpression,
|
step: IrExpression,
|
||||||
@@ -324,7 +157,7 @@ internal class IndexedGetHeaderInfo(
|
|||||||
val objectVariable: IrVariable,
|
val objectVariable: IrVariable,
|
||||||
val expressionHandler: IndexedGetIterationHandler
|
val expressionHandler: IndexedGetIterationHandler
|
||||||
) : NumericHeaderInfo(
|
) : NumericHeaderInfo(
|
||||||
ProgressionType.INT_PROGRESSION,
|
IntProgressionType(symbols),
|
||||||
first,
|
first,
|
||||||
last,
|
last,
|
||||||
step,
|
step,
|
||||||
@@ -435,6 +268,7 @@ internal abstract class HeaderInfoBuilder(context: CommonBackendContext, private
|
|||||||
override fun visitElement(element: IrElement, data: IrCall?): HeaderInfo? = null
|
override fun visitElement(element: IrElement, data: IrCall?): HeaderInfo? = null
|
||||||
|
|
||||||
/** Builds a [HeaderInfo] for iterable expressions that are calls (e.g., `.reversed()`, `.indices`. */
|
/** Builds a [HeaderInfo] for iterable expressions that are calls (e.g., `.reversed()`, `.indices`. */
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
override fun visitCall(iterable: IrCall, iteratorCall: IrCall?): HeaderInfo? {
|
override fun visitCall(iterable: IrCall, iteratorCall: IrCall?): HeaderInfo? {
|
||||||
// Return the HeaderInfo from the first successful match.
|
// Return the HeaderInfo from the first successful match.
|
||||||
// First, try to match a `reversed()` or `withIndex()` call.
|
// First, try to match a `reversed()` or `withIndex()` call.
|
||||||
|
|||||||
+147
-130
@@ -78,51 +78,48 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
|
|||||||
get() = field.deepCopyWithSymbols()
|
get() = field.deepCopyWithSymbols()
|
||||||
|
|
||||||
protected val symbols = context.ir.symbols
|
protected val symbols = context.ir.symbols
|
||||||
private val elementType: IrType
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
with(builder) {
|
with(builder) {
|
||||||
elementType = headerInfo.progressionType.elementType(symbols)
|
with(headerInfo.progressionType) {
|
||||||
|
// 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.createTmpVariable(
|
||||||
|
headerInfo.first.asElementType(),
|
||||||
|
nameHint = "inductionVariable",
|
||||||
|
isMutable = true,
|
||||||
|
irType = elementClass.defaultType
|
||||||
|
)
|
||||||
|
|
||||||
// For this loop:
|
// 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
|
||||||
// for (i in first()..last() step step())
|
// them to non-nullable.
|
||||||
//
|
// TODO: Confirm if casting to non-nullable is still necessary
|
||||||
// We need to cast first(), last(). and step() to conform to the progression type so
|
val last = headerInfo.last.asElementType()
|
||||||
// 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.createTmpVariable(
|
|
||||||
headerInfo.progressionType.castElementIfNecessary(headerInfo.first, context),
|
|
||||||
nameHint = "inductionVariable",
|
|
||||||
isMutable = true
|
|
||||||
)
|
|
||||||
|
|
||||||
// Due to features of PSI2IR we can obtain nullable arguments here while actually
|
lastVariableIfCanCacheLast = if (headerInfo.canCacheLast) {
|
||||||
// they are non-nullable (the frontend takes care about this). So we need to cast
|
scope.createTmpVariable(last, nameHint = "last")
|
||||||
// them to non-nullable.
|
} else null
|
||||||
// TODO: Confirm if casting to non-nullable is still necessary
|
|
||||||
val last = ensureNotNullable(headerInfo.progressionType.castElementIfNecessary(headerInfo.last, context))
|
|
||||||
|
|
||||||
lastVariableIfCanCacheLast = if (headerInfo.canCacheLast) {
|
lastExpression = if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else last
|
||||||
scope.createTmpVariable(
|
|
||||||
last,
|
|
||||||
nameHint = "last"
|
|
||||||
)
|
|
||||||
} else null
|
|
||||||
|
|
||||||
lastExpression = if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else last
|
val (tmpStepVar, tmpStepExpression) =
|
||||||
|
createTemporaryVariableIfNecessary(
|
||||||
val stepType = headerInfo.progressionType.stepType(context.irBuiltIns)
|
ensureNotNullable(headerInfo.step.asStepType()),
|
||||||
val (tmpStepVar, tmpStepExpression) =
|
nameHint = "step",
|
||||||
createTemporaryVariableIfNecessary(
|
irType = stepClass.defaultType
|
||||||
ensureNotNullable(headerInfo.progressionType.castStepIfNecessary(headerInfo.step, context)),
|
)
|
||||||
nameHint = "step",
|
stepVariable = tmpStepVar
|
||||||
irType = stepType
|
stepExpression = tmpStepExpression
|
||||||
)
|
}
|
||||||
stepVariable = tmpStepVar
|
|
||||||
stepExpression = tmpStepExpression
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,112 +132,117 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
|
|||||||
|
|
||||||
/** 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
|
with(headerInfo.progressionType) {
|
||||||
// NOTE: We cannot use `stepExpression.type` to match the value parameter type because it may be of type `Nothing`.
|
// inductionVariable = inductionVariable + step
|
||||||
// This happens in the case of an illegal step where the "step" is actually a `throw IllegalArgumentException(...)`.
|
// NOTE: We cannot use `stepExpression.type` to match the value parameter type because it may be of type `Nothing`.
|
||||||
val stepType = headerInfo.progressionType.stepType(context.irBuiltIns)
|
// This happens in the case of an illegal step where the "step" is actually a `throw IllegalArgumentException(...)`.
|
||||||
val plusFun = elementType.getClass()!!.functions.single {
|
val stepType = stepClass.defaultType
|
||||||
it.name == OperatorNameConventions.PLUS &&
|
val plusFun = elementClass.defaultType.getClass()!!.functions.single {
|
||||||
it.valueParameters.size == 1 &&
|
it.name == OperatorNameConventions.PLUS &&
|
||||||
it.valueParameters[0].type == stepType
|
it.valueParameters.size == 1 &&
|
||||||
}
|
it.valueParameters[0].type == stepType
|
||||||
irSetVar(
|
}
|
||||||
inductionVariable.symbol, irCallOp(
|
irSetVar(
|
||||||
plusFun.symbol, plusFun.returnType,
|
inductionVariable.symbol, irCallOp(
|
||||||
irGet(inductionVariable),
|
plusFun.symbol, plusFun.returnType,
|
||||||
stepExpression
|
irGet(inductionVariable),
|
||||||
|
stepExpression
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun buildLoopCondition(builder: DeclarationIrBuilder): IrExpression =
|
protected fun buildLoopCondition(builder: DeclarationIrBuilder): IrExpression {
|
||||||
with(builder) {
|
with(builder) {
|
||||||
val builtIns = context.irBuiltIns
|
with(headerInfo.progressionType) {
|
||||||
val progressionType = headerInfo.progressionType
|
val builtIns = context.irBuiltIns
|
||||||
val progressionCompareType = progressionType.compareType(symbols)
|
|
||||||
|
|
||||||
// `compareTo` must be used for UInt/ULong; they don't have intrinsic comparison operators.
|
// Bounds are signed for unsigned progressions but bound comparisons should be done as unsigned, to ensure that the
|
||||||
val intCompFun = if (isLastInclusive) {
|
// correct comparison function is used (`UInt/ULongCompare`). Also, `compareTo` must be used for UInt/ULong;
|
||||||
builtIns.lessOrEqualFunByOperandType.getValue(builtIns.intClass)
|
// they don't have intrinsic comparison operators.
|
||||||
} else {
|
val intCompFun = if (isLastInclusive) {
|
||||||
builtIns.lessFunByOperandType.getValue(builtIns.intClass)
|
builtIns.lessOrEqualFunByOperandType.getValue(builtIns.intClass)
|
||||||
}
|
|
||||||
val elementCompareToFun = progressionCompareType.getClass()!!.functions.single {
|
|
||||||
it.name == OperatorNameConventions.COMPARE_TO &&
|
|
||||||
it.dispatchReceiverParameter != null && it.extensionReceiverParameter == null &&
|
|
||||||
it.valueParameters.size == 1 && it.valueParameters[0].type == progressionCompareType
|
|
||||||
}
|
|
||||||
|
|
||||||
val elementCompFun =
|
|
||||||
if (isLastInclusive) {
|
|
||||||
builtIns.lessOrEqualFunByOperandType[progressionCompareType.classifierOrFail]
|
|
||||||
} else {
|
} else {
|
||||||
builtIns.lessFunByOperandType[progressionCompareType.classifierOrFail]
|
builtIns.lessFunByOperandType.getValue(builtIns.intClass)
|
||||||
}
|
}
|
||||||
|
val unsignedCompareToFun = if (this is UnsignedProgressionType) {
|
||||||
|
unsignedType.getClass()!!.functions.single {
|
||||||
|
it.name == OperatorNameConventions.COMPARE_TO &&
|
||||||
|
it.dispatchReceiverParameter != null && it.extensionReceiverParameter == null &&
|
||||||
|
it.valueParameters.size == 1 && it.valueParameters[0].type == unsignedType
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
|
||||||
fun conditionForDecreasing(): IrExpression =
|
val elementCompFun =
|
||||||
// last <= inductionVar (use `<` if last is exclusive)
|
if (isLastInclusive) {
|
||||||
if (progressionType.isUnsigned) {
|
builtIns.lessOrEqualFunByOperandType[elementClass.symbol]
|
||||||
irCall(intCompFun).apply {
|
} else {
|
||||||
putValueArgument(0, irCall(elementCompareToFun).apply {
|
builtIns.lessFunByOperandType[elementClass.symbol]
|
||||||
dispatchReceiver = progressionType.coerceToUnsigned(lastExpression, symbols)
|
|
||||||
putValueArgument(0, progressionType.coerceToUnsigned(irGet(inductionVariable), symbols))
|
|
||||||
})
|
|
||||||
putValueArgument(1, irInt(0))
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
irCall(elementCompFun!!).apply {
|
|
||||||
putValueArgument(0, lastExpression)
|
|
||||||
putValueArgument(1, irGet(inductionVariable))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun conditionForIncreasing(): IrExpression =
|
fun conditionForDecreasing(): IrExpression =
|
||||||
// inductionVar <= last (use `<` if last is exclusive)
|
// last <= inductionVar (use `<` if last is exclusive)
|
||||||
if (progressionType.isUnsigned) {
|
if (this is UnsignedProgressionType) {
|
||||||
irCall(intCompFun).apply {
|
irCall(intCompFun).apply {
|
||||||
putValueArgument(0, irCall(elementCompareToFun).apply {
|
putValueArgument(0, irCall(unsignedCompareToFun!!).apply {
|
||||||
dispatchReceiver = progressionType.coerceToUnsigned(irGet(inductionVariable), symbols)
|
dispatchReceiver = lastExpression.asUnsigned()
|
||||||
putValueArgument(0, progressionType.coerceToUnsigned(lastExpression, symbols))
|
putValueArgument(0, irGet(inductionVariable).asUnsigned())
|
||||||
})
|
})
|
||||||
putValueArgument(1, irInt(0))
|
putValueArgument(1, irInt(0))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
irCall(elementCompFun!!).apply {
|
||||||
|
putValueArgument(0, lastExpression)
|
||||||
|
putValueArgument(1, irGet(inductionVariable))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
irCall(elementCompFun!!).apply {
|
|
||||||
putValueArgument(0, irGet(inductionVariable))
|
|
||||||
putValueArgument(1, lastExpression)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The default condition depends on the direction.
|
fun conditionForIncreasing(): IrExpression =
|
||||||
when (headerInfo.direction) {
|
// inductionVar <= last (use `<` if last is exclusive)
|
||||||
ProgressionDirection.DECREASING -> conditionForDecreasing()
|
if (this is UnsignedProgressionType) {
|
||||||
ProgressionDirection.INCREASING -> conditionForIncreasing()
|
irCall(intCompFun).apply {
|
||||||
ProgressionDirection.UNKNOWN -> {
|
putValueArgument(0, irCall(unsignedCompareToFun!!).apply {
|
||||||
// If the direction is unknown, we check depending on the "step" value:
|
dispatchReceiver = irGet(inductionVariable).asUnsigned()
|
||||||
// // (use `<` if last is exclusive)
|
putValueArgument(0, lastExpression.asUnsigned())
|
||||||
// (step > 0 && inductionVar <= last) || (step < 0 || last <= inductionVar)
|
})
|
||||||
val stepTypeClassifier = progressionType.stepClassifier(builtIns)
|
putValueArgument(1, irInt(0))
|
||||||
context.oror(
|
}
|
||||||
context.andand(
|
} else {
|
||||||
irCall(builtIns.greaterFunByOperandType.getValue(stepTypeClassifier)).apply {
|
irCall(elementCompFun!!).apply {
|
||||||
putValueArgument(0, stepExpression)
|
putValueArgument(0, irGet(inductionVariable))
|
||||||
putValueArgument(1, if (progressionType.isLong) irLong(0) else irInt(0))
|
putValueArgument(1, lastExpression)
|
||||||
},
|
}
|
||||||
conditionForIncreasing()
|
}
|
||||||
),
|
|
||||||
context.andand(
|
// The default condition depends on the direction.
|
||||||
irCall(builtIns.lessFunByOperandType.getValue(stepTypeClassifier)).apply {
|
return when (headerInfo.direction) {
|
||||||
putValueArgument(0, stepExpression)
|
ProgressionDirection.DECREASING -> conditionForDecreasing()
|
||||||
putValueArgument(1, if (progressionType.isLong) irLong(0) else irInt(0))
|
ProgressionDirection.INCREASING -> conditionForIncreasing()
|
||||||
},
|
ProgressionDirection.UNKNOWN -> {
|
||||||
conditionForDecreasing()
|
// If the direction is unknown, we check depending on the "step" value:
|
||||||
|
// // (use `<` if last is exclusive)
|
||||||
|
// (step > 0 && inductionVar <= last) || (step < 0 || last <= inductionVar)
|
||||||
|
context.oror(
|
||||||
|
context.andand(
|
||||||
|
irCall(builtIns.greaterFunByOperandType.getValue(stepClass.symbol)).apply {
|
||||||
|
putValueArgument(0, stepExpression)
|
||||||
|
putValueArgument(1, zeroStepExpression())
|
||||||
|
},
|
||||||
|
conditionForIncreasing()
|
||||||
|
),
|
||||||
|
context.andand(
|
||||||
|
irCall(builtIns.lessFunByOperandType.getValue(stepClass.symbol)).apply {
|
||||||
|
putValueArgument(0, stepExpression)
|
||||||
|
putValueArgument(1, zeroStepExpression())
|
||||||
|
},
|
||||||
|
conditionForDecreasing()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ProgressionLoopHeader(
|
internal class ProgressionLoopHeader(
|
||||||
@@ -283,7 +285,14 @@ internal class ProgressionLoopHeader(
|
|||||||
isMutable = true
|
isMutable = true
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
loopVariable?.initializer = headerInfo.progressionType.coerceToUnsigned(irGet(inductionVariable), symbols)
|
loopVariable?.initializer = irGet(inductionVariable).let {
|
||||||
|
headerInfo.progressionType.run {
|
||||||
|
if (this is UnsignedProgressionType) {
|
||||||
|
// The induction variable is signed for unsigned progressions but the loop variable should be unsigned.
|
||||||
|
it.asUnsigned()
|
||||||
|
} else it
|
||||||
|
}
|
||||||
|
}
|
||||||
loopVariable
|
loopVariable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,8 +316,16 @@ internal class ProgressionLoopHeader(
|
|||||||
// } while (loopVar != last)
|
// } while (loopVar != last)
|
||||||
// }
|
// }
|
||||||
IrDoWhileLoopImpl(oldLoop.startOffset, oldLoop.endOffset, oldLoop.type, oldLoop.origin).apply {
|
IrDoWhileLoopImpl(oldLoop.startOffset, oldLoop.endOffset, oldLoop.type, oldLoop.origin).apply {
|
||||||
|
val loopVariableExpression = irGet(loopVariable!!).let {
|
||||||
|
headerInfo.progressionType.run {
|
||||||
|
if (this is UnsignedProgressionType) {
|
||||||
|
// The loop variable is signed but bounds are signed for unsigned progressions.
|
||||||
|
it.asSigned()
|
||||||
|
} else it
|
||||||
|
}
|
||||||
|
}
|
||||||
label = oldLoop.label
|
label = oldLoop.label
|
||||||
condition = irNotEquals(headerInfo.progressionType.coerceToSigned(irGet(loopVariable!!), symbols), lastExpression)
|
condition = irNotEquals(loopVariableExpression, lastExpression)
|
||||||
body = newBody
|
body = newBody
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+107
-129
@@ -88,100 +88,94 @@ internal class UntilHandler(private val context: CommonBackendContext, private v
|
|||||||
@ExperimentalUnsignedTypes
|
@ExperimentalUnsignedTypes
|
||||||
override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? =
|
override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? =
|
||||||
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
|
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
|
||||||
// `A until B` is essentially the same as `A .. (B-1)`. However, B could be MIN_VALUE and hence `(B-1)` could underflow.
|
with(data) {
|
||||||
// If B is MIN_VALUE, then `A until B` is an empty range. We handle this special case be adding an additional "not empty"
|
// `A until B` is essentially the same as `A .. (B-1)`. However, B could be MIN_VALUE and hence `(B-1)` could underflow.
|
||||||
// condition in the lowered for-loop. Therefore the following for-loop:
|
// If B is MIN_VALUE, then `A until B` is an empty range. We handle this special case be adding an additional "not empty"
|
||||||
//
|
// condition in the lowered for-loop. Therefore the following for-loop:
|
||||||
// for (i in A until B) { // Loop body }
|
//
|
||||||
//
|
// for (i in A until B) { // Loop body }
|
||||||
// is lowered into:
|
//
|
||||||
//
|
// is lowered into:
|
||||||
// var inductionVar = A
|
//
|
||||||
// val last = B - 1
|
// var inductionVar = A
|
||||||
// if (inductionVar <= last && B != MIN_VALUE) {
|
// val last = B - 1
|
||||||
// // Loop is not empty
|
// if (inductionVar <= last && B != MIN_VALUE) {
|
||||||
// do {
|
// // Loop is not empty
|
||||||
// val i = inductionVar
|
// do {
|
||||||
// inductionVar++
|
// val i = inductionVar
|
||||||
// // Loop body
|
// inductionVar++
|
||||||
// } while (inductionVar <= last)
|
// // Loop body
|
||||||
// }
|
// } while (inductionVar <= last)
|
||||||
//
|
// }
|
||||||
// However, `B` may be an expression with side-effects that should only be evaluated once, and `A` may also have side-effects.
|
//
|
||||||
// They are evaluated once and in the correct order (`A` then `B`), the final lowered form is:
|
// However, `B` may be an expression with side-effects that should only be evaluated once, and `A` may also have
|
||||||
//
|
// side-effects. They are evaluated once and in the correct order (`A` then `B`), the final lowered form is:
|
||||||
// // Additional variables
|
//
|
||||||
// val untilReceiverValue = A
|
// // Additional variables
|
||||||
// val untilArg = B
|
// val untilReceiverValue = A
|
||||||
// // Standard form of loop over progression
|
// val untilArg = B
|
||||||
// var inductionVar = untilReceiverValue
|
// // Standard form of loop over progression
|
||||||
// val last = untilArg - 1
|
// var inductionVar = untilReceiverValue
|
||||||
// if (inductionVar <= last && untilArg != MIN_VALUE) {
|
// val last = untilArg - 1
|
||||||
// // Loop is not empty
|
// if (inductionVar <= last && untilArg != MIN_VALUE) {
|
||||||
// do {
|
// // Loop is not empty
|
||||||
// val i = inductionVar
|
// do {
|
||||||
// inductionVar++
|
// val i = inductionVar
|
||||||
// // Loop body
|
// inductionVar++
|
||||||
// } while (inductionVar <= last)
|
// // Loop body
|
||||||
// }
|
// } while (inductionVar <= last)
|
||||||
val receiverValue = expression.extensionReceiver!!
|
// }
|
||||||
val untilArg = expression.getValueArgument(0)!!
|
val receiverValue = expression.extensionReceiver!!
|
||||||
|
val untilArg = expression.getValueArgument(0)!!
|
||||||
|
|
||||||
// Ensure that the argument conforms to the progression type before we decrement.
|
// Ensure that the argument conforms to the progression type before we decrement.
|
||||||
val untilArgCasted = data.castElementIfNecessary(untilArg, this@UntilHandler.context)
|
val untilArgCasted = untilArg.asElementType()
|
||||||
|
|
||||||
// To reduce local variable usage, we create and use temporary variables only if necessary.
|
// To reduce local variable usage, we create and use temporary variables only if necessary.
|
||||||
var receiverValueVar: IrVariable? = null
|
var receiverValueVar: IrVariable? = null
|
||||||
var untilArgVar: IrVariable? = null
|
var untilArgVar: IrVariable? = null
|
||||||
var additionalVariables = emptyList<IrVariable>()
|
var additionalVariables = emptyList<IrVariable>()
|
||||||
if (untilArg.canHaveSideEffects) {
|
if (untilArg.canHaveSideEffects) {
|
||||||
if (receiverValue.canHaveSideEffects) {
|
if (receiverValue.canHaveSideEffects) {
|
||||||
receiverValueVar = scope.createTmpVariable(receiverValue, nameHint = "untilReceiverValue")
|
receiverValueVar = scope.createTmpVariable(receiverValue, nameHint = "untilReceiverValue")
|
||||||
|
}
|
||||||
|
untilArgVar = scope.createTmpVariable(untilArgCasted, nameHint = "untilArg")
|
||||||
|
additionalVariables = listOfNotNull(receiverValueVar, untilArgVar)
|
||||||
}
|
}
|
||||||
untilArgVar = scope.createTmpVariable(untilArgCasted, nameHint = "untilArg")
|
|
||||||
additionalVariables = listOfNotNull(receiverValueVar, untilArgVar)
|
val first = if (receiverValueVar == null) receiverValue else irGet(receiverValueVar)
|
||||||
|
val untilArgExpression = if (untilArgVar == null) untilArgCasted else irGet(untilArgVar)
|
||||||
|
val last = untilArgExpression.decrement()
|
||||||
|
|
||||||
|
// Type of MIN_VALUE constant is signed even for unsigned progressions since the bounds are signed.
|
||||||
|
val additionalNotEmptyCondition = untilArg.constLongValue.let {
|
||||||
|
when {
|
||||||
|
it == null && isAdditionalNotEmptyConditionNeeded(receiverValue.type, untilArg.type) ->
|
||||||
|
// Condition is needed and untilArg is non-const.
|
||||||
|
// Build the additional "not empty" condition: `untilArg != MIN_VALUE`.
|
||||||
|
// Make sure to copy untilArgExpression as it is also used in `last`.
|
||||||
|
irNotEquals(untilArgExpression.deepCopyWithSymbols(), minValueExpression())
|
||||||
|
it == data.minValueAsLong ->
|
||||||
|
// Hardcode "false" as additional condition so that the progression is considered empty.
|
||||||
|
// The entire lowered loop becomes a candidate for dead code elimination, depending on backend.
|
||||||
|
irFalse()
|
||||||
|
else ->
|
||||||
|
// We know that untilArg != MIN_VALUE, so the additional condition is not necessary.
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressionHeaderInfo(
|
||||||
|
data,
|
||||||
|
first = first,
|
||||||
|
last = last,
|
||||||
|
step = irInt(1),
|
||||||
|
canOverflow = false,
|
||||||
|
additionalVariables = additionalVariables,
|
||||||
|
additionalNotEmptyCondition = additionalNotEmptyCondition,
|
||||||
|
direction = ProgressionDirection.INCREASING
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val first = if (receiverValueVar == null) receiverValue else irGet(receiverValueVar)
|
|
||||||
val untilArgExpression = if (untilArgVar == null) untilArgCasted else irGet(untilArgVar)
|
|
||||||
val last = untilArgExpression.decrement()
|
|
||||||
|
|
||||||
// Type of MIN_VALUE constant is signed even for unsigned progressions since the bounds are signed.
|
|
||||||
val (minValueAsLong, minValueIrConst) =
|
|
||||||
when (data) {
|
|
||||||
ProgressionType.INT_PROGRESSION -> Pair(Int.MIN_VALUE.toLong(), irInt(Int.MIN_VALUE))
|
|
||||||
ProgressionType.CHAR_PROGRESSION -> Pair(Char.MIN_VALUE.toLong(), irChar(Char.MIN_VALUE))
|
|
||||||
ProgressionType.LONG_PROGRESSION -> Pair(Long.MIN_VALUE, irLong(Long.MIN_VALUE))
|
|
||||||
ProgressionType.UINT_PROGRESSION -> Pair(UInt.MIN_VALUE.toLong(), irInt(UInt.MIN_VALUE.toInt()))
|
|
||||||
ProgressionType.ULONG_PROGRESSION -> Pair(ULong.MIN_VALUE.toLong(), irLong(ULong.MIN_VALUE.toLong()))
|
|
||||||
}
|
|
||||||
val additionalNotEmptyCondition = untilArg.constLongValue.let {
|
|
||||||
when {
|
|
||||||
it == null && isAdditionalNotEmptyConditionNeeded(receiverValue.type, untilArg.type) ->
|
|
||||||
// Condition is needed and untilArg is non-const.
|
|
||||||
// Build the additional "not empty" condition: `untilArg != MIN_VALUE`.
|
|
||||||
// Make sure to copy untilArgExpression as it is also used in `last`.
|
|
||||||
irNotEquals(untilArgExpression.deepCopyWithSymbols(), minValueIrConst)
|
|
||||||
it == minValueAsLong ->
|
|
||||||
// Hardcode "false" as additional condition so that the progression is considered empty.
|
|
||||||
// The entire lowered loop becomes a candidate for dead code elimination, depending on backend.
|
|
||||||
irFalse()
|
|
||||||
else ->
|
|
||||||
// We know that untilArg != MIN_VALUE, so the additional condition is not necessary.
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProgressionHeaderInfo(
|
|
||||||
data,
|
|
||||||
first = first,
|
|
||||||
last = last,
|
|
||||||
step = irInt(1),
|
|
||||||
canOverflow = false,
|
|
||||||
additionalVariables = additionalVariables,
|
|
||||||
additionalNotEmptyCondition = additionalNotEmptyCondition,
|
|
||||||
direction = ProgressionDirection.INCREASING
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isAdditionalNotEmptyConditionNeeded(receiverType: IrType, argType: IrType): Boolean {
|
private fun isAdditionalNotEmptyConditionNeeded(receiverType: IrType, argType: IrType): Boolean {
|
||||||
@@ -270,9 +264,9 @@ internal class StepHandler(
|
|||||||
// if (step > 0) step else throw IllegalArgumentException("Step must be positive, was: $step.")
|
// if (step > 0) step else throw IllegalArgumentException("Step must be positive, was: $step.")
|
||||||
//
|
//
|
||||||
// We insert this check in the lowered form only if necessary.
|
// We insert this check in the lowered form only if necessary.
|
||||||
val stepType = data.stepType(context.irBuiltIns)
|
val stepType = data.stepClass.defaultType
|
||||||
val stepGreaterFun = context.irBuiltIns.greaterFunByOperandType[data.stepClassifier(context.irBuiltIns)]!!
|
val stepGreaterFun = context.irBuiltIns.greaterFunByOperandType.getValue(data.stepClass.symbol)
|
||||||
val zeroStep = if (data.isLong) irLong(0) else irInt(0)
|
val zeroStep = data.run { zeroStepExpression() }
|
||||||
val throwIllegalStepExceptionCall = {
|
val throwIllegalStepExceptionCall = {
|
||||||
irCall(context.irBuiltIns.illegalArgumentExceptionSymbol).apply {
|
irCall(context.irBuiltIns.illegalArgumentExceptionSymbol).apply {
|
||||||
val exceptionMessage = irConcat()
|
val exceptionMessage = irConcat()
|
||||||
@@ -477,44 +471,25 @@ internal class StepHandler(
|
|||||||
// - getProgressionLastElement(Long, Long, Long): Long // Used by LongProgression
|
// - getProgressionLastElement(Long, Long, Long): Long // Used by LongProgression
|
||||||
// - getProgressionLastElement(UInt, UInt, Int): UInt // Used by UIntProgression (uses Int step)
|
// - getProgressionLastElement(UInt, UInt, Int): UInt // Used by UIntProgression (uses Int step)
|
||||||
// - getProgressionLastElement(ULong, ULong, Long): ULong // Used by ULongProgression (uses Long step)
|
// - getProgressionLastElement(ULong, ULong, Long): ULong // Used by ULongProgression (uses Long step)
|
||||||
//
|
with(progressionType) {
|
||||||
// We make sure to retrieve the correct symbol and use the correct argument types.
|
val getProgressionLastElementFun = getProgressionLastElementFunction
|
||||||
return with(progressionType) {
|
?: error("No `getProgressionLastElement` for progression type ${progressionType::class.simpleName}")
|
||||||
val returnTypeClassifier = if (progressionType.isUnsigned) {
|
return if (this is UnsignedProgressionType) {
|
||||||
progressionType.elementClassifier(symbols)
|
// Bounds are signed for unsigned progressions but `getProgressionLastElement` expects unsigned.
|
||||||
|
// The return value is finally converted back to signed since it will be assigned back to `last`.
|
||||||
|
irCall(getProgressionLastElementFun).apply {
|
||||||
|
putValueArgument(0, first.deepCopyWithSymbols().asElementType().asUnsigned())
|
||||||
|
putValueArgument(1, last.deepCopyWithSymbols().asElementType().asUnsigned())
|
||||||
|
putValueArgument(2, step.deepCopyWithSymbols().asStepType())
|
||||||
|
}.asSigned()
|
||||||
} else {
|
} else {
|
||||||
progressionType.stepClassifier(context.irBuiltIns)
|
irCall(getProgressionLastElementFun).apply {
|
||||||
}
|
// Step type is used for casting because it works for all signed progressions. In particular,
|
||||||
val getProgressionLastElementFun = symbols.getProgressionLastElementByReturnType[returnTypeClassifier]
|
// getProgressionLastElement(Int, Int, Int) is called for CharProgression, which uses an Int step.
|
||||||
?: throw IllegalArgumentException("No `getProgressionLastElement` for return type ${returnTypeClassifier.defaultType}")
|
putValueArgument(0, first.deepCopyWithSymbols().asStepType())
|
||||||
val call = irCall(getProgressionLastElementFun).apply {
|
putValueArgument(1, last.deepCopyWithSymbols().asStepType())
|
||||||
if (isUnsigned) {
|
putValueArgument(2, step.deepCopyWithSymbols().asStepType())
|
||||||
putValueArgument(
|
|
||||||
0,
|
|
||||||
coerceToUnsigned(
|
|
||||||
castElementIfNecessary(first.deepCopyWithSymbols(), this@StepHandler.context),
|
|
||||||
symbols
|
|
||||||
)
|
|
||||||
)
|
|
||||||
putValueArgument(
|
|
||||||
1,
|
|
||||||
coerceToUnsigned(
|
|
||||||
castElementIfNecessary(last.deepCopyWithSymbols(), this@StepHandler.context),
|
|
||||||
symbols
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
putValueArgument(0, castStepIfNecessary(first.deepCopyWithSymbols(), this@StepHandler.context))
|
|
||||||
putValueArgument(1, castStepIfNecessary(last.deepCopyWithSymbols(), this@StepHandler.context))
|
|
||||||
}
|
}
|
||||||
putValueArgument(2, castStepIfNecessary(step.deepCopyWithSymbols(), this@StepHandler.context))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isUnsigned) {
|
|
||||||
// Bounds are signed for unsigned progressions.
|
|
||||||
coerceToSigned(call, symbols)
|
|
||||||
} else {
|
|
||||||
call
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -608,8 +583,10 @@ internal class DefaultProgressionHandler(private val context: CommonBackendConte
|
|||||||
|
|
||||||
private val symbols = context.ir.symbols
|
private val symbols = context.ir.symbols
|
||||||
|
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
override fun matchIterable(expression: IrExpression) = ProgressionType.fromIrType(expression.type, symbols) != null
|
override fun matchIterable(expression: IrExpression) = ProgressionType.fromIrType(expression.type, symbols) != null
|
||||||
|
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
override fun build(expression: IrExpression, scopeOwner: IrSymbol): HeaderInfo? =
|
override fun build(expression: IrExpression, scopeOwner: IrSymbol): HeaderInfo? =
|
||||||
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
|
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
|
||||||
// Directly use the `first/last/step` properties of the progression.
|
// Directly use the `first/last/step` properties of the progression.
|
||||||
@@ -666,6 +643,7 @@ internal abstract class IndexedGetIterationHandler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
IndexedGetHeaderInfo(
|
IndexedGetHeaderInfo(
|
||||||
|
this@IndexedGetIterationHandler.context.ir.symbols,
|
||||||
first = irInt(0),
|
first = irInt(0),
|
||||||
last = last,
|
last = last,
|
||||||
step = irInt(1),
|
step = irInt(1),
|
||||||
|
|||||||
+205
@@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.backend.common.lower.loops
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||||
|
import org.jetbrains.kotlin.backend.common.ir.Symbols
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
|
||||||
|
import org.jetbrains.kotlin.ir.builders.irChar
|
||||||
|
import org.jetbrains.kotlin.ir.builders.irInt
|
||||||
|
import org.jetbrains.kotlin.ir.builders.irLong
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.defaultType
|
||||||
|
import org.jetbrains.kotlin.ir.util.functions
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
|
/** Represents a progression type in the Kotlin stdlib. */
|
||||||
|
internal sealed class ProgressionType(
|
||||||
|
val elementClass: IrClass,
|
||||||
|
val stepClass: IrClass,
|
||||||
|
val minValueAsLong: Long,
|
||||||
|
val maxValueAsLong: Long,
|
||||||
|
val getProgressionLastElementFunction: IrSimpleFunctionSymbol?
|
||||||
|
) {
|
||||||
|
abstract fun DeclarationIrBuilder.minValueExpression(): IrExpression
|
||||||
|
|
||||||
|
abstract fun DeclarationIrBuilder.zeroStepExpression(): IrExpression
|
||||||
|
|
||||||
|
fun IrExpression.asElementType() = castIfNecessary(elementClass)
|
||||||
|
|
||||||
|
fun IrExpression.asStepType() = castIfNecessary(stepClass)
|
||||||
|
|
||||||
|
private fun IrExpression.castIfNecessary(targetClass: IrClass) =
|
||||||
|
// This expression's type could be Nothing from an exception throw.
|
||||||
|
if (type == targetClass.defaultType || type.isNothing()) {
|
||||||
|
this
|
||||||
|
} else {
|
||||||
|
val numberCastFunctionName = Name.identifier("to${targetClass.name.asString()}")
|
||||||
|
val castFun = type.getClass()!!.functions.single {
|
||||||
|
it.name == numberCastFunctionName &&
|
||||||
|
it.dispatchReceiverParameter != null && it.extensionReceiverParameter == null && it.valueParameters.isEmpty()
|
||||||
|
}
|
||||||
|
IrCallImpl(startOffset, endOffset, castFun.returnType, castFun.symbol)
|
||||||
|
.apply { dispatchReceiver = this@castIfNecessary }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
|
fun fromIrType(irType: IrType, symbols: Symbols<CommonBackendContext>): ProgressionType? = when {
|
||||||
|
irType.isSubtypeOfClass(symbols.charProgression) -> CharProgressionType(symbols)
|
||||||
|
irType.isSubtypeOfClass(symbols.intProgression) -> IntProgressionType(symbols)
|
||||||
|
irType.isSubtypeOfClass(symbols.longProgression) -> LongProgressionType(symbols)
|
||||||
|
symbols.uIntProgression != null && irType.isSubtypeOfClass(symbols.uIntProgression) -> UIntProgressionType(symbols)
|
||||||
|
symbols.uLongProgression != null && irType.isSubtypeOfClass(symbols.uLongProgression) -> ULongProgressionType(symbols)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class IntProgressionType(symbols: Symbols<CommonBackendContext>) :
|
||||||
|
ProgressionType(
|
||||||
|
elementClass = symbols.int.owner,
|
||||||
|
stepClass = symbols.int.owner,
|
||||||
|
minValueAsLong = Int.MIN_VALUE.toLong(),
|
||||||
|
maxValueAsLong = Int.MAX_VALUE.toLong(),
|
||||||
|
// Uses `getProgressionLastElement(Int, Int, Int): Int`
|
||||||
|
getProgressionLastElementFunction = symbols.getProgressionLastElementByReturnType[symbols.int]
|
||||||
|
) {
|
||||||
|
override fun DeclarationIrBuilder.minValueExpression() = irInt(Int.MIN_VALUE)
|
||||||
|
|
||||||
|
override fun DeclarationIrBuilder.zeroStepExpression() = irInt(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class LongProgressionType(symbols: Symbols<CommonBackendContext>) :
|
||||||
|
ProgressionType(
|
||||||
|
elementClass = symbols.long.owner,
|
||||||
|
stepClass = symbols.long.owner,
|
||||||
|
minValueAsLong = Long.MIN_VALUE,
|
||||||
|
maxValueAsLong = Long.MAX_VALUE,
|
||||||
|
// Uses `getProgressionLastElement(Long, Long, Long): Long`
|
||||||
|
getProgressionLastElementFunction = symbols.getProgressionLastElementByReturnType[symbols.long]
|
||||||
|
) {
|
||||||
|
override fun DeclarationIrBuilder.minValueExpression() = irLong(Long.MIN_VALUE)
|
||||||
|
|
||||||
|
override fun DeclarationIrBuilder.zeroStepExpression() = irLong(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CharProgressionType(symbols: Symbols<CommonBackendContext>) :
|
||||||
|
ProgressionType(
|
||||||
|
elementClass = symbols.char.owner,
|
||||||
|
stepClass = symbols.int.owner,
|
||||||
|
minValueAsLong = Char.MIN_VALUE.toLong(),
|
||||||
|
maxValueAsLong = Char.MAX_VALUE.toLong(),
|
||||||
|
// Uses `getProgressionLastElement(Int, Int, Int): Int`
|
||||||
|
getProgressionLastElementFunction = symbols.getProgressionLastElementByReturnType[symbols.int]
|
||||||
|
) {
|
||||||
|
override fun DeclarationIrBuilder.minValueExpression() = irChar(Char.MIN_VALUE)
|
||||||
|
|
||||||
|
override fun DeclarationIrBuilder.zeroStepExpression() = irInt(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A note on how ForLoopsLowering handles unsigned progressions:
|
||||||
|
//
|
||||||
|
// We use signed numbers for the elements (induction variable and bounds) to limit calls to UInt/ULong constructors. For example,
|
||||||
|
// `inductionVar += step.toUInt()` would cause instantiation during `toUInt()` (conversion necessary since `step` is signed) and on
|
||||||
|
// assignment to `inductionVar`. There are a few places where we _have_ to convert either the induction variable or bounds to unsigned:
|
||||||
|
//
|
||||||
|
// 1. When comparing in the loop conditions (e.g., `inductionVar <= last`). This ensures that the correct comparison function is used
|
||||||
|
// (`UInt/ULongCompare`) instead of regular Int/Long comparisons.
|
||||||
|
// 2. When assigning to the loop variable, which should have an unsigned type.
|
||||||
|
// 3. When calling `getProgressionLastElement` for stepped progressions. There are overloads which take unsigned numbers, which should
|
||||||
|
// be used to ensure the calculation is correct.
|
||||||
|
//
|
||||||
|
// We use the `<unsafe-coerce>` intrinsic if available (currently JVM-only) to perform the conversions, and fallback to calling
|
||||||
|
// `to(U)Int/(U)Long()` functions otherwise.
|
||||||
|
|
||||||
|
internal abstract class UnsignedProgressionType(
|
||||||
|
symbols: Symbols<CommonBackendContext>,
|
||||||
|
elementClass: IrClass,
|
||||||
|
stepClass: IrClass,
|
||||||
|
minValueAsLong: Long,
|
||||||
|
maxValueAsLong: Long,
|
||||||
|
getProgressionLastElementFunction: IrSimpleFunctionSymbol?,
|
||||||
|
val unsignedType: IrType,
|
||||||
|
private val unsignedConversionFunction: IrSimpleFunctionSymbol
|
||||||
|
) : ProgressionType(elementClass, stepClass, minValueAsLong, maxValueAsLong, getProgressionLastElementFunction) {
|
||||||
|
|
||||||
|
private val unsafeCoerceIntrinsic = symbols.unsafeCoerceIntrinsic
|
||||||
|
|
||||||
|
fun IrExpression.asUnsigned(): IrExpression {
|
||||||
|
val fromType = type
|
||||||
|
if (type == unsignedType) return this
|
||||||
|
|
||||||
|
return if (unsafeCoerceIntrinsic != null) {
|
||||||
|
IrCallImpl(startOffset, endOffset, unsignedType, unsafeCoerceIntrinsic).apply {
|
||||||
|
putTypeArgument(0, fromType)
|
||||||
|
putTypeArgument(1, unsignedType)
|
||||||
|
putValueArgument(0, this@asUnsigned)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback to calling `toUInt/ULong()` extension function.
|
||||||
|
IrCallImpl(startOffset, endOffset, unsignedConversionFunction.owner.returnType, unsignedConversionFunction).apply {
|
||||||
|
extensionReceiver = this@asUnsigned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun IrExpression.asSigned(): IrExpression {
|
||||||
|
val toType = elementClass.defaultType
|
||||||
|
if (type == toType) return this
|
||||||
|
|
||||||
|
return if (unsafeCoerceIntrinsic != null) {
|
||||||
|
IrCallImpl(startOffset, endOffset, toType, unsafeCoerceIntrinsic).apply {
|
||||||
|
putTypeArgument(0, unsignedType)
|
||||||
|
putTypeArgument(1, toType)
|
||||||
|
putValueArgument(0, this@asSigned)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback to calling `toInt/Long()` function.
|
||||||
|
asElementType()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
|
internal class UIntProgressionType(symbols: Symbols<CommonBackendContext>) :
|
||||||
|
UnsignedProgressionType(
|
||||||
|
symbols,
|
||||||
|
elementClass = symbols.int.owner,
|
||||||
|
stepClass = symbols.int.owner,
|
||||||
|
minValueAsLong = UInt.MIN_VALUE.toLong(),
|
||||||
|
maxValueAsLong = UInt.MAX_VALUE.toLong(),
|
||||||
|
// Uses `getProgressionLastElement(UInt, UInt, Int): UInt`
|
||||||
|
getProgressionLastElementFunction = symbols.getProgressionLastElementByReturnType[symbols.uInt!!],
|
||||||
|
unsignedType = symbols.uInt!!.defaultType,
|
||||||
|
unsignedConversionFunction = symbols.toUIntByExtensionReceiver.getValue(symbols.int.defaultType.toKotlinType())
|
||||||
|
) {
|
||||||
|
override fun DeclarationIrBuilder.minValueExpression() = irInt(UInt.MIN_VALUE.toInt())
|
||||||
|
|
||||||
|
override fun DeclarationIrBuilder.zeroStepExpression() = irInt(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
|
internal class ULongProgressionType(symbols: Symbols<CommonBackendContext>) :
|
||||||
|
UnsignedProgressionType(
|
||||||
|
symbols,
|
||||||
|
elementClass = symbols.long.owner,
|
||||||
|
stepClass = symbols.long.owner,
|
||||||
|
minValueAsLong = ULong.MIN_VALUE.toLong(),
|
||||||
|
maxValueAsLong = ULong.MAX_VALUE.toLong(),
|
||||||
|
// Uses `getProgressionLastElement(ULong, ULong, Long): ULong`
|
||||||
|
getProgressionLastElementFunction = symbols.getProgressionLastElementByReturnType[symbols.uLong!!],
|
||||||
|
unsignedType = symbols.uLong!!.defaultType,
|
||||||
|
unsignedConversionFunction = symbols.toULongByExtensionReceiver.getValue(symbols.long.defaultType.toKotlinType())
|
||||||
|
) {
|
||||||
|
override fun DeclarationIrBuilder.minValueExpression() = irLong(ULong.MIN_VALUE.toLong())
|
||||||
|
|
||||||
|
override fun DeclarationIrBuilder.zeroStepExpression() = irLong(0)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user