ForLoopsLowering: Add support for unsigned progressions.
This commit is contained in:
committed by
Alexander Udalov
parent
9fdb1ff4f8
commit
03ef3724f4
@@ -64,9 +64,13 @@ open class BuiltinSymbolsBase(protected val irBuiltIns: IrBuiltIns, protected va
|
|||||||
)
|
)
|
||||||
|
|
||||||
private fun getClass(name: Name, vararg packageNameSegments: String = arrayOf("kotlin")): IrClassSymbol =
|
private fun getClass(name: Name, vararg packageNameSegments: String = arrayOf("kotlin")): IrClassSymbol =
|
||||||
symbolTable.referenceClass(
|
getClassOrNull(name, *packageNameSegments) ?: error("Class '$name' not found in package '${packageNameSegments.joinToString(".")}'")
|
||||||
builtInsPackage(*packageNameSegments).getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
|
|
||||||
)
|
private fun getClassOrNull(name: Name, vararg packageNameSegments: String = arrayOf("kotlin")): IrClassSymbol? =
|
||||||
|
(builtInsPackage(*packageNameSegments).getContributedClassifier(
|
||||||
|
name,
|
||||||
|
NoLookupLocation.FROM_BACKEND
|
||||||
|
) as? ClassDescriptor)?.let { symbolTable.referenceClass(it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use this table to reference external dependencies.
|
* Use this table to reference external dependencies.
|
||||||
@@ -89,11 +93,22 @@ open class BuiltinSymbolsBase(protected val irBuiltIns: IrBuiltIns, protected va
|
|||||||
.map { symbolTable.referenceFunction(it) }
|
.map { symbolTable.referenceFunction(it) }
|
||||||
|
|
||||||
private fun progression(name: String) = getClass(Name.identifier(name), "kotlin", "ranges")
|
private fun progression(name: String) = getClass(Name.identifier(name), "kotlin", "ranges")
|
||||||
|
private fun progressionOrNull(name: String) = getClassOrNull(Name.identifier(name), "kotlin", "ranges")
|
||||||
|
|
||||||
|
// The "...OrNull" variants are used for unsigned (and progressions) because the minimal stdlib used in tests do not include those
|
||||||
|
// those classes. It was not feasible to add them to the JS reduced runtime because all its transitive dependencies also need to be
|
||||||
|
// added, which would include a lot of the full stdlib.
|
||||||
|
open val uByte = getClassOrNull(Name.identifier("UByte"), "kotlin")
|
||||||
|
open val uShort = getClassOrNull(Name.identifier("UShort"), "kotlin")
|
||||||
|
open val uInt = getClassOrNull(Name.identifier("UInt"), "kotlin")
|
||||||
|
open val uLong = getClassOrNull(Name.identifier("ULong"), "kotlin")
|
||||||
|
val uIntProgression = progressionOrNull("UIntProgression")
|
||||||
|
val uLongProgression = progressionOrNull("ULongProgression")
|
||||||
|
|
||||||
val charProgression = progression("CharProgression")
|
val charProgression = progression("CharProgression")
|
||||||
val intProgression = progression("IntProgression")
|
val intProgression = progression("IntProgression")
|
||||||
val longProgression = progression("LongProgression")
|
val longProgression = progression("LongProgression")
|
||||||
val progressionClasses = listOf(charProgression, intProgression, longProgression)
|
val progressionClasses = listOfNotNull(charProgression, intProgression, longProgression, uIntProgression, uLongProgression)
|
||||||
val progressionClassesTypes = progressionClasses.map { it.descriptor.defaultType }.toSet()
|
val progressionClassesTypes = progressionClasses.map { it.descriptor.defaultType }.toSet()
|
||||||
|
|
||||||
val getProgressionLastElementByReturnType = builtInsPackage("kotlin", "internal")
|
val getProgressionLastElementByReturnType = builtInsPackage("kotlin", "internal")
|
||||||
@@ -105,6 +120,18 @@ open class BuiltinSymbolsBase(protected val irBuiltIns: IrBuiltIns, protected va
|
|||||||
c to f
|
c to f
|
||||||
}.toMap()
|
}.toMap()
|
||||||
|
|
||||||
|
val toUIntByExtensionReceiver = builtInsPackage("kotlin").getContributedFunctions(
|
||||||
|
Name.identifier("toUInt"),
|
||||||
|
NoLookupLocation.FROM_BACKEND
|
||||||
|
).filter { it.containingDeclaration !is BuiltInsPackageFragment && it.extensionReceiverParameter != null }
|
||||||
|
.map { Pair(it.extensionReceiverParameter!!.type, symbolTable.referenceSimpleFunction(it)) }.toMap()
|
||||||
|
|
||||||
|
val toULongByExtensionReceiver = builtInsPackage("kotlin").getContributedFunctions(
|
||||||
|
Name.identifier("toULong"),
|
||||||
|
NoLookupLocation.FROM_BACKEND
|
||||||
|
).filter { it.containingDeclaration !is BuiltInsPackageFragment && it.extensionReceiverParameter != null }
|
||||||
|
.map { Pair(it.extensionReceiverParameter!!.type, symbolTable.referenceSimpleFunction(it)) }.toMap()
|
||||||
|
|
||||||
val any = symbolTable.referenceClass(builtIns.any)
|
val any = symbolTable.referenceClass(builtIns.any)
|
||||||
val unit = symbolTable.referenceClass(builtIns.unit)
|
val unit = symbolTable.referenceClass(builtIns.unit)
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.backend.common.lower.loops
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
|
||||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||||
@@ -238,7 +237,7 @@ private class RangeLoopTransformer(
|
|||||||
mainLoopVariable.endOffset,
|
mainLoopVariable.endOffset,
|
||||||
context.irBuiltIns.unitType,
|
context.irBuiltIns.unitType,
|
||||||
IrStatementOrigin.FOR_LOOP_NEXT,
|
IrStatementOrigin.FOR_LOOP_NEXT,
|
||||||
loopHeader.initializeIteration(mainLoopVariable, loopVariableComponents, symbols, this)
|
loopHeader.initializeIteration(mainLoopVariable, loopVariableComponents, this)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+106
-41
@@ -14,46 +14,77 @@ 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.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.IrType
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.types.isSubtypeOfClass
|
import org.jetbrains.kotlin.ir.util.functions
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||||
|
|
||||||
// TODO: Handle withIndex()
|
|
||||||
// TODO: Handle UIntProgression, ULongProgression
|
|
||||||
|
|
||||||
/** Represents a progression type in the Kotlin stdlib. */
|
/** Represents a progression type in the Kotlin stdlib. */
|
||||||
internal enum class ProgressionType(val elementCastFunctionName: Name, val stepCastFunctionName: Name) {
|
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")),
|
INT_PROGRESSION(Name.identifier("toInt"), Name.identifier("toInt")),
|
||||||
LONG_PROGRESSION(Name.identifier("toLong"), Name.identifier("toLong")),
|
LONG_PROGRESSION(Name.identifier("toLong"), Name.identifier("toLong"), isLong = true),
|
||||||
CHAR_PROGRESSION(Name.identifier("toChar"), Name.identifier("toInt"));
|
CHAR_PROGRESSION(Name.identifier("toChar"), Name.identifier("toInt")),
|
||||||
|
UINT_PROGRESSION(Name.identifier("toUInt"), Name.identifier("toInt"), isUnsigned = true),
|
||||||
|
ULONG_PROGRESSION(Name.identifier("toULong"), Name.identifier("toLong"), isLong = true, isUnsigned = true);
|
||||||
|
|
||||||
/** Returns the [IrType] of the `first`/`last` properties and elements in the progression. */
|
/** Returns the [IrType] of the `first`/`last` properties and elements in the progression. */
|
||||||
fun elementType(builtIns: IrBuiltIns): IrType = when (this) {
|
fun elementType(symbols: Symbols<CommonBackendContext>): IrType = elementClassifier(symbols).defaultType
|
||||||
INT_PROGRESSION -> builtIns.intType
|
|
||||||
LONG_PROGRESSION -> builtIns.longType
|
/** Returns the [IrClassSymbol] of the `first`/`last` properties and elements in the progression. */
|
||||||
CHAR_PROGRESSION -> builtIns.charType
|
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. */
|
/** Returns the [IrType] of the `step` property in the progression. */
|
||||||
fun stepType(builtIns: IrBuiltIns): IrType = when (this) {
|
fun stepType(builtIns: IrBuiltIns): IrType = when (this) {
|
||||||
INT_PROGRESSION, CHAR_PROGRESSION -> builtIns.intType
|
INT_PROGRESSION, CHAR_PROGRESSION, UINT_PROGRESSION -> builtIns.intType
|
||||||
LONG_PROGRESSION -> builtIns.longType
|
LONG_PROGRESSION, ULONG_PROGRESSION -> builtIns.longType
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the [IrClassSymbol] of the `step` property type constructor in the progression. */
|
/** Returns the [IrClassSymbol] of the `step` property type constructor in the progression. */
|
||||||
fun stepClassifier(builtIns: IrBuiltIns): IrClassSymbol = when(this) {
|
fun stepClassifier(builtIns: IrBuiltIns): IrClassSymbol = when(this) {
|
||||||
INT_PROGRESSION, CHAR_PROGRESSION -> builtIns.intClass
|
INT_PROGRESSION, CHAR_PROGRESSION , UINT_PROGRESSION-> builtIns.intClass
|
||||||
LONG_PROGRESSION -> builtIns.longClass
|
LONG_PROGRESSION, ULONG_PROGRESSION -> builtIns.longClass
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromIrType(irType: IrType, symbols: Symbols<CommonBackendContext>): ProgressionType? = when {
|
fun fromIrType(irType: IrType, symbols: Symbols<CommonBackendContext>): ProgressionType? = when {
|
||||||
irType.isSubtypeOfClass(symbols.charProgression) -> CHAR_PROGRESSION
|
irType.isSubtypeOfClass(symbols.charProgression) -> CHAR_PROGRESSION
|
||||||
irType.isSubtypeOfClass(symbols.intProgression) -> INT_PROGRESSION
|
irType.isSubtypeOfClass(symbols.intProgression) -> INT_PROGRESSION
|
||||||
irType.isSubtypeOfClass(symbols.longProgression) -> LONG_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
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,12 +144,18 @@ internal class ProgressionHeaderInfo(
|
|||||||
additionalNotEmptyCondition = additionalNotEmptyCondition
|
additionalNotEmptyCondition = additionalNotEmptyCondition
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
val canOverflow: Boolean by lazy {
|
val canOverflow: Boolean by lazy {
|
||||||
if (canOverflow != null) return@lazy canOverflow
|
if (canOverflow != null) return@lazy canOverflow
|
||||||
|
|
||||||
// We can't determine the safe limit at compile-time if "step" is not const.
|
// We can't determine the safe limit at compile-time if "step" is not const.
|
||||||
val stepValueAsLong = step.constLongValue ?: return@lazy true
|
val stepValueAsLong = step.constLongValue ?: return@lazy true
|
||||||
|
|
||||||
|
if (direction == ProgressionDirection.UNKNOWN) {
|
||||||
|
// If we don't know the direction, we can't be sure which limit to use.
|
||||||
|
return@lazy true
|
||||||
|
}
|
||||||
|
|
||||||
// Induction variable can NOT overflow if "last" is const and is <= (MAX/MIN_VALUE - step) (depending on direction).
|
// Induction variable can NOT overflow if "last" is const and is <= (MAX/MIN_VALUE - step) (depending on direction).
|
||||||
//
|
//
|
||||||
// Examples that can NOT overflow:
|
// Examples that can NOT overflow:
|
||||||
@@ -137,26 +174,51 @@ internal class ProgressionHeaderInfo(
|
|||||||
// - `0..10 step someStep()` CAN overflow (we don't know the step and hence can't determine the safe limit)
|
// - `0..10 step someStep()` CAN overflow (we don't know the step and hence can't determine the safe limit)
|
||||||
// - `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)
|
||||||
val lastValueAsLong = last.constLongValue ?: return@lazy true // If "last" is not a const Number or Char.
|
|
||||||
when (direction) {
|
if (progressionType.isUnsigned) {
|
||||||
ProgressionDirection.UNKNOWN ->
|
// "step" is still signed for unsigned progressions.
|
||||||
// If we don't know the direction, we can't be sure which limit to use.
|
val lastValueAsULong = last.constLongValue?.toULong() ?: return@lazy true // If "last" is not a const Number or Char.
|
||||||
true
|
when (direction) {
|
||||||
ProgressionDirection.DECREASING -> {
|
ProgressionDirection.DECREASING -> {
|
||||||
val constLimitAsLong = when (progressionType) {
|
val constLimitAsULong = when (progressionType) {
|
||||||
ProgressionType.INT_PROGRESSION -> Int.MIN_VALUE.toLong()
|
ProgressionType.UINT_PROGRESSION -> UInt.MIN_VALUE.toULong()
|
||||||
ProgressionType.CHAR_PROGRESSION -> Char.MIN_VALUE.toLong()
|
ProgressionType.ULONG_PROGRESSION -> ULong.MIN_VALUE
|
||||||
ProgressionType.LONG_PROGRESSION -> Long.MIN_VALUE
|
else -> error("Unexpected progression type")
|
||||||
|
}
|
||||||
|
lastValueAsULong < (constLimitAsULong - stepValueAsLong.toULong())
|
||||||
}
|
}
|
||||||
lastValueAsLong < (constLimitAsLong - stepValueAsLong)
|
ProgressionDirection.INCREASING -> {
|
||||||
|
val constLimitAsULong = when (progressionType) {
|
||||||
|
ProgressionType.UINT_PROGRESSION -> UInt.MAX_VALUE.toULong()
|
||||||
|
ProgressionType.ULONG_PROGRESSION -> ULong.MAX_VALUE
|
||||||
|
else -> error("Unexpected progression type")
|
||||||
|
}
|
||||||
|
lastValueAsULong > (constLimitAsULong - stepValueAsLong.toULong())
|
||||||
|
}
|
||||||
|
else -> error("Unexpected progression direction")
|
||||||
}
|
}
|
||||||
ProgressionDirection.INCREASING -> {
|
} else {
|
||||||
val constLimitAsLong = when (progressionType) {
|
val lastValueAsLong = last.constLongValue ?: return@lazy true // If "last" is not a const Number or Char.
|
||||||
ProgressionType.INT_PROGRESSION -> Int.MAX_VALUE.toLong()
|
when (direction) {
|
||||||
ProgressionType.CHAR_PROGRESSION -> Char.MAX_VALUE.toLong()
|
ProgressionDirection.DECREASING -> {
|
||||||
ProgressionType.LONG_PROGRESSION -> Long.MAX_VALUE
|
val constLimitAsLong = when (progressionType) {
|
||||||
|
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 -> {
|
||||||
|
val constLimitAsLong = when (progressionType) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
else -> error("Unexpected progression direction")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,14 +330,17 @@ internal abstract class HeaderInfoBuilder(context: CommonBackendContext, private
|
|||||||
|
|
||||||
private val symbols = context.ir.symbols
|
private val symbols = context.ir.symbols
|
||||||
|
|
||||||
// TODO: Include unsigned types
|
private val progressionElementTypes = listOfNotNull(
|
||||||
private val progressionElementTypes = listOf(
|
symbols.byte,
|
||||||
context.irBuiltIns.byteType,
|
symbols.short,
|
||||||
context.irBuiltIns.shortType,
|
symbols.int,
|
||||||
context.irBuiltIns.intType,
|
symbols.long,
|
||||||
context.irBuiltIns.longType,
|
symbols.char,
|
||||||
context.irBuiltIns.charType
|
symbols.uByte,
|
||||||
)
|
symbols.uShort,
|
||||||
|
symbols.uInt,
|
||||||
|
symbols.uLong
|
||||||
|
).map { it.defaultType }
|
||||||
|
|
||||||
private val progressionHandlers = listOf(
|
private val progressionHandlers = listOf(
|
||||||
CollectionIndicesHandler(context),
|
CollectionIndicesHandler(context),
|
||||||
|
|||||||
+109
-65
@@ -6,7 +6,6 @@
|
|||||||
package org.jetbrains.kotlin.backend.common.lower.loops
|
package org.jetbrains.kotlin.backend.common.lower.loops
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
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.backend.common.lower.DeclarationIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
||||||
@@ -50,7 +49,6 @@ internal interface ForLoopHeader {
|
|||||||
fun initializeIteration(
|
fun initializeIteration(
|
||||||
loopVariable: IrVariable?,
|
loopVariable: IrVariable?,
|
||||||
loopVariableComponents: Map<Int, IrVariable>,
|
loopVariableComponents: Map<Int, IrVariable>,
|
||||||
symbols: Symbols<CommonBackendContext>,
|
|
||||||
builder: DeclarationIrBuilder
|
builder: DeclarationIrBuilder
|
||||||
): List<IrStatement>
|
): List<IrStatement>
|
||||||
|
|
||||||
@@ -61,6 +59,7 @@ internal interface ForLoopHeader {
|
|||||||
internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
|
internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
|
||||||
protected val headerInfo: T,
|
protected val headerInfo: T,
|
||||||
builder: DeclarationIrBuilder,
|
builder: DeclarationIrBuilder,
|
||||||
|
context: CommonBackendContext,
|
||||||
protected val isLastInclusive: Boolean
|
protected val isLastInclusive: Boolean
|
||||||
) : ForLoopHeader {
|
) : ForLoopHeader {
|
||||||
|
|
||||||
@@ -68,16 +67,18 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
|
|||||||
|
|
||||||
val inductionVariable: IrVariable
|
val inductionVariable: IrVariable
|
||||||
val stepVariable: IrVariable
|
val stepVariable: IrVariable
|
||||||
|
val stepVariableForIncrement: IrVariable?
|
||||||
protected val lastVariableIfCanCacheLast: IrVariable?
|
protected val lastVariableIfCanCacheLast: IrVariable?
|
||||||
protected val lastExpression: IrExpression
|
protected val lastExpression: IrExpression
|
||||||
// Always copy `lastExpression` is it may be used in multiple conditions.
|
// Always copy `lastExpression` is it may be used in multiple conditions.
|
||||||
get() = field.deepCopyWithSymbols()
|
get() = field.deepCopyWithSymbols()
|
||||||
|
|
||||||
|
private val symbols = context.ir.symbols
|
||||||
private val elementType: IrType
|
private val elementType: IrType
|
||||||
|
|
||||||
init {
|
init {
|
||||||
with(builder) {
|
with(builder) {
|
||||||
elementType = headerInfo.progressionType.elementType(context.irBuiltIns)
|
elementType = headerInfo.progressionType.elementType(symbols)
|
||||||
|
|
||||||
// For this loop:
|
// For this loop:
|
||||||
//
|
//
|
||||||
@@ -89,10 +90,7 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
|
|||||||
// In the above example, if first() is a Long and last() is an Int, this creates a
|
// 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.
|
// LongProgression so last() should be cast to a Long.
|
||||||
inductionVariable = scope.createTemporaryVariable(
|
inductionVariable = scope.createTemporaryVariable(
|
||||||
headerInfo.first.castIfNecessary(
|
headerInfo.progressionType.castElementIfNecessary(headerInfo.first, context),
|
||||||
elementType,
|
|
||||||
headerInfo.progressionType.elementCastFunctionName
|
|
||||||
),
|
|
||||||
nameHint = "inductionVariable",
|
nameHint = "inductionVariable",
|
||||||
isMutable = true
|
isMutable = true
|
||||||
)
|
)
|
||||||
@@ -101,12 +99,7 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
|
|||||||
// they are non-nullable (the frontend takes care about this). So we need to cast
|
// they are non-nullable (the frontend takes care about this). So we need to cast
|
||||||
// them to non-nullable.
|
// them to non-nullable.
|
||||||
// TODO: Confirm if casting to non-nullable is still necessary
|
// TODO: Confirm if casting to non-nullable is still necessary
|
||||||
val last = ensureNotNullable(
|
val last = ensureNotNullable(headerInfo.progressionType.castElementIfNecessary(headerInfo.last, context))
|
||||||
headerInfo.last.castIfNecessary(
|
|
||||||
elementType,
|
|
||||||
headerInfo.progressionType.elementCastFunctionName
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
lastVariableIfCanCacheLast = if (headerInfo.canCacheLast) {
|
lastVariableIfCanCacheLast = if (headerInfo.canCacheLast) {
|
||||||
scope.createTemporaryVariable(
|
scope.createTemporaryVariable(
|
||||||
@@ -117,17 +110,29 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
|
|||||||
|
|
||||||
lastExpression = if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else last
|
lastExpression = if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else last
|
||||||
|
|
||||||
stepVariable = headerInfo.progressionType.stepType(context.irBuiltIns).let {
|
stepVariable =
|
||||||
scope.createTemporaryVariable(
|
scope.createTemporaryVariable(
|
||||||
ensureNotNullable(
|
ensureNotNullable(headerInfo.progressionType.castStepIfNecessary(headerInfo.step, context)),
|
||||||
headerInfo.step.castIfNecessary(
|
|
||||||
it,
|
|
||||||
headerInfo.progressionType.stepCastFunctionName
|
|
||||||
)
|
|
||||||
),
|
|
||||||
nameHint = "step",
|
nameHint = "step",
|
||||||
irType = it
|
irType = headerInfo.progressionType.stepType(context.irBuiltIns)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
stepVariableForIncrement = when (headerInfo.progressionType) {
|
||||||
|
ProgressionType.UINT_PROGRESSION -> {
|
||||||
|
val castFun = context.ir.symbols.toUIntByExtensionReceiver.getValue(stepVariable.type.toKotlinType())
|
||||||
|
scope.createTemporaryVariable(
|
||||||
|
irCall(castFun).apply { extensionReceiver = irGet(stepVariable) },
|
||||||
|
nameHint = "stepForIncrement"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ProgressionType.ULONG_PROGRESSION -> {
|
||||||
|
val castFun = context.ir.symbols.toULongByExtensionReceiver.getValue(stepVariable.type.toKotlinType())
|
||||||
|
scope.createTemporaryVariable(
|
||||||
|
irCall(castFun).apply { extensionReceiver = irGet(stepVariable) },
|
||||||
|
nameHint = "stepForIncrement"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,16 +147,17 @@ 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
|
// inductionVariable = inductionVariable + step
|
||||||
|
val stepVariableToUse = stepVariableForIncrement ?: stepVariable
|
||||||
val plusFun = elementType.getClass()!!.functions.single {
|
val plusFun = elementType.getClass()!!.functions.single {
|
||||||
it.name == OperatorNameConventions.PLUS &&
|
it.name == OperatorNameConventions.PLUS &&
|
||||||
it.valueParameters.size == 1 &&
|
it.valueParameters.size == 1 &&
|
||||||
it.valueParameters[0].type == stepVariable.type
|
it.valueParameters[0].type == stepVariableToUse.type
|
||||||
}
|
}
|
||||||
irSetVar(
|
irSetVar(
|
||||||
inductionVariable.symbol, irCallOp(
|
inductionVariable.symbol, irCallOp(
|
||||||
plusFun.symbol, plusFun.returnType,
|
plusFun.symbol, plusFun.returnType,
|
||||||
irGet(inductionVariable),
|
irGet(inductionVariable),
|
||||||
irGet(stepVariable)
|
irGet(stepVariableToUse)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -160,25 +166,65 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
|
|||||||
with(builder) {
|
with(builder) {
|
||||||
val builtIns = context.irBuiltIns
|
val builtIns = context.irBuiltIns
|
||||||
val progressionType = headerInfo.progressionType
|
val progressionType = headerInfo.progressionType
|
||||||
val progressionElementType = progressionType.elementType(builtIns)
|
val progressionElementType = progressionType.elementType(symbols)
|
||||||
val compFun =
|
|
||||||
if (isLastInclusive) builtIns.lessOrEqualFunByOperandType[progressionElementType.classifierOrFail]!!
|
|
||||||
else builtIns.lessFunByOperandType[progressionElementType.classifierOrFail]!!
|
|
||||||
|
|
||||||
// The default condition depends on the direction.
|
// `compareTo` must be used for UInt/ULong; they don't have intrinsic comparison operators.
|
||||||
when (headerInfo.direction) {
|
val intCompFun = if (isLastInclusive) {
|
||||||
ProgressionDirection.DECREASING ->
|
builtIns.lessOrEqualFunByOperandType.getValue(builtIns.intClass)
|
||||||
// last <= inductionVar (use `<` if last is exclusive)
|
} else {
|
||||||
irCall(compFun).apply {
|
builtIns.lessFunByOperandType.getValue(builtIns.intClass)
|
||||||
|
}
|
||||||
|
val elementCompareToFun = progressionElementType.getClass()!!.functions.single {
|
||||||
|
it.name == OperatorNameConventions.COMPARE_TO &&
|
||||||
|
it.dispatchReceiverParameter != null && it.extensionReceiverParameter == null &&
|
||||||
|
it.valueParameters.size == 1 && it.valueParameters[0].type == progressionElementType
|
||||||
|
}
|
||||||
|
|
||||||
|
val elementCompFun =
|
||||||
|
if (isLastInclusive) {
|
||||||
|
builtIns.lessOrEqualFunByOperandType[progressionElementType.classifierOrFail]
|
||||||
|
} else {
|
||||||
|
builtIns.lessFunByOperandType[progressionElementType.classifierOrFail]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun conditionForDecreasing(): IrExpression =
|
||||||
|
// last <= inductionVar (use `<` if last is exclusive)
|
||||||
|
if (progressionType.isUnsigned) {
|
||||||
|
irCall(intCompFun).apply {
|
||||||
|
putValueArgument(0, irCall(elementCompareToFun).apply {
|
||||||
|
dispatchReceiver = lastExpression
|
||||||
|
putValueArgument(0, irGet(inductionVariable))
|
||||||
|
})
|
||||||
|
putValueArgument(1, irInt(0))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
irCall(elementCompFun!!).apply {
|
||||||
putValueArgument(0, lastExpression)
|
putValueArgument(0, lastExpression)
|
||||||
putValueArgument(1, irGet(inductionVariable))
|
putValueArgument(1, irGet(inductionVariable))
|
||||||
}
|
}
|
||||||
ProgressionDirection.INCREASING ->
|
}
|
||||||
// inductionVar <= last (use `<` if last is exclusive)
|
|
||||||
irCall(compFun).apply {
|
fun conditionForIncreasing(): IrExpression =
|
||||||
|
// inductionVar <= last (use `<` if last is exclusive)
|
||||||
|
if (progressionType.isUnsigned) {
|
||||||
|
irCall(intCompFun).apply {
|
||||||
|
putValueArgument(0, irCall(elementCompareToFun).apply {
|
||||||
|
dispatchReceiver = irGet(inductionVariable)
|
||||||
|
putValueArgument(0, lastExpression)
|
||||||
|
})
|
||||||
|
putValueArgument(1, irInt(0))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
irCall(elementCompFun!!).apply {
|
||||||
putValueArgument(0, irGet(inductionVariable))
|
putValueArgument(0, irGet(inductionVariable))
|
||||||
putValueArgument(1, lastExpression)
|
putValueArgument(1, lastExpression)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default condition depends on the direction.
|
||||||
|
when (headerInfo.direction) {
|
||||||
|
ProgressionDirection.DECREASING -> conditionForDecreasing()
|
||||||
|
ProgressionDirection.INCREASING -> conditionForIncreasing()
|
||||||
ProgressionDirection.UNKNOWN -> {
|
ProgressionDirection.UNKNOWN -> {
|
||||||
// If the direction is unknown, we check depending on the "step" value:
|
// If the direction is unknown, we check depending on the "step" value:
|
||||||
// // (use `<` if last is exclusive)
|
// // (use `<` if last is exclusive)
|
||||||
@@ -187,33 +233,31 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
|
|||||||
val isLong = progressionType == ProgressionType.LONG_PROGRESSION
|
val isLong = progressionType == ProgressionType.LONG_PROGRESSION
|
||||||
context.oror(
|
context.oror(
|
||||||
context.andand(
|
context.andand(
|
||||||
irCall(builtIns.greaterFunByOperandType[stepTypeClassifier]!!).apply {
|
irCall(builtIns.greaterFunByOperandType.getValue(stepTypeClassifier)).apply {
|
||||||
putValueArgument(0, irGet(stepVariable))
|
putValueArgument(0, irGet(stepVariable))
|
||||||
putValueArgument(1, if (isLong) irLong(0) else irInt(0))
|
putValueArgument(1, if (progressionType.isLong) irLong(0) else irInt(0))
|
||||||
},
|
},
|
||||||
irCall(compFun).apply {
|
conditionForIncreasing()
|
||||||
putValueArgument(0, irGet(inductionVariable))
|
),
|
||||||
putValueArgument(1, lastExpression)
|
|
||||||
}),
|
|
||||||
context.andand(
|
context.andand(
|
||||||
irCall(builtIns.lessFunByOperandType[stepTypeClassifier]!!).apply {
|
irCall(builtIns.lessFunByOperandType.getValue(stepTypeClassifier)).apply {
|
||||||
putValueArgument(0, irGet(stepVariable))
|
putValueArgument(0, irGet(stepVariable))
|
||||||
putValueArgument(1, if (isLong) irLong(0) else irInt(0))
|
putValueArgument(1, if (progressionType.isLong) irLong(0) else irInt(0))
|
||||||
},
|
},
|
||||||
irCall(compFun).apply {
|
conditionForDecreasing()
|
||||||
putValueArgument(0, lastExpression)
|
)
|
||||||
putValueArgument(1, irGet(inductionVariable))
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ProgressionLoopHeader(
|
internal class ProgressionLoopHeader(
|
||||||
headerInfo: ProgressionHeaderInfo,
|
headerInfo: ProgressionHeaderInfo,
|
||||||
builder: DeclarationIrBuilder
|
builder: DeclarationIrBuilder,
|
||||||
) : NumericForLoopHeader<ProgressionHeaderInfo>(headerInfo, builder, isLastInclusive = true) {
|
context: CommonBackendContext
|
||||||
|
) : NumericForLoopHeader<ProgressionHeaderInfo>(headerInfo, builder, context, isLastInclusive = true) {
|
||||||
|
|
||||||
// For this loop:
|
// For this loop:
|
||||||
//
|
//
|
||||||
@@ -230,14 +274,14 @@ internal class ProgressionLoopHeader(
|
|||||||
else
|
else
|
||||||
listOfNotNull(inductionVariable, lastVariableIfCanCacheLast)
|
listOfNotNull(inductionVariable, lastVariableIfCanCacheLast)
|
||||||
) +
|
) +
|
||||||
stepVariable
|
listOfNotNull(stepVariable, stepVariableForIncrement)
|
||||||
|
|
||||||
private var loopVariable: IrVariable? = null
|
private var loopVariable: IrVariable? = null
|
||||||
|
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
override fun initializeIteration(
|
override fun initializeIteration(
|
||||||
loopVariable: IrVariable?,
|
loopVariable: IrVariable?,
|
||||||
loopVariableComponents: Map<Int, IrVariable>,
|
loopVariableComponents: Map<Int, IrVariable>,
|
||||||
symbols: Symbols<CommonBackendContext>,
|
|
||||||
builder: DeclarationIrBuilder
|
builder: DeclarationIrBuilder
|
||||||
) =
|
) =
|
||||||
with(builder) {
|
with(builder) {
|
||||||
@@ -258,6 +302,7 @@ internal class ProgressionLoopHeader(
|
|||||||
listOfNotNull(loopVariable, incrementInductionVariable(this))
|
listOfNotNull(loopVariable, incrementInductionVariable(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
override fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?) =
|
override fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?) =
|
||||||
with(builder) {
|
with(builder) {
|
||||||
val newLoop = if (headerInfo.canOverflow) {
|
val newLoop = if (headerInfo.canOverflow) {
|
||||||
@@ -322,15 +367,16 @@ private class InitializerCallReplacer(symbolRemapper: SymbolRemapper, typeRemapp
|
|||||||
|
|
||||||
internal class IndexedGetLoopHeader(
|
internal class IndexedGetLoopHeader(
|
||||||
headerInfo: IndexedGetHeaderInfo,
|
headerInfo: IndexedGetHeaderInfo,
|
||||||
builder: DeclarationIrBuilder
|
builder: DeclarationIrBuilder,
|
||||||
) : NumericForLoopHeader<IndexedGetHeaderInfo>(headerInfo, builder, isLastInclusive = false) {
|
context: CommonBackendContext
|
||||||
|
) : NumericForLoopHeader<IndexedGetHeaderInfo>(headerInfo, builder, context, isLastInclusive = false) {
|
||||||
|
|
||||||
override val loopInitStatements = listOfNotNull(headerInfo.objectVariable, inductionVariable, lastVariableIfCanCacheLast, stepVariable)
|
override val loopInitStatements =
|
||||||
|
listOfNotNull(headerInfo.objectVariable, inductionVariable, lastVariableIfCanCacheLast, stepVariable, stepVariableForIncrement)
|
||||||
|
|
||||||
override fun initializeIteration(
|
override fun initializeIteration(
|
||||||
loopVariable: IrVariable?,
|
loopVariable: IrVariable?,
|
||||||
loopVariableComponents: Map<Int, IrVariable>,
|
loopVariableComponents: Map<Int, IrVariable>,
|
||||||
symbols: Symbols<CommonBackendContext>,
|
|
||||||
builder: DeclarationIrBuilder
|
builder: DeclarationIrBuilder
|
||||||
) =
|
) =
|
||||||
with(builder) {
|
with(builder) {
|
||||||
@@ -371,7 +417,8 @@ internal class IndexedGetLoopHeader(
|
|||||||
|
|
||||||
internal class WithIndexLoopHeader(
|
internal class WithIndexLoopHeader(
|
||||||
headerInfo: WithIndexHeaderInfo,
|
headerInfo: WithIndexHeaderInfo,
|
||||||
builder: DeclarationIrBuilder
|
builder: DeclarationIrBuilder,
|
||||||
|
context: CommonBackendContext
|
||||||
) : ForLoopHeader {
|
) : ForLoopHeader {
|
||||||
|
|
||||||
private val nestedLoopHeader: ForLoopHeader
|
private val nestedLoopHeader: ForLoopHeader
|
||||||
@@ -384,8 +431,8 @@ internal class WithIndexLoopHeader(
|
|||||||
// To build the optimized/lowered `for` loop over a `withIndex()` call, we first need the header for the underlying iterable so
|
// To build the optimized/lowered `for` loop over a `withIndex()` call, we first need the header for the underlying iterable so
|
||||||
// so that we know how to build the loop for that iterable. More info in comments in initializeIteration().
|
// so that we know how to build the loop for that iterable. More info in comments in initializeIteration().
|
||||||
nestedLoopHeader = when (val nestedInfo = headerInfo.nestedInfo) {
|
nestedLoopHeader = when (val nestedInfo = headerInfo.nestedInfo) {
|
||||||
is IndexedGetHeaderInfo -> IndexedGetLoopHeader(nestedInfo, this@with)
|
is IndexedGetHeaderInfo -> IndexedGetLoopHeader(nestedInfo, this@with, context)
|
||||||
is ProgressionHeaderInfo -> ProgressionLoopHeader(nestedInfo, this@with)
|
is ProgressionHeaderInfo -> ProgressionLoopHeader(nestedInfo, this@with, context)
|
||||||
is IterableHeaderInfo -> IterableLoopHeader(nestedInfo)
|
is IterableHeaderInfo -> IterableLoopHeader(nestedInfo)
|
||||||
is WithIndexHeaderInfo -> throw IllegalStateException("Nested WithIndexHeaderInfo not allowed for WithIndexLoopHeader")
|
is WithIndexHeaderInfo -> throw IllegalStateException("Nested WithIndexHeaderInfo not allowed for WithIndexLoopHeader")
|
||||||
}
|
}
|
||||||
@@ -409,7 +456,7 @@ internal class WithIndexLoopHeader(
|
|||||||
)
|
)
|
||||||
ownsIndexVariable = true
|
ownsIndexVariable = true
|
||||||
// `index++` during iteration initialization
|
// `index++` during iteration initialization
|
||||||
// TODO: MUSTDO: Check for overflow for Iterable and Sequence (call to checkIndexOverflow()).
|
// TODO: KT-34665: Check for overflow for Iterable and Sequence (call to checkIndexOverflow()).
|
||||||
val plusFun = indexVariable.type.getClass()!!.functions.first {
|
val plusFun = indexVariable.type.getClass()!!.functions.first {
|
||||||
it.name == OperatorNameConventions.PLUS &&
|
it.name == OperatorNameConventions.PLUS &&
|
||||||
it.valueParameters.size == 1 &&
|
it.valueParameters.size == 1 &&
|
||||||
@@ -435,7 +482,6 @@ internal class WithIndexLoopHeader(
|
|||||||
override fun initializeIteration(
|
override fun initializeIteration(
|
||||||
loopVariable: IrVariable?,
|
loopVariable: IrVariable?,
|
||||||
loopVariableComponents: Map<Int, IrVariable>,
|
loopVariableComponents: Map<Int, IrVariable>,
|
||||||
symbols: Symbols<CommonBackendContext>,
|
|
||||||
builder: DeclarationIrBuilder
|
builder: DeclarationIrBuilder
|
||||||
) =
|
) =
|
||||||
with(builder) {
|
with(builder) {
|
||||||
@@ -508,7 +554,6 @@ internal class WithIndexLoopHeader(
|
|||||||
listOfNotNull(loopVariableComponents[1], incrementIndexStatement) + nestedLoopHeader.initializeIteration(
|
listOfNotNull(loopVariableComponents[1], incrementIndexStatement) + nestedLoopHeader.initializeIteration(
|
||||||
loopVariableComponents[2],
|
loopVariableComponents[2],
|
||||||
linkedMapOf(),
|
linkedMapOf(),
|
||||||
symbols,
|
|
||||||
builder
|
builder
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -528,7 +573,6 @@ internal class IterableLoopHeader(
|
|||||||
override fun initializeIteration(
|
override fun initializeIteration(
|
||||||
loopVariable: IrVariable?,
|
loopVariable: IrVariable?,
|
||||||
loopVariableComponents: Map<Int, IrVariable>,
|
loopVariableComponents: Map<Int, IrVariable>,
|
||||||
symbols: Symbols<CommonBackendContext>,
|
|
||||||
builder: DeclarationIrBuilder
|
builder: DeclarationIrBuilder
|
||||||
) =
|
) =
|
||||||
with(builder) {
|
with(builder) {
|
||||||
@@ -615,9 +659,9 @@ internal class HeaderProcessor(
|
|||||||
|
|
||||||
val builder = context.createIrBuilder(scopeOwnerSymbol(), variable.startOffset, variable.endOffset)
|
val builder = context.createIrBuilder(scopeOwnerSymbol(), variable.startOffset, variable.endOffset)
|
||||||
return when (headerInfo) {
|
return when (headerInfo) {
|
||||||
is IndexedGetHeaderInfo -> IndexedGetLoopHeader(headerInfo, builder)
|
is IndexedGetHeaderInfo -> IndexedGetLoopHeader(headerInfo, builder, context)
|
||||||
is ProgressionHeaderInfo -> ProgressionLoopHeader(headerInfo, builder)
|
is ProgressionHeaderInfo -> ProgressionLoopHeader(headerInfo, builder, context)
|
||||||
is WithIndexHeaderInfo -> WithIndexLoopHeader(headerInfo, builder)
|
is WithIndexHeaderInfo -> WithIndexLoopHeader(headerInfo, builder, context)
|
||||||
is IterableHeaderInfo -> IterableLoopHeader(headerInfo)
|
is IterableHeaderInfo -> IterableLoopHeader(headerInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+60
-29
@@ -17,10 +17,12 @@ import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
|||||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||||
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.expressions.impl.IrConstImpl
|
||||||
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.*
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.types.asSimpleType
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
@@ -73,12 +75,19 @@ internal class DownToHandler(private val context: CommonBackendContext, private
|
|||||||
internal class UntilHandler(private val context: CommonBackendContext, private val progressionElementTypes: Collection<IrType>) :
|
internal class UntilHandler(private val context: CommonBackendContext, private val progressionElementTypes: Collection<IrType>) :
|
||||||
ProgressionHandler {
|
ProgressionHandler {
|
||||||
|
|
||||||
|
private val symbols = context.ir.symbols
|
||||||
|
private val uByteType = symbols.uByte?.defaultType
|
||||||
|
private val uShortType = symbols.uShort?.defaultType
|
||||||
|
private val uIntType = symbols.uInt?.defaultType
|
||||||
|
private val uLongType = symbols.uLong?.defaultType
|
||||||
|
|
||||||
override val matcher = SimpleCalleeMatcher {
|
override val matcher = SimpleCalleeMatcher {
|
||||||
singleArgumentExtension(FqName("kotlin.ranges.until"), progressionElementTypes)
|
singleArgumentExtension(FqName("kotlin.ranges.until"), progressionElementTypes)
|
||||||
parameterCount { it == 1 }
|
parameterCount { it == 1 }
|
||||||
parameter(0) { it.type in progressionElementTypes }
|
parameter(0) { it.type in progressionElementTypes }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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.
|
// `A until B` is essentially the same as `A .. (B-1)`. However, B could be MIN_VALUE and hence `(B-1)` could underflow.
|
||||||
@@ -121,10 +130,7 @@ internal class UntilHandler(private val context: CommonBackendContext, private v
|
|||||||
val untilArg = expression.getValueArgument(0)!!
|
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 = untilArg.castIfNecessary(
|
val untilArgCasted = data.castElementIfNecessary(untilArg, this@UntilHandler.context)
|
||||||
data.elementType(context.irBuiltIns),
|
|
||||||
data.elementCastFunctionName
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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
|
||||||
@@ -147,6 +153,24 @@ internal class UntilHandler(private val context: CommonBackendContext, private v
|
|||||||
ProgressionType.INT_PROGRESSION -> Pair(Int.MIN_VALUE.toLong(), irInt(Int.MIN_VALUE))
|
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.CHAR_PROGRESSION -> Pair(Char.MIN_VALUE.toLong(), irChar(Char.MIN_VALUE))
|
||||||
ProgressionType.LONG_PROGRESSION -> Pair(Long.MIN_VALUE, irLong(Long.MIN_VALUE))
|
ProgressionType.LONG_PROGRESSION -> Pair(Long.MIN_VALUE, irLong(Long.MIN_VALUE))
|
||||||
|
ProgressionType.UINT_PROGRESSION -> Pair(
|
||||||
|
UInt.MIN_VALUE.toLong(),
|
||||||
|
IrConstImpl.int(
|
||||||
|
startOffset,
|
||||||
|
endOffset,
|
||||||
|
symbols.uInt!!.defaultType,
|
||||||
|
UInt.MIN_VALUE.toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
ProgressionType.ULONG_PROGRESSION -> Pair(
|
||||||
|
ULong.MIN_VALUE.toLong(),
|
||||||
|
IrConstImpl.long(
|
||||||
|
startOffset,
|
||||||
|
endOffset,
|
||||||
|
symbols.uLong!!.defaultType,
|
||||||
|
ULong.MIN_VALUE.toLong()
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
val additionalNotEmptyCondition = untilArg.constLongValue.let {
|
val additionalNotEmptyCondition = untilArg.constLongValue.let {
|
||||||
when {
|
when {
|
||||||
@@ -197,11 +221,14 @@ internal class UntilHandler(private val context: CommonBackendContext, private v
|
|||||||
// infix fun Long.until(to: Short): LongRange
|
// infix fun Long.until(to: Short): LongRange
|
||||||
// infix fun Long.until(to: Int): LongRange
|
// infix fun Long.until(to: Int): LongRange
|
||||||
// infix fun Long.until(to: Long): LongRange
|
// infix fun Long.until(to: Long): LongRange
|
||||||
|
// infix fun UByte.until(to: UByte): UIntRange
|
||||||
|
// infix fun UShort.until(to: UShort): UIntRange
|
||||||
|
// infix fun UInt.until(to: UInt): UIntRange
|
||||||
|
// infix fun ULong.until(to: ULong): ULongRange
|
||||||
//
|
//
|
||||||
// The combinations where the range element type is strictly larger than the argument type do NOT need the additional condition.
|
// The combinations where the range element type is strictly larger than the argument type do NOT need the additional condition.
|
||||||
// In such combinations, there is no possibility of underflow when the argument (casted to the range element type) is decremented.
|
// In such combinations, there is no possibility of underflow when the argument (casted to the range element type) is decremented.
|
||||||
// For unexpected combinations that currently don't exist (e.g., Int until Char), we assume the check is needed to be safe.
|
// For unexpected combinations that currently don't exist (e.g., Int until Char), we assume the check is needed to be safe.
|
||||||
// TODO: Include unsigned types
|
|
||||||
return with(context.irBuiltIns) {
|
return with(context.irBuiltIns) {
|
||||||
when (receiverType) {
|
when (receiverType) {
|
||||||
charType -> true
|
charType -> true
|
||||||
@@ -213,7 +240,11 @@ internal class UntilHandler(private val context: CommonBackendContext, private v
|
|||||||
byteType, shortType, intType -> false
|
byteType, shortType, intType -> false
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
else -> true
|
uByteType -> false
|
||||||
|
uShortType -> false
|
||||||
|
uIntType -> true
|
||||||
|
uLongType -> true
|
||||||
|
else -> true // Default in case a new `until` overload is added to stdlib and this function was not updated.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,7 +289,7 @@ internal class StepHandler(
|
|||||||
// 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.stepType(context.irBuiltIns)
|
||||||
val stepGreaterFun = context.irBuiltIns.greaterFunByOperandType[data.stepClassifier(context.irBuiltIns)]!!
|
val stepGreaterFun = context.irBuiltIns.greaterFunByOperandType[data.stepClassifier(context.irBuiltIns)]!!
|
||||||
val zeroStep = if (data == ProgressionType.LONG_PROGRESSION) irLong(0) else irInt(0)
|
val zeroStep = if (data.isLong) irLong(0) else irInt(0)
|
||||||
val throwIllegalStepExceptionCall = {
|
val throwIllegalStepExceptionCall = {
|
||||||
irCall(context.irBuiltIns.illegalArgumentExceptionSymbol).apply {
|
irCall(context.irBuiltIns.illegalArgumentExceptionSymbol).apply {
|
||||||
val exceptionMessage = irConcat()
|
val exceptionMessage = irConcat()
|
||||||
@@ -458,29 +489,29 @@ internal class StepHandler(
|
|||||||
return last
|
return last
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call `getProgressionLastElement(first, last, step)`
|
// Call `getProgressionLastElement(first, last, step)`. The following overloads are present in the stdlib:
|
||||||
val stepTypeClassifier = progressionType.stepClassifier(context.irBuiltIns)
|
// - getProgressionLastElement(Int, Int, Int): Int // Used by IntProgression and CharProgression (uses Int step)
|
||||||
val getProgressionLastElementFun = symbols.getProgressionLastElementByReturnType[stepTypeClassifier]
|
// - getProgressionLastElement(Long, Long, Long): Long // Used by LongProgression
|
||||||
?: throw IllegalArgumentException("No `getProgressionLastElement` for step type $stepTypeClassifier")
|
// - getProgressionLastElement(UInt, UInt, Int): UInt // Used by UIntProgression (uses Int step)
|
||||||
|
// - getProgressionLastElement(ULong, ULong, Long): ULong // Used by ULongProgression (uses Long step)
|
||||||
|
//
|
||||||
|
// We make sure to retrieve the correct symbol and use the correct argument types.
|
||||||
|
val returnTypeClassifier = if (progressionType.isUnsigned) {
|
||||||
|
progressionType.elementClassifier(symbols)
|
||||||
|
} else {
|
||||||
|
progressionType.stepClassifier(context.irBuiltIns)
|
||||||
|
}
|
||||||
|
val getProgressionLastElementFun = symbols.getProgressionLastElementByReturnType[returnTypeClassifier]
|
||||||
|
?: throw IllegalArgumentException("No `getProgressionLastElement` for return type ${returnTypeClassifier.defaultType}")
|
||||||
return irCall(getProgressionLastElementFun).apply {
|
return irCall(getProgressionLastElementFun).apply {
|
||||||
putValueArgument(
|
if (progressionType.isUnsigned) {
|
||||||
0, first.deepCopyWithSymbols().castIfNecessary(
|
putValueArgument(0, progressionType.castElementIfNecessary(first.deepCopyWithSymbols(), this@StepHandler.context))
|
||||||
progressionType.stepType(context.irBuiltIns),
|
putValueArgument(1, progressionType.castElementIfNecessary(last.deepCopyWithSymbols(), this@StepHandler.context))
|
||||||
progressionType.stepCastFunctionName
|
} else {
|
||||||
)
|
putValueArgument(0, progressionType.castStepIfNecessary(first.deepCopyWithSymbols(), this@StepHandler.context))
|
||||||
)
|
putValueArgument(1, progressionType.castStepIfNecessary(last.deepCopyWithSymbols(), this@StepHandler.context))
|
||||||
putValueArgument(
|
}
|
||||||
1, last.deepCopyWithSymbols().castIfNecessary(
|
putValueArgument(2, progressionType.castStepIfNecessary(step.deepCopyWithSymbols(), this@StepHandler.context))
|
||||||
progressionType.stepType(context.irBuiltIns),
|
|
||||||
progressionType.stepCastFunctionName
|
|
||||||
)
|
|
||||||
)
|
|
||||||
putValueArgument(
|
|
||||||
2, step.deepCopyWithSymbols().castIfNecessary(
|
|
||||||
progressionType.stepType(context.irBuiltIns),
|
|
||||||
progressionType.stepCastFunctionName
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-13
@@ -14,24 +14,11 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression
|
|||||||
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
|
||||||
import org.jetbrains.kotlin.ir.types.getClass
|
import org.jetbrains.kotlin.ir.types.getClass
|
||||||
import org.jetbrains.kotlin.ir.types.isNothing
|
import org.jetbrains.kotlin.ir.types.isNothing
|
||||||
import org.jetbrains.kotlin.ir.util.functions
|
import org.jetbrains.kotlin.ir.util.functions
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
internal fun IrExpression.castIfNecessary(targetType: IrType, numberCastFunctionName: Name): IrExpression {
|
|
||||||
// This expression's type could be Nothing from an exception throw.
|
|
||||||
return if (type == targetType || type.isNothing()) {
|
|
||||||
this
|
|
||||||
} else {
|
|
||||||
val castFun = type.getClass()!!.functions.single { it.name == numberCastFunctionName && it.valueParameters.isEmpty() }
|
|
||||||
IrCallImpl(startOffset, endOffset, castFun.returnType, castFun.symbol)
|
|
||||||
.apply { dispatchReceiver = this@castIfNecessary }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return the negated value if the expression is const, otherwise call unaryMinus(). */
|
/** Return the negated value if the expression is const, otherwise call unaryMinus(). */
|
||||||
internal fun IrExpression.negate(): IrExpression {
|
internal fun IrExpression.negate(): IrExpression {
|
||||||
return when (val value = (this as? IrConst<*>)?.value as? Number) {
|
return when (val value = (this as? IrConst<*>)?.value as? Number) {
|
||||||
|
|||||||
@@ -299,6 +299,7 @@ private val jvmFilePhases =
|
|||||||
anonymousObjectSuperConstructorPhase then
|
anonymousObjectSuperConstructorPhase then
|
||||||
tailrecPhase then
|
tailrecPhase then
|
||||||
|
|
||||||
|
forLoopsPhase then
|
||||||
jvmInlineClassPhase then
|
jvmInlineClassPhase then
|
||||||
|
|
||||||
sharedVariablesPhase then
|
sharedVariablesPhase then
|
||||||
@@ -318,7 +319,6 @@ private val jvmFilePhases =
|
|||||||
|
|
||||||
jvmDefaultConstructorPhase then
|
jvmDefaultConstructorPhase then
|
||||||
|
|
||||||
forLoopsPhase then
|
|
||||||
flattenStringConcatenationPhase then
|
flattenStringConcatenationPhase then
|
||||||
foldConstantLoweringPhase then
|
foldConstantLoweringPhase then
|
||||||
computeStringTrimPhase then
|
computeStringTrimPhase then
|
||||||
|
|||||||
+4
-1
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.ir.copyParameterDeclarationsFrom
|
|||||||
import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom
|
import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom
|
||||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
|
||||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||||
@@ -42,7 +43,9 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
|||||||
val jvmInlineClassPhase = makeIrFilePhase(
|
val jvmInlineClassPhase = makeIrFilePhase(
|
||||||
::JvmInlineClassLowering,
|
::JvmInlineClassLowering,
|
||||||
name = "Inline Classes",
|
name = "Inline Classes",
|
||||||
description = "Lower inline classes"
|
description = "Lower inline classes",
|
||||||
|
// forLoopsPhase may produce UInt and ULong which are inline classes.
|
||||||
|
prerequisite = setOf(forLoopsPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+1
-2
@@ -1,5 +1,4 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
// WITH_RUNTIME
|
||||||
// TODO KT-36773 Use counter loop when generating 'for' loop over an unsigned range in JVM_IR
|
|
||||||
|
|
||||||
fun testUIntRangeLiteral(a: UInt, b: UInt): Int {
|
fun testUIntRangeLiteral(a: UInt, b: UInt): Int {
|
||||||
var s = 0
|
var s = 0
|
||||||
|
|||||||
Reference in New Issue
Block a user