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:
committed by
Nikolay Igotti
parent
3dc0e66f7d
commit
e240621550
+1
-1
@@ -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
|
||||
|
||||
|
||||
-233
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
-110
@@ -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)
|
||||
}
|
||||
}
|
||||
-270
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
-185
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user