Share for loops lowering with JVM IR backend. (#3294)

The code delete here was moved to common and is used for JVM
IR as well. We should remove the original kotlin-native version
so that we do improvements in the shared version going forward.
This commit is contained in:
Mads Sig Ager
2019-08-30 15:10:43 +02:00
committed by Nikolay Igotti
parent 3dc0e66f7d
commit e240621550
5 changed files with 1 additions and 799 deletions
@@ -3,12 +3,12 @@ package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.lower.inline.FunctionInlining
import org.jetbrains.kotlin.backend.common.lower.loops.ForLoopsLowering
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.konan.lower.*
import org.jetbrains.kotlin.backend.konan.lower.ExpectDeclarationsRemoving
import org.jetbrains.kotlin.backend.konan.lower.FinallyBlocksLowering
import org.jetbrains.kotlin.backend.konan.lower.InitializersLowering
import org.jetbrains.kotlin.backend.konan.lower.loops.ForLoopsLowering
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
@@ -1,233 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.lower.loops
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irIfThen
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.util.OperatorNameConventions
/** This lowering pass optimizes for-loops.
*
* Replace iteration over ranges (X.indices, a..b, etc.) and arrays with
* simple while loop over primitive induction variable.
*/
internal class ForLoopsLowering(val context: Context) : FileLoweringPass {
private val headerInfoBuilder = HeaderInfoBuilder(context)
override fun lower(irFile: IrFile) {
val oldLoopToNewLoop = mutableMapOf<IrLoop, IrLoop>()
val transformer = RangeLoopTransformer(context, oldLoopToNewLoop, headerInfoBuilder)
irFile.transformChildrenVoid(transformer)
// Update references in break/continue.
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitBreakContinue(jump: IrBreakContinue): IrExpression {
oldLoopToNewLoop[jump.loop]?.let { jump.loop = it }
return jump
}
})
}
}
private class RangeLoopTransformer(
val context: Context,
val oldLoopToNewLoop: MutableMap<IrLoop, IrLoop>,
headerInfoBuilder: HeaderInfoBuilder)
: IrElementTransformerVoidWithContext() {
private val symbols = context.ir.symbols
private val iteratorToLoopHeader = mutableMapOf<IrVariableSymbol, ForLoopHeader>()
private val headerProcessor = HeaderProcessor(context, headerInfoBuilder, this::getScopeOwnerSymbol)
fun getScopeOwnerSymbol() = currentScope!!.scope.scopeOwnerSymbol
override fun visitVariable(declaration: IrVariable): IrStatement {
val initializer = declaration.initializer
if (initializer == null || initializer !is IrCall) {
return super.visitVariable(declaration)
}
return when (initializer.origin) {
IrStatementOrigin.FOR_LOOP_ITERATOR ->
processHeader(declaration)
IrStatementOrigin.FOR_LOOP_NEXT ->
processNext(declaration)
else -> null
} ?: super.visitVariable(declaration)
}
private fun processHeader(variable: IrVariable): IrStatement? {
assert(variable.symbol !in iteratorToLoopHeader)
val forLoopInfo = headerProcessor.processHeader(variable)
?: return null
iteratorToLoopHeader[variable.symbol] = forLoopInfo
return IrCompositeImpl(
variable.startOffset,
variable.endOffset,
context.irBuiltIns.unitType,
null,
forLoopInfo.declarations)
}
/**
* This loop
*
* for (i in first..last step foo) { ... }
*
* is represented in IR in such a manner:
*
* val it = (first..last step foo).iterator()
* while (it.hasNext()) {
* val i = it.next()
* ...
* }
*
* We transform it into the following loop:
*
* var it = first
* if (it <= last) { // (it >= last if the progression is decreasing)
* do {
* val i = it++
* ...
* } while (i != last)
* }
*
* In case of iteration over array we transform it into following:
* while (i <= array.size - 1) {
* val element = array[i]
* i++
* ...
* }
*/
// TODO: Lower `for (i in a until b)` to loop with precondition: for (i = a; i < b; a++);
override fun visitWhileLoop(loop: IrWhileLoop): IrExpression {
if (loop.origin != IrStatementOrigin.FOR_LOOP_INNER_WHILE) {
return super.visitWhileLoop(loop)
}
return with(context.createIrBuilder(getScopeOwnerSymbol(), loop.startOffset, loop.endOffset)) {
val newBody = loop.body?.transform(this@RangeLoopTransformer, null)?.let {
if (it is IrContainerExpression && !it.isTransparentScope) {
IrCompositeImpl(startOffset, endOffset, it.type, it.origin, it.statements)
} else {
it
}
}
val loopHeader = getLoopHeader(loop.condition)
?: return super.visitWhileLoop(loop)
val newLoop = loopHeader.buildBody(this, loop, newBody)
oldLoopToNewLoop[loop] = newLoop
// Build a check for an empty progression before the loop.
if (loopHeader is ProgressionLoopHeader) {
buildEmptinessCheck(newLoop, loopHeader)
} else {
newLoop
}
}
}
private fun DeclarationIrBuilder.buildMinValueCondition(forLoopHeader: ForLoopHeader): IrExpression {
// Condition for a corner case: for (i in a until Int.MIN_VALUE) {}.
// Check if forLoopHeader.bound > MIN_VALUE.
val progressionType = forLoopHeader.progressionType
val irBuiltIns = context.irBuiltIns
val minConst = when (progressionType) {
ProgressionType.INT_PROGRESSION -> IrConstImpl
.int(startOffset, endOffset, irBuiltIns.intType, Int.MIN_VALUE)
ProgressionType.CHAR_PROGRESSION -> IrConstImpl
.char(startOffset, endOffset, irBuiltIns.charType, 0.toChar())
ProgressionType.LONG_PROGRESSION -> IrConstImpl
.long(startOffset, endOffset, irBuiltIns.longType, Long.MIN_VALUE)
}
val compareTo = symbols.getBinaryOperator(OperatorNameConventions.COMPARE_TO,
forLoopHeader.bound.type.toKotlinType(),
minConst.type.toKotlinType())
return irCall(irBuiltIns.greaterFunByOperandType[irBuiltIns.int]!!).apply {
val compareToCall = irCall(compareTo).apply {
dispatchReceiver = irGet(forLoopHeader.bound)
putValueArgument(0, minConst)
}
putValueArgument(0, compareToCall)
putValueArgument(1, irInt(0))
}
}
private fun DeclarationIrBuilder.buildEmptinessCheck(loop: IrLoop, loopHeader: ProgressionLoopHeader): IrExpression {
val builtIns = context.irBuiltIns
val comparingBuiltIn = loopHeader.comparingFunction(builtIns)
// Check if inductionVariable <= last (or >= in case of downTo).
val compareTo = symbols.getBinaryOperator(OperatorNameConventions.COMPARE_TO,
loopHeader.inductionVariable.type.toKotlinType(),
loopHeader.last.type.toKotlinType())
val check = irCall(comparingBuiltIn as IrFunctionSymbol).apply {
putValueArgument(0, irCallOp(compareTo.owner, irGet(loopHeader.inductionVariable), irGet(loopHeader.last)))
putValueArgument(1, irInt(0))
}
// Process closed and open ranges in different manners.
return if (loopHeader.closed) {
irIfThen(check, loop) // if (inductionVariable <= last) { loop }
} else {
// Take into account a corner case: for (i in a until Int.MIN_VALUE) {}.
// if (inductionVariable <= last && bound > MIN_VALUE) { loop }
irIfThen(check, irIfThen(buildMinValueCondition(loopHeader), loop))
}
}
private fun getLoopHeader(oldCondition: IrExpression): ForLoopHeader? {
if (oldCondition !is IrCall || oldCondition.origin != IrStatementOrigin.FOR_LOOP_HAS_NEXT) {
return null
}
val irIteratorAccess = oldCondition.dispatchReceiver as? IrGetValue
?: throw AssertionError()
// Return null if we didn't lower the corresponding header.
return iteratorToLoopHeader[irIteratorAccess.symbol]
}
// Lower getting a next induction variable value.
fun processNext(variable: IrVariable): IrExpression? {
val initializer = variable.initializer as IrCall
val iterator = initializer.dispatchReceiver as? IrGetValue
?: throw AssertionError()
val forLoopInfo = iteratorToLoopHeader[iterator.symbol]
?: return null // If we didn't lower a corresponding header.
val plusOperator = symbols.getBinaryOperator(
OperatorNameConventions.PLUS,
forLoopInfo.inductionVariable.type.toKotlinType(),
forLoopInfo.step.type.toKotlinType()
)
if (forLoopInfo is ProgressionLoopHeader) {
forLoopInfo.loopVariable = variable
}
return with(context.createIrBuilder(getScopeOwnerSymbol(), initializer.startOffset, initializer.endOffset)) {
variable.initializer = forLoopInfo.initializeLoopVariable(symbols, this)
val increment = irSetVar(forLoopInfo.inductionVariable, irCallOp(plusOperator.owner,
irGet(forLoopInfo.inductionVariable),
irGet(forLoopInfo.step)))
IrCompositeImpl(variable.startOffset,
variable.endOffset,
context.irBuiltIns.unitType,
IrStatementOrigin.FOR_LOOP_NEXT,
listOf(variable, increment))
}
}
}
@@ -1,110 +0,0 @@
package org.jetbrains.kotlin.backend.konan.lower.loops
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.ir.isSubtypeOf
import org.jetbrains.kotlin.backend.konan.lower.matchers.IrCallMatcher
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
enum class ProgressionType(val numberCastFunctionName: Name) {
INT_PROGRESSION(Name.identifier("toInt")),
LONG_PROGRESSION(Name.identifier("toLong")),
CHAR_PROGRESSION(Name.identifier("toChar"));
}
// Information about loop that is required by HeaderProcessor to build ForLoopHeader
internal sealed class HeaderInfo(
val progressionType: ProgressionType,
val lowerBound: IrExpression,
val upperBound: IrExpression,
val step: IrExpression?, // null value denotes default step (1)
val increasing: Boolean,
val needLastCalculation: Boolean,
val closed: Boolean)
internal class ProgressionHeaderInfo(
progressionType: ProgressionType,
lowerBound: IrExpression,
upperBound: IrExpression,
step: IrExpression? = null,
increasing: Boolean = true,
needLastCalculation: Boolean = false,
closed: Boolean = true
) : HeaderInfo(progressionType, lowerBound, upperBound, step, increasing, needLastCalculation, closed)
internal class ArrayHeaderInfo(
lowerBound: IrExpression,
upperBound: IrExpression,
val arrayDeclaration: IrValueDeclaration
) : HeaderInfo(
ProgressionType.INT_PROGRESSION,
lowerBound,
upperBound,
step = null,
increasing = true,
needLastCalculation = false,
closed = false
)
internal interface HeaderInfoHandler<T> {
val matcher: IrCallMatcher
fun build(call: IrCall, data: T): HeaderInfo?
fun handle(irCall: IrCall, data: T) = if (matcher(irCall)) {
build(irCall, data)
} else {
null
}
}
internal typealias ProgressionHandler = HeaderInfoHandler<ProgressionType>
// We need to wrap builder into visitor because of `StepHandler` which has to visit it's subtree
// to get information about underlying progression.
private class ProgressionHeaderInfoBuilder(val context: Context) : IrElementVisitor<HeaderInfo?, Nothing?> {
private val symbols = context.ir.symbols
private val progressionElementClasses = symbols.integerClasses + symbols.char
private val progressionHandlers = listOf(
IndicesHandler(context),
UntilHandler(progressionElementClasses),
DownToHandler(progressionElementClasses),
StepHandler(context, this),
RangeToHandler(progressionElementClasses)
)
private fun IrType.getProgressionType(): ProgressionType? = when {
isSubtypeOf(symbols.charProgression.owner.defaultType) -> ProgressionType.CHAR_PROGRESSION
isSubtypeOf(symbols.intProgression.owner.defaultType) -> ProgressionType.INT_PROGRESSION
isSubtypeOf(symbols.longProgression.owner.defaultType) -> ProgressionType.LONG_PROGRESSION
else -> null
}
override fun visitElement(element: IrElement, data: Nothing?): HeaderInfo? = null
override fun visitCall(expression: IrCall, data: Nothing?): HeaderInfo? {
val progressionType = expression.type.getProgressionType()
?: return null
return progressionHandlers.firstNotNullResult { it.handle(expression, progressionType) }
}
}
internal class HeaderInfoBuilder(context: Context) {
private val progressionInfoBuilder = ProgressionHeaderInfoBuilder(context)
private val arrayIterationHandler = ArrayIterationHandler(context)
fun build(variable: IrVariable): HeaderInfo? {
val initializer = variable.initializer!! as IrCall
return arrayIterationHandler.handle(initializer, null)
?: initializer.dispatchReceiver?.accept(progressionInfoBuilder, null)
}
}
@@ -1,270 +0,0 @@
package org.jetbrains.kotlin.backend.konan.lower.loops
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.ir.isSubtypeOf
import org.jetbrains.kotlin.backend.konan.ir.typeWithStarProjections
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irImplicitCast
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.isSimpleTypeWithQuestionMark
import org.jetbrains.kotlin.util.OperatorNameConventions
/** Contains information about variables used in the loop. */
internal sealed class ForLoopHeader(
val inductionVariable: IrVariable,
val bound: IrVariable,
val last: IrVariable,
val step: IrVariable,
val progressionType: ProgressionType
) {
abstract fun initializeLoopVariable(symbols: KonanSymbols, builder: DeclarationIrBuilder): IrExpression
abstract val declarations: List<IrStatement>
abstract fun buildBody(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop
}
internal class ProgressionLoopHeader(
headerInfo: ProgressionHeaderInfo,
inductionVariable: IrVariable,
bound: IrVariable,
last: IrVariable,
step: IrVariable,
var loopVariable: IrVariable? = null
) : ForLoopHeader(inductionVariable, bound, last, step, headerInfo.progressionType) {
val closed = headerInfo.closed
private val increasing = headerInfo.increasing
fun comparingFunction(builtIns: IrBuiltIns) = if (increasing)
builtIns.lessOrEqualFunByOperandType[builtIns.int]
else
builtIns.greaterOrEqualFunByOperandType[builtIns.int]
override fun initializeLoopVariable(symbols: KonanSymbols, builder: DeclarationIrBuilder) = with(builder) {
irGet(inductionVariable)
}
override val declarations: List<IrStatement>
get() = listOf(inductionVariable, step, bound, last)
override fun buildBody(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with (builder) {
assert(loopVariable != null)
val newCondition = irCall(context.irBuiltIns.booleanNotSymbol).apply {
dispatchReceiver = irCall(context.irBuiltIns.eqeqSymbol).apply {
putValueArgument(0, irGet(loopVariable!!))
putValueArgument(1, irGet(last))
}
}
IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply {
label = loop.label
condition = newCondition
body = newBody
}
}
}
internal class ArrayLoopHeader(
headerInfo: ArrayHeaderInfo,
inductionVariable: IrVariable,
bound: IrVariable,
last: IrVariable,
step: IrVariable
) : ForLoopHeader(inductionVariable, bound, last, step, ProgressionType.INT_PROGRESSION) {
private val arrayDeclaration = headerInfo.arrayDeclaration
override fun initializeLoopVariable(symbols: KonanSymbols, builder: DeclarationIrBuilder) = with(builder) {
val callee = symbols.arrayGet[arrayDeclaration.type.classifierOrFail]!!
irCall(callee).apply {
dispatchReceiver = irGet(arrayDeclaration)
putValueArgument(0, irGet(inductionVariable))
}
}
override val declarations: List<IrStatement>
get() = listOf(arrayDeclaration, inductionVariable, step, bound, last)
override fun buildBody(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with (builder) {
val builtIns = context.irBuiltIns
val callee = builtIns.lessOrEqualFunByOperandType[builtIns.int]
val newCondition = irCall(callee as IrFunctionSymbol).apply {
putValueArgument(0, irGet(inductionVariable))
putValueArgument(1, irGet(last))
}
IrWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply {
label = loop.label
condition = newCondition
body = newBody
}
}
}
private fun ProgressionType.elementType(context: Context): IrType = when (this) {
ProgressionType.INT_PROGRESSION -> context.irBuiltIns.intType
ProgressionType.LONG_PROGRESSION -> context.irBuiltIns.longType
ProgressionType.CHAR_PROGRESSION -> context.irBuiltIns.charType
}
// Given the for loop iterator variable, extract information about iterable subject
// and create ForLoopHeader from it.
internal class HeaderProcessor(
private val context: Context,
private val headerInfoBuilder: HeaderInfoBuilder,
private val scopeOwnerSymbol: () -> IrSymbol) {
private val symbols = context.ir.symbols
private val progressionElementClasses = (symbols.integerClasses + symbols.char).toSet()
fun processHeader(variable: IrVariable): ForLoopHeader? {
assert(variable.origin == IrDeclarationOrigin.FOR_LOOP_ITERATOR)
val iteratorType = symbols.iterator.typeWithStarProjections
if (!variable.type.isSubtypeOf(iteratorType)) {
return null
}
val builder = context.createIrBuilder(scopeOwnerSymbol(), variable.startOffset, variable.endOffset)
// Collect loop info and form the loop header composite.
val progressionInfo = headerInfoBuilder.build(variable)
?: return null
if (progressionInfo is ArrayHeaderInfo) {
progressionInfo.arrayDeclaration.parent = variable.parent
}
with(builder) {
with(progressionInfo) {
/**
* For this loop:
* `for (i in a() .. b() step c() step d())`
* We need to call functions in the following order: a, b, c, d.
* So we call b() before step calculations and then call last element calculation function (if required).
*/
// 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 them to non-nullable.
val inductionVariable = scope.createTemporaryVariable(lowerBound.castIfNecessary(progressionType),
nameHint = "inductionVariable",
isMutable = true,
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE)
val upperBoundTmpVariable = scope.createTemporaryVariable(ensureNotNullable(upperBound.castIfNecessary(progressionType)),
nameHint = "upperBound",
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE)
val stepExpression = if (step != null) {
ensureNotNullable(if (increasing) step else step.unaryMinus())
} else {
defaultStep(startOffset, endOffset)
}
val stepValue = scope.createTemporaryVariable(stepExpression,
nameHint = "step",
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE)
// Calculate the last element of the progression
// The last element can be:
// boundValue, if step is 1 and the range is closed.
// boundValue - 1, if step is 1 and the range is open.
// getProgressionLast(inductionVariable, boundValue, step), if step != 1 and the range is closed.
// getProgressionLast(inductionVariable, boundValue - 1, step), if step != 1 and the range is open.
val lastExpression = if (closed) {
irGet(upperBoundTmpVariable)
} else {
val decrementSymbol = symbols.getUnaryOperator(OperatorNameConventions.DEC, upperBoundTmpVariable.type.toKotlinType())
irCall(decrementSymbol.owner).apply {
dispatchReceiver = irGet(upperBoundTmpVariable)
}
}
// In case of `step` we need to calculate the last element.
val lastElement = if (needLastCalculation) {
irGetProgressionLast(progressionType, inductionVariable, lastExpression, stepValue)
} else {
lastExpression
}
val lastValue = scope.createTemporaryVariable(lastElement,
nameHint = "last",
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE)
return when (progressionInfo) {
is ArrayHeaderInfo -> ArrayLoopHeader(
progressionInfo,
inductionVariable,
upperBoundTmpVariable,
lastValue,
stepValue)
is ProgressionHeaderInfo -> ProgressionLoopHeader(
progressionInfo,
inductionVariable,
upperBoundTmpVariable,
lastValue,
stepValue)
}
}
}
}
private fun IrExpression.castIfNecessary(progressionType: ProgressionType): IrExpression {
assert(type.classifierOrNull in progressionElementClasses)
return if (type.classifierOrNull == progressionType.elementType(context).classifierOrNull) {
this
} else {
val function = symbols.getNoParamFunction(progressionType.numberCastFunctionName, type.toKotlinType())
IrCallImpl(startOffset, endOffset, function.owner.returnType, function)
.apply { dispatchReceiver = this@castIfNecessary }
}
}
private fun DeclarationIrBuilder.ensureNotNullable(expression: IrExpression) =
if (expression.type.isSimpleTypeWithQuestionMark) {
irImplicitCast(expression, expression.type.makeNotNull())
} else {
expression
}
private fun IrExpression.unaryMinus(): IrExpression {
val unaryOperator = symbols.getUnaryOperator(OperatorNameConventions.UNARY_MINUS, type.toKotlinType())
return IrCallImpl(startOffset, endOffset, unaryOperator.owner.returnType, unaryOperator).apply {
dispatchReceiver = this@unaryMinus
}
}
private fun HeaderInfo.defaultStep(startOffset: Int, endOffset: Int): IrExpression {
val type = progressionType.elementType(context)
val step = if (increasing) 1 else -1
return when {
type.isInt() || type.isChar() ->
IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, step)
type.isLong() ->
IrConstImpl.long(startOffset, endOffset, context.irBuiltIns.longType, step.toLong())
else -> throw IllegalArgumentException()
}
}
private fun irGetProgressionLast(progressionType: ProgressionType,
first: IrVariable,
lastExpression: IrExpression,
step: IrVariable): IrExpression {
val symbol = symbols.getProgressionLast[progressionType.elementType(context).toKotlinType()]
?: throw IllegalArgumentException("No `getProgressionLast` for type ${step.type} ${lastExpression.type}")
val startOffset = lastExpression.startOffset
val endOffset = lastExpression.endOffset
return IrCallImpl(startOffset, endOffset, symbol.owner.returnType, symbol).apply {
putValueArgument(0, IrGetValueImpl(startOffset, endOffset, first.type, first.symbol))
putValueArgument(1, lastExpression)
putValueArgument(2, IrGetValueImpl(startOffset, endOffset, step.type, step.symbol))
}
}
}
@@ -1,185 +0,0 @@
package org.jetbrains.kotlin.backend.konan.lower.loops
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.lower.matchers.IrCallMatcher
import org.jetbrains.kotlin.backend.konan.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.backend.konan.lower.matchers.createIrCallMatcher
import org.jetbrains.kotlin.backend.konan.lower.matchers.singleArgumentExtension
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
// TODO: What if functions like Int.rangeTo(Char) will be added to stdlib later?
internal class RangeToHandler(val progressionElementClasses: Collection<IrClassSymbol>) : ProgressionHandler {
override val matcher = SimpleCalleeMatcher {
dispatchReceiver { it != null && it.type.classifierOrNull in progressionElementClasses }
fqName { it.pathSegments().last() == Name.identifier("rangeTo") }
parameterCount { it == 1 }
parameter(0) { it.type.classifierOrNull in progressionElementClasses }
}
override fun build(call: IrCall, data: ProgressionType) =
ProgressionHeaderInfo(data, call.dispatchReceiver!!, call.getValueArgument(0)!!)
}
internal class DownToHandler(val progressionElementClasses: Collection<IrClassSymbol>) : ProgressionHandler {
override val matcher = SimpleCalleeMatcher {
singleArgumentExtension(FqName("kotlin.ranges.downTo"), progressionElementClasses)
parameter(0) { it.type.classifierOrNull in progressionElementClasses }
}
override fun build(call: IrCall, data: ProgressionType): HeaderInfo? =
ProgressionHeaderInfo(data,
call.extensionReceiver!!,
call.getValueArgument(0)!!,
increasing = false)
}
internal class UntilHandler(val progressionElementClasses: Collection<IrClassSymbol>) : ProgressionHandler {
override val matcher = SimpleCalleeMatcher {
singleArgumentExtension(FqName("kotlin.ranges.until"), progressionElementClasses)
parameter(0) { it.type.classifierOrNull in progressionElementClasses }
}
override fun build(call: IrCall, data: ProgressionType): HeaderInfo? =
ProgressionHeaderInfo(data,
call.extensionReceiver!!,
call.getValueArgument(0)!!,
closed = false)
}
internal class IndicesHandler(val context: Context) : ProgressionHandler {
private val symbols = context.ir.symbols
private val supportedArrays = symbols.primitiveArrays.values + symbols.array
override val matcher = createIrCallMatcher {
callee {
fqName { it == FqName("kotlin.collections.<get-indices>") }
parameterCount { it == 0 }
}
extensionReceiver { it != null && it.type.classifierOrNull in supportedArrays }
}
override fun build(call: IrCall, data: ProgressionType): HeaderInfo? =
with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) {
val lowerBound = irInt(0)
val clazz = call.extensionReceiver!!.type.classifierOrFail
val symbol = symbols.arraySize[clazz]!!
val upperBound = irCall(symbol).apply {
dispatchReceiver = call.extensionReceiver
}
ProgressionHeaderInfo(data, lowerBound, upperBound, closed = false)
}
}
internal class StepHandler(context: Context, val visitor: IrElementVisitor<HeaderInfo?, Nothing?>) : ProgressionHandler {
private val symbols = context.ir.symbols
override val matcher: IrCallMatcher = SimpleCalleeMatcher {
singleArgumentExtension(FqName("kotlin.ranges.step"), symbols.progressionClasses)
parameter(0) { it.type.isInt() || it.type.isLong() }
}
private fun isDefaultStep(step: IrExpression?) =
step == null || (step is IrConst<*> && step.isOne())
override fun build(call: IrCall, data: ProgressionType): HeaderInfo? {
val nestedInfo = call.extensionReceiver!!.accept(visitor, null)
?: return null
// Due to KT-27607 nested non-default steps could lead to incorrect behaviour.
// So disable optimization of such rare cases for now.
if (!isDefaultStep(nestedInfo.step)) {
return null
}
val newStep = call.getValueArgument(0)!!
val (newStepCheck, needBoundCalculation) = irCheckProgressionStep(symbols, data, newStep)
return ProgressionHeaderInfo(
data,
nestedInfo.lowerBound,
nestedInfo.upperBound,
newStepCheck,
nestedInfo.increasing,
needBoundCalculation,
nestedInfo.closed)
}
private fun IrConst<*>.isOne() = when (kind) {
IrConstKind.Long -> value as Long == 1L
IrConstKind.Int -> value as Int == 1
else -> false
}
private fun IrExpression.isPositiveConst() = this is IrConst<*> &&
((kind == IrConstKind.Long && value as Long > 0) || (kind == IrConstKind.Int && value as Int > 0))
// Used only by the assert.
private fun stepHasRightType(step: IrExpression, progressionType: ProgressionType) = when (progressionType) {
ProgressionType.CHAR_PROGRESSION,
ProgressionType.INT_PROGRESSION -> step.type.makeNotNull().isInt()
ProgressionType.LONG_PROGRESSION -> step.type.makeNotNull().isLong()
}
private fun irCheckProgressionStep(symbols: KonanSymbols, progressionType: ProgressionType, step: IrExpression) =
if (step.isPositiveConst()) {
step to !(step as IrConst<*>).isOne()
} else {
// The frontend checks if the step has a right type (Long for LongProgression and Int for {Int/Char}Progression)
// so there is no need to cast it.
assert(stepHasRightType(step, progressionType))
val symbol = symbols.checkProgressionStep[step.type.makeNotNull().toKotlinType()]
?: throw IllegalArgumentException("No `checkProgressionStep` for type ${step.type}")
IrCallImpl(step.startOffset, step.endOffset, symbol.owner.returnType, symbol).apply {
putValueArgument(0, step)
} to true
}
}
internal class ArrayIterationHandler(val context: Context) : HeaderInfoHandler<Nothing?> {
private val symbols = context.ir.symbols
private val supportedArrays = symbols.arrays
// No support for rare cases like `T : IntArray` for now.
override val matcher = createIrCallMatcher {
origin { it == IrStatementOrigin.FOR_LOOP_ITERATOR }
dispatchReceiver { it != null && (it.type.classifierOrNull in supportedArrays) }
}
// Consider case like `for (elem in A) { f(elem) }`
// If we lower it to `for (i in A.indices) { f(A[i]) }`
// Then we will break program behaviour if A is an expression with side-effect.
// Instead, we lower it to
// ```
// val a = A
// for (i in a.indices) { f(a[i]) }
// ```
override fun build(call: IrCall, data: Nothing?): HeaderInfo? =
with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) {
val arrayReference = scope.createTemporaryVariable(call.dispatchReceiver!!)
val clazz = arrayReference.type.classifierOrFail
val arraySizeSymbol = symbols.arraySize[clazz]!!
val upperBound = irCall(arraySizeSymbol).apply {
dispatchReceiver = irGet(arrayReference)
}
ArrayHeaderInfo(irInt(0), upperBound, arrayReference)
}
}