diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt index 73384921736..b8b6261fad7 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt @@ -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 diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/loops/ForLoopsLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/loops/ForLoopsLowering.kt deleted file mode 100644 index 18444ba2743..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/loops/ForLoopsLowering.kt +++ /dev/null @@ -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() - 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, - headerInfoBuilder: HeaderInfoBuilder) - : IrElementTransformerVoidWithContext() { - - private val symbols = context.ir.symbols - private val iteratorToLoopHeader = mutableMapOf() - 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)) - } - } -} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/loops/HeaderInfo.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/loops/HeaderInfo.kt deleted file mode 100644 index 520bd5b343f..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/loops/HeaderInfo.kt +++ /dev/null @@ -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 { - 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 - -// 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 { - - 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) - } -} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/loops/HeaderProcessor.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/loops/HeaderProcessor.kt deleted file mode 100644 index c690c58a0dc..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/loops/HeaderProcessor.kt +++ /dev/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 - - 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 - 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 - 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)) - } - } -} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/loops/ProgressionHandlers.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/loops/ProgressionHandlers.kt deleted file mode 100644 index 9c9cc84eaa1..00000000000 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/loops/ProgressionHandlers.kt +++ /dev/null @@ -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) : 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) : 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) : 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.") } - 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) : 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 { - - 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) - } -} - -