backend: Don't create a Progression objects in 'for' loops
This patch optimizes the following pattern:
for (i in first..last step st) { ... }
In this case we need to create a Progression object and then call its
iterator() method causing at least 2 allocation per loop. This change
replaces such loops with the following constuction:
var inductionVar = first
checkProgressionStep(step) // check if step > 0
last = getProgressionLastElement(first, last, step)
if (first <= last) {
do {
i = inductionVar
inductionVar += step
...
} while(i != last)
}
This commit is contained in:
+47
-2
@@ -1,7 +1,6 @@
|
||||
package org.jetbrains.kotlin.backend.common.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.replace
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
@@ -9,11 +8,13 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
// This is what Context collects about IR.
|
||||
@@ -61,17 +62,42 @@ open class Symbols<out T: CommonBackendContext>(val context: T, private val symb
|
||||
) as ClassDescriptor
|
||||
)
|
||||
|
||||
val iterator = symbolTable.referenceClass(
|
||||
builtInsPackage("kotlin", "collections").getContributedClassifier(
|
||||
Name.identifier("Iterator"), NoLookupLocation.FROM_BACKEND
|
||||
) as ClassDescriptor)
|
||||
|
||||
private fun progression(name: String) = symbolTable.referenceClass(
|
||||
builtInsPackage("kotlin", "ranges").getContributedClassifier(
|
||||
Name.identifier(name), NoLookupLocation.FROM_BACKEND
|
||||
) as ClassDescriptor
|
||||
)
|
||||
|
||||
val charProgression = progression("CharProgression")
|
||||
val intProgression = progression("IntProgression")
|
||||
val longProgression = progression("LongProgression")
|
||||
val progressionClasses = listOf(charProgression, intProgression, longProgression)
|
||||
val progressionClassesTypes = progressionClasses.map { it.descriptor.defaultType }.toSet()
|
||||
|
||||
val checkProgressionStep = context.getInternalFunctions("checkProgressionStep")
|
||||
.map { Pair(it.returnType, symbolTable.referenceSimpleFunction(it)) }.toMap()
|
||||
val getProgressionBound = context.getInternalFunctions("getProgressionBound")
|
||||
.map { Pair(it.returnType, symbolTable.referenceSimpleFunction(it)) }.toMap()
|
||||
|
||||
val defaultConstructorMarker = symbolTable.referenceClass(context.getInternalClass("DefaultConstructorMarker"))
|
||||
|
||||
val any = symbolTable.referenceClass(builtIns.any)
|
||||
val unit = symbolTable.referenceClass(builtIns.unit)
|
||||
|
||||
val char = symbolTable.referenceClass(builtIns.char)
|
||||
|
||||
val byte = symbolTable.referenceClass(builtIns.byte)
|
||||
val short = symbolTable.referenceClass(builtIns.short)
|
||||
val int = symbolTable.referenceClass(builtIns.int)
|
||||
val long = symbolTable.referenceClass(builtIns.long)
|
||||
|
||||
val integerClasses = listOf(byte, short, int, long)
|
||||
val integerClassesTypes = integerClasses.map { it.descriptor.defaultType }
|
||||
|
||||
val arrayOf = symbolTable.referenceSimpleFunction(
|
||||
builtInsPackage("kotlin").getContributedFunctions(
|
||||
@@ -143,6 +169,8 @@ open class Symbols<out T: CommonBackendContext>(val context: T, private val symb
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
val valuesForEnum = symbolTable.referenceSimpleFunction(
|
||||
context.getInternalFunctions("valuesForEnum").single())
|
||||
|
||||
@@ -171,4 +199,21 @@ open class Symbols<out T: CommonBackendContext>(val context: T, private val symb
|
||||
val kLocalDelegatedPropertyImpl = symbolTable.referenceClass(context.reflectionTypes.kLocalDelegatedPropertyImpl)
|
||||
val kLocalDelegatedMutablePropertyImpl = symbolTable.referenceClass(context.reflectionTypes.kLocalDelegatedMutablePropertyImpl)
|
||||
|
||||
fun getFunction(name: Name, receiverType: KotlinType, vararg argTypes: KotlinType) =
|
||||
symbolTable.referenceFunction(receiverType.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND)
|
||||
.single {
|
||||
var i = 0
|
||||
it.valueParameters.size == argTypes.size && it.valueParameters.all { type -> type == argTypes[i++] }
|
||||
}
|
||||
)
|
||||
|
||||
fun getBinaryOperator(name: Name, lhsType: KotlinType, rhsType: KotlinType) =
|
||||
symbolTable.referenceFunction(lhsType.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND)
|
||||
.single { it.valueParameters.size == 1 && it.valueParameters[0].type == rhsType }
|
||||
)
|
||||
|
||||
fun getUnaryOperator(name: Name, receiverType: KotlinType) =
|
||||
symbolTable.referenceFunction(receiverType.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND)
|
||||
.single { it.valueParameters.isEmpty() }
|
||||
)
|
||||
}
|
||||
+3
@@ -66,6 +66,9 @@ internal class KonanLower(val context: Context) {
|
||||
phaser.phase(KonanPhase.LOWER_DATA_CLASSES) {
|
||||
DataClassOperatorsLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_FOR_LOOPS) {
|
||||
ForLoopsLowering(context).lower(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_ENUMS) {
|
||||
EnumClassLowering(context).run(irFile)
|
||||
}
|
||||
|
||||
+1
@@ -32,6 +32,7 @@ enum class KonanPhase(val description: String,
|
||||
/* ... ... */ LOWER_INLINE("Functions inlining", LOWER_INLINE_CONSTRUCTORS),
|
||||
/* ... ... ... */ DESERIALIZER("Deserialize inline bodies"),
|
||||
/* ... ... */ LOWER_INTEROP_PART1("Interop lowering, part 1", LOWER_INLINE),
|
||||
/* ... ... */ LOWER_FOR_LOOPS("For loops lowering"),
|
||||
/* ... ... */ LOWER_ENUMS("Enum classes lowering"),
|
||||
/* ... ... */ LOWER_DELEGATION("Delegation lowering"),
|
||||
/* ... ... */ LOWER_INITIALIZERS("Initializers lowering", LOWER_ENUMS),
|
||||
|
||||
+414
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.lower
|
||||
|
||||
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.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
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.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
|
||||
import org.jetbrains.kotlin.ir.visitors.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isBoolean
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
/** This lowering pass optimizes range-based for loops. */
|
||||
internal class ForLoopsLowering(val context: Context) : FileLoweringPass {
|
||||
override fun lower(irFile: IrFile) {
|
||||
val transformer = ForLoopsTransformer(context)
|
||||
// Lower loops
|
||||
irFile.transformChildrenVoid(transformer)
|
||||
|
||||
// Update references in break/continue.
|
||||
irFile.transformChildrenVoid(object: IrElementTransformerVoid() {
|
||||
override fun visitBreakContinue(jump: IrBreakContinue): IrExpression {
|
||||
transformer.oldLoopToNewLoop[jump.loop]?.let { jump.loop = it }
|
||||
return jump
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private class ForLoopsTransformer(val context: Context) : IrElementTransformerVoidWithContext() {
|
||||
|
||||
private val symbols = context.ir.symbols
|
||||
private val iteratorToLoopInfo = mutableMapOf<IrVariableSymbol, ForLoopInfo>()
|
||||
internal val oldLoopToNewLoop = mutableMapOf<IrLoop, IrLoop>()
|
||||
|
||||
private val iteratorType = symbols.iterator.descriptor.defaultType.replaceArgumentsWithStarProjections()
|
||||
|
||||
private val scopeOwnerSymbol
|
||||
get() = currentScope!!.scope.scopeOwnerSymbol
|
||||
|
||||
private fun irConstOne(startOffset: Int, endOffset: Int) =
|
||||
IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, 1)
|
||||
|
||||
private fun irConstMinusOne(startOffset: Int, endOffset: Int) =
|
||||
IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, -1)
|
||||
|
||||
private val progressionElementClasses: Set<IrClassSymbol> = mutableSetOf(symbols.char).apply {
|
||||
addAll(symbols.integerClasses)
|
||||
}
|
||||
|
||||
private val progressionElementClassesTypes: Set<SimpleType> = mutableSetOf<SimpleType>().apply {
|
||||
progressionElementClasses.mapTo(this) { it.descriptor.defaultType }
|
||||
}
|
||||
|
||||
// Symbols for progression building functions ======================================================================
|
||||
private fun getProgressionBuildingMethods(name: String): Set<IrFunctionSymbol> =
|
||||
getMethodsForProgressionElements(name) {
|
||||
it.valueParameters.size == 1 &&
|
||||
it.valueParameters[0].type in progressionElementClassesTypes
|
||||
}
|
||||
|
||||
private fun getProgressionBuildingExtensions(name: String, pkg: FqName): Set<IrFunctionSymbol> =
|
||||
getExtensionsForProgressionElements(name, pkg) {
|
||||
it.extensionReceiverParameter?.type in progressionElementClassesTypes &&
|
||||
it.valueParameters.size == 1 &&
|
||||
it.valueParameters[0].type in progressionElementClassesTypes
|
||||
}
|
||||
|
||||
private fun getMethodsForProgressionElements(name: String,
|
||||
filter: (SimpleFunctionDescriptor) -> Boolean): Set<IrFunctionSymbol> =
|
||||
mutableSetOf<IrFunctionSymbol>().apply {
|
||||
progressionElementClasses.flatMapTo(this) { receiver ->
|
||||
receiver.descriptor.unsubstitutedMemberScope
|
||||
.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND)
|
||||
.filter(filter).map { symbols.symbolTable.referenceFunction(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getExtensionsForProgressionElements(name: String,
|
||||
pkg: FqName,
|
||||
filter: (SimpleFunctionDescriptor) -> Boolean): Set<IrFunctionSymbol> =
|
||||
mutableSetOf<IrFunctionSymbol>().apply {
|
||||
progressionElementClasses.flatMapTo(this) { receiver ->
|
||||
context.builtIns.builtInsModule.getPackage(pkg).memberScope
|
||||
.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND)
|
||||
.filter(filter).map { symbols.symbolTable.referenceFunction(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private val rangeToSymbols by lazy { getProgressionBuildingMethods("rangeTo") }
|
||||
private val untilSymbols by lazy { getProgressionBuildingExtensions("until", FqName("kotlin.ranges")) }
|
||||
private val downToSymbols by lazy { getProgressionBuildingExtensions("downTo", FqName("kotlin.ranges")) }
|
||||
private val stepSymbols by lazy {
|
||||
getExtensionsForProgressionElements("step", FqName("kotlin.ranges")) {
|
||||
it.extensionReceiverParameter?.type in symbols.progressionClassesTypes &&
|
||||
it.valueParameters.size == 1 &&
|
||||
(KotlinBuiltIns.isLong(it.valueParameters[0].type) || KotlinBuiltIns.isInt(it.valueParameters[0].type))
|
||||
}
|
||||
}
|
||||
|
||||
//region Util methods ==============================================================================================
|
||||
private fun IrExpression.castIfNecessary(progressionType: ProgressionType, castToChar: Boolean = true): IrExpression {
|
||||
assert(type in progressionElementClassesTypes)
|
||||
if (type == progressionType.elementType || (!castToChar && KotlinBuiltIns.isChar(progressionType.elementType))) {
|
||||
return this
|
||||
}
|
||||
return IrCallImpl(startOffset, endOffset, symbols.getFunction(progressionType.numberCastFunctionName, type))
|
||||
.apply { dispatchReceiver = this@castIfNecessary }
|
||||
}
|
||||
|
||||
private fun IrExpression.unaryMinus(): IrExpression =
|
||||
IrCallImpl(startOffset, endOffset, symbols.getUnaryOperator(OperatorNameConventions.UNARY_MINUS, type)).apply {
|
||||
dispatchReceiver = this@unaryMinus
|
||||
}
|
||||
|
||||
private fun irCheckProgressionStep(progressionType: ProgressionType,
|
||||
step: IrExpression): IrExpression {
|
||||
if (step is IrConst<*> &&
|
||||
((step.kind == IrConstKind.Long && step.value as Long > 0) ||
|
||||
(step.kind == IrConstKind.Int && step.value as Int > 0))) {
|
||||
return step
|
||||
}
|
||||
val castedStep = step.castIfNecessary(progressionType, false)
|
||||
val symbol = symbols.checkProgressionStep[castedStep.type]
|
||||
?: throw IllegalArgumentException("Unknown progression element type: ${step.type}")
|
||||
return IrCallImpl(step.startOffset, step.endOffset, symbol).apply {
|
||||
putValueArgument(0, castedStep)
|
||||
}
|
||||
}
|
||||
|
||||
private fun irGetProgressionBound(progressionType: ProgressionType,
|
||||
first: IrVariableSymbol,
|
||||
lastExpression: IrExpression,
|
||||
step: IrVariableSymbol): IrExpression {
|
||||
val symbol = symbols.getProgressionBound[progressionType.elementType]
|
||||
?: throw IllegalArgumentException("Unknown progression element type: ${lastExpression.type}")
|
||||
val startOffset = lastExpression.startOffset
|
||||
val endOffset = lastExpression.endOffset
|
||||
return IrCallImpl(startOffset, lastExpression.endOffset, symbol).apply {
|
||||
putValueArgument(0, IrGetValueImpl(startOffset, endOffset, first))
|
||||
putValueArgument(1, lastExpression.castIfNecessary(progressionType))
|
||||
putValueArgument(2, IrGetValueImpl(startOffset, endOffset, step))
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region Util classes ====================================================================================================
|
||||
// TODO: Replace with a cast when such support is added in the boxing lowering.
|
||||
private data class ProgressionType(val elementType: KotlinType,
|
||||
val numberCastFunctionName: Name)
|
||||
|
||||
private data class ProgressionInfo(
|
||||
val progressionType: ProgressionType,
|
||||
val first: IrExpression,
|
||||
val last: IrExpression,
|
||||
val step: IrExpression? = null,
|
||||
val increasing: Boolean = true)
|
||||
|
||||
/** Contains information about variables used in the loop. */
|
||||
private data class ForLoopInfo(
|
||||
val progressionInfo: ProgressionInfo,
|
||||
val inductionVariable: IrVariableSymbol,
|
||||
val bound: IrVariableSymbol,
|
||||
val step: IrVariableSymbol,
|
||||
var loopVariable: IrVariableSymbol? = null)
|
||||
|
||||
private inner class ProgressionInfoBuilder : IrElementVisitor<ProgressionInfo?, Nothing?> {
|
||||
|
||||
val INT_PROGRESSION = ProgressionType(context.builtIns.intType, Name.identifier("toInt"))
|
||||
val LONG_PROGRESSION = ProgressionType(context.builtIns.longType, Name.identifier("toLong"))
|
||||
val CHAR_PROGRESSION = ProgressionType(context.builtIns.charType, Name.identifier("toChar"))
|
||||
|
||||
private fun buildRangeTo(expression: IrCall, progressionType: ProgressionType) =
|
||||
ProgressionInfo(progressionType, expression.dispatchReceiver!!, expression.getValueArgument(0)!!)
|
||||
|
||||
private fun buildUntil(expression: IrCall, progressionType: ProgressionType): ProgressionInfo {
|
||||
val firstExpression = expression.extensionReceiver!!
|
||||
val lastExpression = expression.getValueArgument(0)!!
|
||||
val decrementSymbol = symbols.getUnaryOperator(OperatorNameConventions.DEC, lastExpression.type)
|
||||
val decrementCall = IrCallImpl(expression.startOffset, expression.endOffset, decrementSymbol).apply {
|
||||
dispatchReceiver = lastExpression
|
||||
}
|
||||
return ProgressionInfo(progressionType, firstExpression, decrementCall)
|
||||
}
|
||||
|
||||
private fun buildDownTo(expression: IrCall, progressionType: ProgressionType) =
|
||||
ProgressionInfo(progressionType, expression.extensionReceiver!!, expression.getValueArgument(0)!!, increasing = false)
|
||||
|
||||
private fun buildStep(expression: IrCall, progressionType: ProgressionType) =
|
||||
expression.extensionReceiver!!.accept(this, null)?.let {
|
||||
val newStep = expression.getValueArgument(0)!!
|
||||
val step = when {
|
||||
it.step == null -> irCheckProgressionStep(progressionType, newStep)
|
||||
// There were step calls before. Just add our check in the container or create a new one.
|
||||
it.step is IrStatementContainer -> {
|
||||
it.step.statements.add(irCheckProgressionStep(progressionType, newStep)); it.step
|
||||
}
|
||||
else -> IrCompositeImpl(expression.startOffset, expression.endOffset, newStep.type).apply {
|
||||
statements.add(it.step)
|
||||
statements.add(irCheckProgressionStep(progressionType, newStep))
|
||||
}
|
||||
}
|
||||
ProgressionInfo(progressionType, it.first, it.last, step, it.increasing)
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement, data: Nothing?): ProgressionInfo? = null
|
||||
|
||||
override fun visitCall(expression: IrCall, data: Nothing?): ProgressionInfo? {
|
||||
val type = expression.type
|
||||
val progressionType = when {
|
||||
type.isSubtypeOf(symbols.charProgression.descriptor.defaultType) -> CHAR_PROGRESSION
|
||||
type.isSubtypeOf(symbols.intProgression.descriptor.defaultType) -> INT_PROGRESSION
|
||||
type.isSubtypeOf(symbols.longProgression.descriptor.defaultType) -> LONG_PROGRESSION
|
||||
else -> return null
|
||||
}
|
||||
|
||||
// TODO: Process constructors and other factory functions.
|
||||
return when (expression.symbol) {
|
||||
in rangeToSymbols -> buildRangeTo(expression, progressionType)
|
||||
in untilSymbols -> buildUntil(expression, progressionType)
|
||||
in downToSymbols -> buildDownTo(expression, progressionType)
|
||||
in stepSymbols -> buildStep(expression, progressionType)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
// Lowering ========================================================================================================
|
||||
// Lower a loop header.
|
||||
private fun processHeader(variable: IrVariable, initializer: IrCall): IrStatement? {
|
||||
val symbol = variable.symbol
|
||||
if (!variable.descriptor.type.isSubtypeOf(iteratorType)) {
|
||||
return null
|
||||
}
|
||||
assert(symbol !in iteratorToLoopInfo)
|
||||
|
||||
val builder = context.createIrBuilder(scopeOwnerSymbol, variable.startOffset, variable.endOffset)
|
||||
// Collect loop info and form the loop header block.
|
||||
val progressionInfo = initializer.dispatchReceiver?.accept(ProgressionInfoBuilder(), null) ?: return null
|
||||
return builder.irBlock {
|
||||
with(progressionInfo) {
|
||||
val inductionVariable = irTemporaryVar(first.castIfNecessary(progressionType), "inductionVariable")
|
||||
val stepExpression = if (increasing) { // TODO: Remove the casts.
|
||||
step ?: irConstOne(startOffset, endOffset).castIfNecessary(progressionType, false)
|
||||
} else {
|
||||
step?.unaryMinus() ?: irConstMinusOne(startOffset, endOffset).castIfNecessary(progressionType, false)
|
||||
}
|
||||
val stepValue = irTemporary(stepExpression, "step")
|
||||
// TODO: Don't call the check it step is 1 or -1
|
||||
val boundExpression = irGetProgressionBound(progressionType, inductionVariable.symbol, last, stepValue.symbol)
|
||||
val boundValue = irTemporary(boundExpression, "bound")
|
||||
iteratorToLoopInfo[symbol] = ForLoopInfo(progressionInfo,
|
||||
inductionVariable.symbol,
|
||||
boundValue.symbol,
|
||||
stepValue.symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Lower getting a next induction variable value.
|
||||
private fun processNext(variable: IrVariable, initializer: IrCall): IrExpression? {
|
||||
val irIteratorAccess = initializer.dispatchReceiver as? IrGetValue ?: throw AssertionError()
|
||||
val forLoopInfo = iteratorToLoopInfo[irIteratorAccess.symbol] ?: return null // If we didn't lower a corresponding header.
|
||||
val builder = context.createIrBuilder(scopeOwnerSymbol, initializer.startOffset, initializer.endOffset)
|
||||
|
||||
// TODO: Cache it.
|
||||
val plusOperator = symbols.getBinaryOperator(
|
||||
OperatorNameConventions.PLUS,
|
||||
forLoopInfo.inductionVariable.descriptor.type,
|
||||
forLoopInfo.step.descriptor.type
|
||||
)
|
||||
forLoopInfo.loopVariable = variable.symbol
|
||||
|
||||
with(builder) {
|
||||
variable.initializer = irGet(forLoopInfo.inductionVariable)
|
||||
val increment = irSetVar(forLoopInfo.inductionVariable,
|
||||
irCallOp(plusOperator, irGet(forLoopInfo.inductionVariable), irGet(forLoopInfo.step)))
|
||||
return IrCompositeImpl(variable.startOffset,
|
||||
variable.endOffset,
|
||||
context.irBuiltIns.unit,
|
||||
IrStatementOrigin.FOR_LOOP_NEXT,
|
||||
listOf(variable, increment))
|
||||
}
|
||||
}
|
||||
|
||||
private fun DeclarationIrBuilder.buildEmptyCheck(loop: IrLoop, forLoopInfo: ForLoopInfo): IrExpression {
|
||||
val increasing = forLoopInfo.progressionInfo.increasing
|
||||
val comparingBuiltIn = if (increasing) context.irBuiltIns.lteq0Symbol else context.irBuiltIns.gteq0Symbol
|
||||
|
||||
val compareTo = symbols.getBinaryOperator(OperatorNameConventions.COMPARE_TO,
|
||||
forLoopInfo.inductionVariable.descriptor.type,
|
||||
forLoopInfo.bound.descriptor.type)
|
||||
|
||||
val check = irCall(comparingBuiltIn).apply {
|
||||
putValueArgument(0, irCallOp(compareTo, irGet(forLoopInfo.inductionVariable), irGet(forLoopInfo.bound)))
|
||||
}
|
||||
return irIfThen(check, loop)
|
||||
}
|
||||
|
||||
private fun DeclarationIrBuilder.buildNewCondition(oldCondition: IrExpression): Pair<IrExpression, ForLoopInfo>? {
|
||||
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 a corresponding header.
|
||||
val forLoopInfo = iteratorToLoopInfo[irIteratorAccess.symbol] ?: return null
|
||||
val loopVariable = forLoopInfo.loopVariable!! // TODO: Check!
|
||||
|
||||
return irCall(context.irBuiltIns.booleanNotSymbol).apply {
|
||||
val eqeqCall = irCall(context.irBuiltIns.eqeqSymbol).apply {
|
||||
putValueArgument(0, irGet(loopVariable))
|
||||
putValueArgument(1, irGet(forLoopInfo.bound))
|
||||
}
|
||||
putValueArgument(0, eqeqCall)
|
||||
} to forLoopInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
* }
|
||||
*/
|
||||
override fun visitWhileLoop(loop: IrWhileLoop): IrExpression {
|
||||
if (loop.origin != IrStatementOrigin.FOR_LOOP_INNER_WHILE) {
|
||||
return super.visitWhileLoop(loop)
|
||||
}
|
||||
|
||||
with(context.createIrBuilder(scopeOwnerSymbol)) {
|
||||
// Transform accesses to the old iterator (see visitVariable method). Store loopVariable in loopInfo.
|
||||
val newBody = loop.body?.transform(this@ForLoopsTransformer, null)
|
||||
val (newCondition, forLoopInfo) = buildNewCondition(loop.condition) ?: return super.visitWhileLoop(loop)
|
||||
assert(forLoopInfo.loopVariable != null)
|
||||
|
||||
val newLoop = IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply {
|
||||
label = loop.label
|
||||
condition = newCondition
|
||||
body = newBody
|
||||
}
|
||||
oldLoopToNewLoop[loop] = newLoop
|
||||
// Build a check for an empty progression before the loop.
|
||||
return buildEmptyCheck(newLoop, forLoopInfo)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable): IrStatement {
|
||||
val initializer = declaration.initializer
|
||||
if (initializer == null || initializer !is IrCall) {
|
||||
return super.visitVariable(declaration)
|
||||
}
|
||||
val result = when (initializer.origin) {
|
||||
IrStatementOrigin.FOR_LOOP_ITERATOR -> processHeader(declaration, initializer)
|
||||
IrStatementOrigin.FOR_LOOP_NEXT -> processNext(declaration, initializer)
|
||||
else -> null
|
||||
}
|
||||
return result ?: super.visitVariable(declaration)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,6 +307,29 @@ task sum_3const(type: RunKonanTest) {
|
||||
source = "codegen/function/sum_3const.kt"
|
||||
}
|
||||
|
||||
task codegen_basics_for_loops(type: RunKonanTest) {
|
||||
source = "codegen/basics/for_loops.kt"
|
||||
goldValue = "01234\n0123\n43210\n\n024\n02\n420\n\n036\n03\n630\n\n"
|
||||
}
|
||||
|
||||
task codegen_basics_for_loops_types(type: RunKonanTest) {
|
||||
source = "codegen/basics/for_loops_types.kt"
|
||||
goldValue = "01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n" +
|
||||
"01234\n01234\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n" +
|
||||
"0123\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n" +
|
||||
"43210\n43210\nabcd\nabc\ndcba\n"
|
||||
}
|
||||
|
||||
task codegen_basics_for_loops_overflow(type: RunKonanTest) {
|
||||
source = "codegen/basics/for_loops_overflow.kt"
|
||||
goldValue = "2147483646 2147483647 \n2147483646 \n-2147483647 -2147483648 \n1073741827 \n"
|
||||
}
|
||||
|
||||
task codegen_basics_for_loops_errors(type: RunKonanTest) {
|
||||
source = "codegen/basics/for_loops_errors.kt"
|
||||
goldValue = "OK\n"
|
||||
}
|
||||
|
||||
task local_variable(type: RunKonanTest) {
|
||||
source = "codegen/basics/local_variable.kt"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
fun main(args: Array<String>) {
|
||||
|
||||
// Simple loops
|
||||
for (i in 0..4) {
|
||||
print(i)
|
||||
}
|
||||
println()
|
||||
|
||||
for (i in 0 until 4) {
|
||||
print(i)
|
||||
}
|
||||
println()
|
||||
|
||||
for (i in 4 downTo 0) {
|
||||
print(i)
|
||||
}
|
||||
println()
|
||||
println()
|
||||
|
||||
// Steps
|
||||
for (i in 0..4 step 2) {
|
||||
print(i)
|
||||
}
|
||||
println()
|
||||
|
||||
for (i in 0 until 4 step 2) {
|
||||
print(i)
|
||||
}
|
||||
println()
|
||||
|
||||
for (i in 4 downTo 0 step 2) {
|
||||
print(i)
|
||||
}
|
||||
println()
|
||||
println()
|
||||
|
||||
|
||||
// Two steps
|
||||
for (i in 0..6 step 2 step 3) {
|
||||
print(i)
|
||||
}
|
||||
println()
|
||||
|
||||
for (i in 0 until 6 step 2 step 3) {
|
||||
print(i)
|
||||
}
|
||||
println()
|
||||
|
||||
for (i in 6 downTo 0 step 2 step 3) {
|
||||
print(i)
|
||||
}
|
||||
println()
|
||||
println()
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
fun main(args: Array<String>) {
|
||||
// Negative step.
|
||||
try {
|
||||
for (i in 0 .. 4 step -2) print(i); println()
|
||||
throw AssertionError()
|
||||
} catch (e: IllegalArgumentException) {}
|
||||
|
||||
try {
|
||||
for (i in 0 until 4 step -2) print(i); println()
|
||||
throw AssertionError()
|
||||
} catch (e: IllegalArgumentException) {}
|
||||
|
||||
try {
|
||||
for (i in 4 downTo 0 step -2) print(i); println()
|
||||
throw AssertionError()
|
||||
} catch (e: IllegalArgumentException) {}
|
||||
|
||||
// Zero step.
|
||||
try {
|
||||
for (i in 0 .. 4 step 0) print(i); println()
|
||||
throw AssertionError()
|
||||
} catch (e: IllegalArgumentException) {}
|
||||
|
||||
try {
|
||||
for (i in 0 until 4 step 0) print(i); println()
|
||||
throw AssertionError()
|
||||
} catch (e: IllegalArgumentException) {}
|
||||
|
||||
try {
|
||||
for (i in 4 downTo 0 step 0) print(i); println()
|
||||
throw AssertionError()
|
||||
} catch (e: IllegalArgumentException) {}
|
||||
|
||||
// Two steps, one is negative.
|
||||
try {
|
||||
for (i in 0 .. 4 step -2 step 3) print(i); println()
|
||||
throw AssertionError()
|
||||
} catch (e: IllegalArgumentException) {}
|
||||
|
||||
try {
|
||||
for (i in 0 until 4 step -2 step 3) print(i); println()
|
||||
throw AssertionError()
|
||||
} catch (e: IllegalArgumentException) {}
|
||||
|
||||
try {
|
||||
for (i in 4 downTo 0 step -2 step 3) print(i); println()
|
||||
throw AssertionError()
|
||||
} catch (e: IllegalArgumentException) {}
|
||||
|
||||
println("OK")
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fun main(args: Array<String>) {
|
||||
for (i in Int.MAX_VALUE - 1 .. Int.MAX_VALUE) { print(i); print(' ') }; println()
|
||||
for (i in Int.MAX_VALUE - 1 until Int.MAX_VALUE) { print(i); print(' ') }; println()
|
||||
for (i in Int.MIN_VALUE + 1 downTo Int.MIN_VALUE) { print(i); print(' ') }; println()
|
||||
|
||||
val M = Int.MAX_VALUE / 2
|
||||
for (i in M + 4..M + 10 step M) { print(i); print(' ') }; println()
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
fun main(args: Array<String>) {
|
||||
for (i in 0.toByte() .. 4.toByte()) print(i); println()
|
||||
for (i in 0.toByte() .. 4.toShort()) print(i); println()
|
||||
for (i in 0.toByte() .. 4.toInt()) print(i); println()
|
||||
for (i in 0.toByte() .. 4.toLong()) print(i); println()
|
||||
for (i in 0.toShort() .. 4.toByte()) print(i); println()
|
||||
for (i in 0.toShort() .. 4.toShort()) print(i); println()
|
||||
for (i in 0.toShort() .. 4.toInt()) print(i); println()
|
||||
for (i in 0.toShort() .. 4.toLong()) print(i); println()
|
||||
for (i in 0.toInt() .. 4.toByte()) print(i); println()
|
||||
for (i in 0.toInt() .. 4.toShort()) print(i); println()
|
||||
for (i in 0.toInt() .. 4.toInt()) print(i); println()
|
||||
for (i in 0.toInt() .. 4.toLong()) print(i); println()
|
||||
for (i in 0.toLong() .. 4.toByte()) print(i); println()
|
||||
for (i in 0.toLong() .. 4.toShort()) print(i); println()
|
||||
for (i in 0.toLong() .. 4.toInt()) print(i); println()
|
||||
for (i in 0.toLong() .. 4.toLong()) print(i); println()
|
||||
for (i in 0.toByte() until 4.toByte()) print(i); println()
|
||||
for (i in 0.toByte() until 4.toShort()) print(i); println()
|
||||
for (i in 0.toByte() until 4.toInt()) print(i); println()
|
||||
for (i in 0.toByte() until 4.toLong()) print(i); println()
|
||||
for (i in 0.toShort() until 4.toByte()) print(i); println()
|
||||
for (i in 0.toShort() until 4.toShort()) print(i); println()
|
||||
for (i in 0.toShort() until 4.toInt()) print(i); println()
|
||||
for (i in 0.toShort() until 4.toLong()) print(i); println()
|
||||
for (i in 0.toInt() until 4.toByte()) print(i); println()
|
||||
for (i in 0.toInt() until 4.toShort()) print(i); println()
|
||||
for (i in 0.toInt() until 4.toInt()) print(i); println()
|
||||
for (i in 0.toInt() until 4.toLong()) print(i); println()
|
||||
for (i in 0.toLong() until 4.toByte()) print(i); println()
|
||||
for (i in 0.toLong() until 4.toShort()) print(i); println()
|
||||
for (i in 0.toLong() until 4.toInt()) print(i); println()
|
||||
for (i in 0.toLong() until 4.toLong()) print(i); println()
|
||||
for (i in 4.toByte() downTo 0.toByte()) print(i); println()
|
||||
for (i in 4.toByte() downTo 0.toShort()) print(i); println()
|
||||
for (i in 4.toByte() downTo 0.toInt()) print(i); println()
|
||||
for (i in 4.toByte() downTo 0.toLong()) print(i); println()
|
||||
for (i in 4.toShort() downTo 0.toByte()) print(i); println()
|
||||
for (i in 4.toShort() downTo 0.toShort()) print(i); println()
|
||||
for (i in 4.toShort() downTo 0.toInt()) print(i); println()
|
||||
for (i in 4.toShort() downTo 0.toLong()) print(i); println()
|
||||
for (i in 4.toInt() downTo 0.toByte()) print(i); println()
|
||||
for (i in 4.toInt() downTo 0.toShort()) print(i); println()
|
||||
for (i in 4.toInt() downTo 0.toInt()) print(i); println()
|
||||
for (i in 4.toInt() downTo 0.toLong()) print(i); println()
|
||||
for (i in 4.toLong() downTo 0.toByte()) print(i); println()
|
||||
for (i in 4.toLong() downTo 0.toShort()) print(i); println()
|
||||
for (i in 4.toLong() downTo 0.toInt()) print(i); println()
|
||||
for (i in 4.toLong() downTo 0.toLong()) print(i); println()
|
||||
for (i in 'a' .. 'd') print(i); println()
|
||||
for (i in 'a' until 'd') print(i); println()
|
||||
for (i in 'd' downTo 'a') print(i); println()
|
||||
|
||||
// TODO: Add step tests.
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package konan.internal
|
||||
|
||||
import kotlin.internal.getProgressionLastElement
|
||||
|
||||
@ExportForCppRuntime
|
||||
fun ThrowNullPointerException(): Nothing {
|
||||
throw NullPointerException()
|
||||
@@ -84,4 +86,13 @@ fun <T: Enum<T>> valuesForEnum(values: Array<T>): Array<T>
|
||||
result[value.ordinal] = value
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return result as Array<T>
|
||||
}
|
||||
}
|
||||
|
||||
fun checkProgressionStep(step: Int) = if (step > 0) step else throw IllegalArgumentException("Step must be positive, was: $step.")
|
||||
fun checkProgressionStep(step: Long) = if (step > 0) step else throw IllegalArgumentException("Step must be positive, was: $step.")
|
||||
|
||||
fun getProgressionBound(start: Char, end: Char, step: Int): Char =
|
||||
getProgressionBound(start.toInt(), end.toInt(), step).toChar()
|
||||
|
||||
fun getProgressionBound(start: Int, end: Int, step: Int): Int = getProgressionLastElement(start, end, step)
|
||||
fun getProgressionBound(start: Long, end: Long, step: Long): Long = getProgressionLastElement(start, end, step)
|
||||
Reference in New Issue
Block a user