Add support for arrays in for loops lowering. (#2351)

This commit is contained in:
Sergey Bogolepov
2018-11-23 16:01:59 +07:00
committed by GitHub
parent 3610588666
commit c322d432a0
19 changed files with 825 additions and 425 deletions
@@ -306,7 +306,6 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
} ?: error(descriptor.toString())
return symbolTable.referenceSimpleFunction(functionDescriptor)
}
override val copyRangeTo = arrays.map { symbol ->
val packageViewDescriptor = builtIns.builtInsModule.getPackage(KotlinBuiltIns.COLLECTIONS_PACKAGE_FQ_NAME)
val functionDescriptor = packageViewDescriptor.memberScope
@@ -317,21 +316,18 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
symbol.descriptor to symbolTable.referenceSimpleFunction(functionDescriptor)
}.toMap()
val arrayGet = array.descriptor.unsubstitutedMemberScope
val arrayGet = arrays.associateWith { it.descriptor.unsubstitutedMemberScope
.getContributedFunctions(Name.identifier("get"), NoLookupLocation.FROM_BACKEND)
.single().let { symbolTable.referenceSimpleFunction(it) }
.single().let { symbolTable.referenceSimpleFunction(it) } }
val arraySet = array.descriptor.unsubstitutedMemberScope
.getContributedFunctions(Name.identifier("set"), NoLookupLocation.FROM_BACKEND)
.single().let { symbolTable.referenceSimpleFunction(it) }
val arraySet = arrays.associateWith { it.descriptor.unsubstitutedMemberScope
.getContributedFunctions(Name.identifier("set"), NoLookupLocation.FROM_BACKEND)
.single().let { symbolTable.referenceSimpleFunction(it) } }
val arraySize = arrays.associateBy(
{ it },
{ it.descriptor.unsubstitutedMemberScope
val arraySize = arrays.associateWith { it.descriptor.unsubstitutedMemberScope
.getContributedVariables(Name.identifier("size"), NoLookupLocation.FROM_BACKEND)
.single().let { symbolTable.referenceSimpleFunction(it.getter!!) } }
)
val valuesForEnum = symbolTable.referenceSimpleFunction(
@@ -1247,7 +1247,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
val location = codegen.generateLocationInfo(locationInfo)
val file = (currentCodeContext.fileScope() as FileScope).file.file()
return when (element) {
is IrVariable -> if (element.origin != IrDeclarationOrigin.IR_TEMPORARY_VARIABLE) debugInfoLocalVariableLocation(
is IrVariable -> if (shouldGenerateDebugInfo(element)) debugInfoLocalVariableLocation(
builder = context.debugInfo.builder,
functionScope = locationInfo.scope,
diType = element.descriptor.type.diType(context, codegen.llvmTargetData),
@@ -1269,6 +1269,13 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
}
}
private fun shouldGenerateDebugInfo(variable: IrVariable) = when(variable.origin) {
IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE,
IrDeclarationOrigin.FOR_LOOP_ITERATOR,
IrDeclarationOrigin.IR_TEMPORARY_VARIABLE -> false
else -> true
}
private fun generateVariable(variable: IrVariable) {
context.log{"generateVariable : ${ir2string(variable)}"}
val value = variable.initializer?.let { evaluateExpression(it) }
@@ -11,12 +11,8 @@ 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.backend.konan.irasdescriptors.isSubtypeOf
import org.jetbrains.kotlin.backend.konan.irasdescriptors.typeWithStarProjections
import org.jetbrains.kotlin.ir.util.isSimpleTypeWithQuestionMark
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
@@ -27,304 +23,66 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.util.OperatorNameConventions
/** This lowering pass optimizes range-based for loops.
/** This lowering pass optimizes for-loops.
*
* NB! not implemented yet.
* Process `for (elem in array) { f(elem) }` construct first.
* Replace with `for (i in array.indices) { f(array.get(i)) }`.
*
* Next replace iteration over ranges (X.indices, a..b, etc.) with
* 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 progressionInfoBuilder = ProgressionInfoBuilder(context)
private val headerInfoBuilder = HeaderInfoBuilder(context)
override fun lower(irFile: IrFile) {
val transformer = ForLoopsTransformer(context, progressionInfoBuilder)
// Lower loops
val oldLoopToNewLoop = mutableMapOf<IrLoop, IrLoop>()
val transformer = RangeLoopTransformer(context, oldLoopToNewLoop, headerInfoBuilder)
irFile.transformChildrenVoid(transformer)
// Update references in break/continue.
irFile.transformChildrenVoid(object: IrElementTransformerVoid() {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitBreakContinue(jump: IrBreakContinue): IrExpression {
transformer.oldLoopToNewLoop[jump.loop]?.let { jump.loop = it }
oldLoopToNewLoop[jump.loop]?.let { jump.loop = it }
return jump
}
})
}
}
/** Contains information about variables used in the loop. */
internal data class ForLoopInfo(
val progressionInfo: ProgressionInfo,
val inductionVariable: IrVariable,
val bound: IrVariable,
val last: IrVariable,
val step: IrVariable,
var loopVariable: IrVariable? = null)
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
}
private class ForLoopsTransformer(val context: Context, val progressionInfoBuilder: ProgressionInfoBuilder) : IrElementTransformerVoidWithContext() {
private class RangeLoopTransformer(
val context: Context,
val oldLoopToNewLoop: MutableMap<IrLoop, IrLoop>,
headerInfoBuilder: HeaderInfoBuilder)
: IrElementTransformerVoidWithContext() {
private val symbols = context.ir.symbols
private val iteratorToLoopInfo = mutableMapOf<IrVariableSymbol, ForLoopInfo>()
internal val oldLoopToNewLoop = mutableMapOf<IrLoop, IrLoop>()
private val iteratorToLoopHeader = mutableMapOf<IrVariableSymbol, ForLoopHeader>()
private val headerProcessor = HeaderProcessor(context, headerInfoBuilder, this::getScopeOwnerSymbol)
private val progressionElementClasses =
(symbols.integerClasses + symbols.char).toSet()
fun getScopeOwnerSymbol() = currentScope!!.scope.scopeOwnerSymbol
private val scopeOwnerSymbol
get() = currentScope!!.scope.scopeOwnerSymbol
//region Util methods ==============================================================================================
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.getFunction(progressionType.numberCastFunctionName, type.toKotlinType())
IrCallImpl(startOffset, endOffset, function.owner.returnType, function)
.apply { dispatchReceiver = this@castIfNecessary }
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 DeclarationIrBuilder.ensureNotNullable(expression: IrExpression): IrExpression {
return 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 ProgressionInfo.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, lastExpression.endOffset, symbol.owner.returnType, symbol).apply {
putValueArgument(0, IrGetValueImpl(startOffset, endOffset, first.type, first.symbol))
putValueArgument(1, lastExpression.castIfNecessary(progressionType))
putValueArgument(2, IrGetValueImpl(startOffset, endOffset, step.type, step.symbol))
}
}
//endregion
//region Lowering ==================================================================================================
// Lower a loop header.
private fun processHeader(variable: IrVariable, initializer: IrCall): IrStatement? {
assert(variable.origin == IrDeclarationOrigin.FOR_LOOP_ITERATOR)
val symbol = variable.symbol
val iteratorType = symbols.iterator.typeWithStarProjections
if (!variable.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 composite.
val progressionInfo = initializer.dispatchReceiver?.accept(progressionInfoBuilder, null)
private fun processHeader(variable: IrVariable): IrStatement? {
assert(variable.symbol !in iteratorToLoopHeader)
val forLoopInfo = headerProcessor.processHeader(variable)
?: return null
with(builder) {
with(progressionInfo) {
// 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 statements = mutableListOf<IrStatement>()
/**
* 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).
*/
val inductionVariable = scope.createTemporaryVariable(first.castIfNecessary(progressionType),
nameHint = "inductionVariable",
isMutable = true,
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE).also {
statements.add(it)
}
val boundValue = scope.createTemporaryVariable(ensureNotNullable(bound.castIfNecessary(progressionType)),
nameHint = "bound",
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE)
.also { statements.add(it) }
val stepExpression = if (step != null) {
if (increasing) step else step.unaryMinus()
} else {
defaultStep(startOffset, endOffset)
}
val stepValue = scope.createTemporaryVariable(ensureNotNullable(stepExpression),
nameHint = "step",
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE).also {
statements.add(it)
}
// 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.
var lastExpression: IrExpression = if (!closed) {
val decrementSymbol = symbols.getUnaryOperator(OperatorNameConventions.DEC, boundValue.type.toKotlinType())
irCall(decrementSymbol.owner).apply {
dispatchReceiver = irGet(boundValue)
}
} else {
irGet(boundValue)
}
if (needLastCalculation) {
lastExpression = irGetProgressionLast(progressionType,
inductionVariable,
lastExpression,
stepValue)
}
val lastValue = scope.createTemporaryVariable(lastExpression,
nameHint = "last",
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE).also {
statements.add(it)
}
iteratorToLoopInfo[symbol] = ForLoopInfo(progressionInfo,
inductionVariable,
boundValue,
lastValue,
stepValue)
return IrCompositeImpl(startOffset, endOffset, context.irBuiltIns.unitType, null, statements)
}
}
}
// Lower getting a next induction variable value.
private fun processNext(variable: IrVariable, initializer: IrCall): IrExpression? {
assert(variable.origin == IrDeclarationOrigin.FOR_LOOP_VARIABLE
|| variable.origin == IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE)
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)
val plusOperator = symbols.getBinaryOperator(
OperatorNameConventions.PLUS,
forLoopInfo.inductionVariable.type.toKotlinType(),
forLoopInfo.step.type.toKotlinType()
)
forLoopInfo.loopVariable = variable
with(builder) {
variable.initializer = irGet(forLoopInfo.inductionVariable)
val increment = irSetVar(forLoopInfo.inductionVariable,
irCallOp(plusOperator.owner, irGet(forLoopInfo.inductionVariable), irGet(forLoopInfo.step)))
return IrCompositeImpl(variable.startOffset,
variable.endOffset,
context.irBuiltIns.unitType,
IrStatementOrigin.FOR_LOOP_NEXT,
listOf(variable, increment))
}
}
private fun DeclarationIrBuilder.buildMinValueCondition(forLoopInfo: ForLoopInfo): IrExpression {
// Condition for a corner case: for (i in a until Int.MIN_VALUE) {}.
// Check if forLoopInfo.bound > MIN_VALUE.
val progressionType = forLoopInfo.progressionInfo.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,
forLoopInfo.bound.type.toKotlinType(),
minConst.type.toKotlinType())
return irCall(irBuiltIns.greaterFunByOperandType[irBuiltIns.int]?.symbol!!).apply {
val compareToCall = irCall(compareTo).apply {
dispatchReceiver = irGet(forLoopInfo.bound)
putValueArgument(0, minConst)
}
putValueArgument(0, compareToCall)
putValueArgument(1, irInt(0))
}
}
// TODO: Eliminate the loop if we can prove that it will not be executed.
private fun DeclarationIrBuilder.buildEmptinessCheck(loop: IrLoop, forLoopInfo: ForLoopInfo): IrExpression {
val builtIns = context.irBuiltIns
val increasing = forLoopInfo.progressionInfo.increasing
val comparingBuiltIn = if (increasing) builtIns.lessOrEqualFunByOperandType[builtIns.int]?.symbol
else builtIns.greaterOrEqualFunByOperandType[builtIns.int]?.symbol
// Check if inductionVariable <= last.
val compareTo = symbols.getBinaryOperator(OperatorNameConventions.COMPARE_TO,
forLoopInfo.inductionVariable.type.toKotlinType(),
forLoopInfo.last.type.toKotlinType())
val check: IrExpression = irCall(comparingBuiltIn!!).apply {
putValueArgument(0, irCallOp(compareTo.owner, irGet(forLoopInfo.inductionVariable), irGet(forLoopInfo.last)))
putValueArgument(1, irInt(0))
}
// Process closed and open ranges in different manners.
return if (forLoopInfo.progressionInfo.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(forLoopInfo), 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
assert(forLoopInfo.loopVariable != null)
return irCall(context.irBuiltIns.booleanNotSymbol).apply {
val eqeqCall = irCall(context.irBuiltIns.eqeqSymbol).apply {
putValueArgument(0, irGet(forLoopInfo.loopVariable!!))
putValueArgument(1, irGet(forLoopInfo.last))
}
putValueArgument(0, eqeqCall)
} to forLoopInfo
iteratorToLoopHeader[variable.symbol] = forLoopInfo
return IrCompositeImpl(
variable.startOffset,
variable.endOffset,
context.irBuiltIns.unitType,
null,
forLoopInfo.declarations)
}
/**
@@ -349,47 +107,126 @@ private class ForLoopsTransformer(val context: Context, val progressionInfoBuild
* ...
* } 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)
}
with(context.createIrBuilder(scopeOwnerSymbol, loop.startOffset, loop.endOffset)) {
// Transform accesses to the old iterator (see visitVariable method). Store loopVariable in loopInfo.
// Replace not transparent containers with transparent ones (IrComposite)
val newBody = loop.body?.transform(this@ForLoopsTransformer, null)?.let {
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 (newCondition, forLoopInfo) = buildNewCondition(loop.condition) ?: return super.visitWhileLoop(loop)
val newLoop = IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply {
label = loop.label
condition = newCondition
body = newBody
}
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.
return buildEmptinessCheck(newLoop, forLoopInfo)
if (loopHeader is ProgressionLoopHeader) {
buildEmptinessCheck(newLoop, loopHeader)
} else {
newLoop
}
}
}
override fun visitVariable(declaration: IrVariable): IrStatement {
val initializer = declaration.initializer
if (initializer == null || initializer !is IrCall) {
return super.visitVariable(declaration)
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 result = when (initializer.origin) {
IrStatementOrigin.FOR_LOOP_ITERATOR -> processHeader(declaration, initializer)
IrStatementOrigin.FOR_LOOP_NEXT -> processNext(declaration, initializer)
else -> null
val compareTo = symbols.getBinaryOperator(OperatorNameConventions.COMPARE_TO,
forLoopHeader.bound.type.toKotlinType(),
minConst.type.toKotlinType())
return irCall(irBuiltIns.greaterFunByOperandType[irBuiltIns.int]?.symbol!!).apply {
val compareToCall = irCall(compareTo).apply {
dispatchReceiver = irGet(forLoopHeader.bound)
putValueArgument(0, minConst)
}
putValueArgument(0, compareToCall)
putValueArgument(1, irInt(0))
}
return result ?: super.visitVariable(declaration)
}
//endregion
}
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).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))
}
}
}
@@ -0,0 +1,110 @@
package org.jetbrains.kotlin.backend.konan.lower.loops
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.irasdescriptors.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)
}
}
@@ -0,0 +1,270 @@
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.irasdescriptors.isSubtypeOf
import org.jetbrains.kotlin.backend.konan.irasdescriptors.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.IrSimpleFunctionSymbol
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]?.symbol!!
else
builtIns.greaterOrEqualFunByOperandType[builtIns.int]?.symbol!!
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 {
putValueArgument(0, 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]?.symbol!!
val newCondition = irCall(callee).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.getFunction(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))
}
}
}
@@ -8,15 +8,17 @@ 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.expressions.impl.IrConstImpl
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 }
@@ -25,8 +27,8 @@ internal class RangeToHandler(val progressionElementClasses: Collection<IrClassS
parameter(0) { it.type.classifierOrNull in progressionElementClasses }
}
override fun build(call: IrCall, progressionType: ProgressionType) =
ProgressionInfo(progressionType, call.dispatchReceiver!!, call.getValueArgument(0)!!)
override fun build(call: IrCall, data: ProgressionType) =
ProgressionHeaderInfo(data, call.dispatchReceiver!!, call.getValueArgument(0)!!)
}
internal class DownToHandler(val progressionElementClasses: Collection<IrClassSymbol>) : ProgressionHandler {
@@ -35,8 +37,8 @@ internal class DownToHandler(val progressionElementClasses: Collection<IrClassSy
parameter(0) { it.type.classifierOrNull in progressionElementClasses }
}
override fun build(call: IrCall, progressionType: ProgressionType): ProgressionInfo? =
ProgressionInfo(progressionType,
override fun build(call: IrCall, data: ProgressionType): HeaderInfo? =
ProgressionHeaderInfo(data,
call.extensionReceiver!!,
call.getValueArgument(0)!!,
increasing = false)
@@ -48,8 +50,8 @@ internal class UntilHandler(val progressionElementClasses: Collection<IrClassSym
parameter(0) { it.type.classifierOrNull in progressionElementClasses }
}
override fun build(call: IrCall, progressionType: ProgressionType): ProgressionInfo? =
ProgressionInfo(progressionType,
override fun build(call: IrCall, data: ProgressionType): HeaderInfo? =
ProgressionHeaderInfo(data,
call.extensionReceiver!!,
call.getValueArgument(0)!!,
closed = false)
@@ -69,21 +71,20 @@ internal class IndicesHandler(val context: Context) : ProgressionHandler {
extensionReceiver { it != null && it.type.classifierOrNull in supportedArrays }
}
override fun build(call: IrCall, progressionType: ProgressionType): ProgressionInfo? {
val int0 = IrConstImpl.int(call.startOffset, call.endOffset, context.irBuiltIns.intType, 0)
val bound = with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) {
val clazz = call.extensionReceiver!!.type.classifierOrFail
val symbol = symbols.arraySize[clazz]!!
irCall(symbol).apply {
dispatchReceiver = call.extensionReceiver
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)
}
}
return ProgressionInfo(progressionType, int0, bound, closed = false)
}
}
internal class StepHandler(context: Context, val visitor: IrElementVisitor<ProgressionInfo?, Nothing?>) : ProgressionHandler {
internal class StepHandler(context: Context, val visitor: IrElementVisitor<HeaderInfo?, Nothing?>) : ProgressionHandler {
private val symbols = context.ir.symbols
override val matcher: IrCallMatcher = SimpleCalleeMatcher {
@@ -91,28 +92,32 @@ internal class StepHandler(context: Context, val visitor: IrElementVisitor<Progr
parameter(0) { it.type.isInt() || it.type.isLong() }
}
override fun build(call: IrCall, progressionType: ProgressionType): ProgressionInfo? {
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 (nestedInfo.step != null) {
if (!isDefaultStep(nestedInfo.step)) {
return null
}
val newStep = call.getValueArgument(0)!!
val (newStepCheck, needBoundCalculation) = irCheckProgressionStep(symbols, progressionType, newStep)
return ProgressionInfo(
progressionType,
nestedInfo.first,
nestedInfo.bound,
newStepCheck, nestedInfo.increasing,
val (newStepCheck, needBoundCalculation) = irCheckProgressionStep(symbols, data, newStep)
return ProgressionHeaderInfo(
data,
nestedInfo.lowerBound,
nestedInfo.upperBound,
newStepCheck,
nestedInfo.increasing,
needBoundCalculation,
nestedInfo.closed)
}
private fun IrConst<*>.stepIsOne() = when (kind) {
private fun IrConst<*>.isOne() = when (kind) {
IrConstKind.Long -> value as Long == 1L
IrConstKind.Int -> value as Int == 1
else -> false
@@ -132,7 +137,7 @@ internal class StepHandler(context: Context, val visitor: IrElementVisitor<Progr
private fun irCheckProgressionStep(symbols: KonanSymbols, progressionType: ProgressionType, step: IrExpression) =
if (step.isPositiveConst()) {
step to !(step as IrConst<*>).stepIsOne()
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.
@@ -145,3 +150,36 @@ internal class StepHandler(context: Context, val visitor: IrElementVisitor<Progr
} 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)
}
}
@@ -1,70 +0,0 @@
package org.jetbrains.kotlin.backend.konan.lower.loops
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.irasdescriptors.isSubtypeOf
import org.jetbrains.kotlin.backend.konan.lower.matchers.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.*
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"));
}
internal data class ProgressionInfo(
val progressionType: ProgressionType,
val first: IrExpression,
val bound: IrExpression,
val step: IrExpression? = null,
val increasing: Boolean = true,
var needLastCalculation: Boolean = false,
val closed: Boolean = true)
internal interface ProgressionHandler {
val matcher: IrCallMatcher
fun build(call: IrCall, progressionType: ProgressionType): ProgressionInfo?
fun handle(irCall: IrCall, progressionType: ProgressionType) = if (matcher(irCall)) {
build(irCall, progressionType)
} else {
null
}
}
internal class ProgressionInfoBuilder(val context: Context) : IrElementVisitor<ProgressionInfo?, Nothing?> {
private val symbols = context.ir.symbols
private val progressionElementClasses = symbols.integerClasses + symbols.char
private val handlers = 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?): ProgressionInfo? = null
override fun visitCall(expression: IrCall, data: Nothing?): ProgressionInfo? {
val progressionType = expression.type.getProgressionType()
?: return null
return handlers.firstNotNullResult { it.handle(expression, progressionType) }
}
}
@@ -2,6 +2,7 @@ package org.jetbrains.kotlin.backend.konan.lower.matchers
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
internal interface IrCallMatcher : (IrCall) -> Boolean
@@ -25,6 +26,18 @@ internal class IrCallExtensionReceiverMatcher(
override fun invoke(call: IrCall) = restriction(call.extensionReceiver)
}
internal class IrCallDispatchReceiverMatcher(
val restriction: (IrExpression?) -> Boolean
) : IrCallMatcher {
override fun invoke(call: IrCall) = restriction(call.dispatchReceiver)
}
internal class IrCallOriginMatcher(
val restriction: (IrStatementOrigin?) -> Boolean
) : IrCallMatcher {
override fun invoke(call: IrCall) = restriction(call.origin)
}
internal open class IrCallMatcherContainer : IrCallMatcher {
private val matchers = mutableListOf<IrCallMatcher>()
@@ -36,10 +49,16 @@ internal open class IrCallMatcherContainer : IrCallMatcher {
fun extensionReceiver(restriction: (IrExpression?) -> Boolean) =
add(IrCallExtensionReceiverMatcher(restriction))
fun origin(restriction: (IrStatementOrigin?) -> Boolean) =
add(IrCallOriginMatcher(restriction))
fun callee(restrictions: IrFunctionMatcherContainer.() -> Unit) {
add(SimpleCalleeMatcher(restrictions))
}
fun dispatchReceiver(restriction: (IrExpression?) -> Boolean) =
add(IrCallDispatchReceiverMatcher(restriction))
override fun invoke(call: IrCall) = matchers.all { it(call) }
}
@@ -391,19 +391,21 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
}
}
private val symbols = context.ir.symbols
private val invokeSuspendFunctionSymbol =
context.ir.symbols.baseContinuationImpl.owner.declarations
symbols.baseContinuationImpl.owner.declarations
.filterIsInstance<IrSimpleFunction>().single { it.name.asString() == "invokeSuspend" }.symbol
private val getContinuationSymbol = context.ir.symbols.getContinuation
private val getContinuationSymbol = symbols.getContinuation
private val continuationType = getContinuationSymbol.owner.returnType
private val arrayGetSymbol = context.ir.symbols.arrayGet
private val arraySetSymbol = context.ir.symbols.arraySet
private val createUninitializedInstanceSymbol = context.ir.symbols.createUninitializedInstance
private val initInstanceSymbol = context.ir.symbols.initInstance
private val executeImplSymbol = context.ir.symbols.executeImpl
private val executeImplProducerClassSymbol = context.ir.symbols.functions[0]
private val arrayGetSymbol = symbols.arrayGet[symbols.array]
private val arraySetSymbol = symbols.arraySet[symbols.array]
private val createUninitializedInstanceSymbol = symbols.createUninitializedInstance
private val initInstanceSymbol = symbols.initInstance
private val executeImplSymbol = symbols.executeImpl
private val executeImplProducerClassSymbol = symbols.functions[0]
private val executeImplProducerInvoke = executeImplProducerClassSymbol.owner.simpleFunctions()
.single { it.name == OperatorNameConventions.INVOKE }
@@ -460,7 +462,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
)
val throwsNode = DataFlowIR.Node.Variable(
values = thrownValues.map { expressionToEdge(it) },
type = symbolTable.mapClassReferenceType(context.ir.symbols.throwable.owner),
type = symbolTable.mapClassReferenceType(symbols.throwable.owner),
kind = DataFlowIR.VariableKind.Temporary
)
variables.forEach { descriptor, node ->
+35
View File
@@ -455,6 +455,41 @@ task codegen_controlflow_for_loops_call_order(type: RunKonanTest) {
goldValue = "1234\n1234\n2134\n"
}
task codegen_controlflow_for_loops_array_indices(type: RunKonanTest) {
source = "codegen/controlflow/for_loops_array_indices.kt"
goldValue = "0123\n\n"
}
task codegen_controlflow_for_loops_array(type: RunKonanTest) {
source = "codegen/controlflow/for_loops_array.kt"
goldValue = "4035\n\n0\n0\n"
}
task codegen_controlflow_for_loops_array_nested(type: RunKonanTest) {
source = "codegen/controlflow/for_loops_array_nested.kt"
goldValue = "123\nHello\n\n12345678910\n"
}
task codegen_controlflow_for_loops_array_side_effects(type: RunKonanTest) {
source = "codegen/controlflow/for_loops_array_side_effects.kt"
goldValue = "side-effect\n4035\nside-effect\n\n"
}
task codegen_controlflow_for_loops_array_break_continue(type: RunKonanTest) {
source = "codegen/controlflow/for_loops_array_break_continue.kt"
goldValue = "403\n\n"
}
task codegen_controlflow_for_loops_array_mutation(type: RunKonanTest) {
source = "codegen/controlflow/for_loops_array_mutation.kt"
goldValue = "4000"
}
task codegen_controlflow_for_loops_array_nullable(type: RunKonanTest) {
source = "codegen/controlflow/for_loops_array_nullable.kt"
goldValue = "123"
}
task local_variable(type: RunKonanTest) {
source = "codegen/basics/local_variable.kt"
}
@@ -0,0 +1,40 @@
package codegen.controlflow.for_loops_array
import kotlin.test.*
fun <T : ByteArray> genericArray(data : T): Int {
var sum = 0
for (element in data) {
sum += element
}
return sum
}
fun IntArray.sum(): Int {
var sum = 0
for (element in this) {
sum += element
}
return sum
}
@Test fun runTest() {
val intArray = intArrayOf(4, 0, 3, 5)
val emptyArray = arrayOf<Any>()
for (element in intArray) {
print(element)
}
println()
for (element in emptyArray) {
print(element)
}
println()
val byteArray = byteArrayOf(1, -1)
println(genericArray(byteArray))
val fives = intArrayOf(5, 5, 5, -5, -5, -5)
println(fives.sum())
}
@@ -0,0 +1,21 @@
package codegen.controlflow.for_loops_array_break_continue
import kotlin.test.*
@Test fun runTest() {
val intArray = intArrayOf(4, 0, 3, 5)
val emptyArray = arrayOf<Any>()
for (element in intArray) {
print(element)
if (element == 3) {
break
}
}
println()
for (element in emptyArray) {
print(element)
}
println()
}
@@ -0,0 +1,18 @@
package codegen.controlflow.for_loops_array_indices
import kotlin.test.*
@Test fun runTest() {
val intArray = intArrayOf(4, 0, 3, 5)
val emptyArray = arrayOf<Any>()
for (index in intArray.indices) {
print(index)
}
println()
for (index in emptyArray.indices) {
print(index)
}
println()
}
@@ -0,0 +1,13 @@
package codegen.controlflow.for_loops_array_mutation
import kotlin.test.*
@Test fun runTest() {
val intArray = arrayOf(4, 0, 3, 5)
for (element in intArray) {
intArray[2] = 0
intArray[3] = 0
print(element)
}
}
@@ -0,0 +1,25 @@
package codegen.controlflow.for_loops_array_nested
import kotlin.test.*
@Test fun arrayOfArrays() {
val metaArray = arrayOf(
arrayOf(1, 2, 3),
arrayOf("Hello"),
arrayOf<Any>(),
arrayOf(1..10)
)
for (array in metaArray) {
inner@for (elem in array) {
if (elem is IntProgression) {
for (i in elem) {
print(i)
}
continue@inner
} else {
print(elem)
}
}
println()
}
}
@@ -0,0 +1,16 @@
package codegen.controlflow.for_loops_array_nullable
import kotlin.test.*
private fun nullableArray(a: Array<Int>): Array<Int>? {
return a
}
@Test fun nullable() {
val array = arrayOf(1, 2, 3)
nullableArray(array)?.let {
for (elem in it) {
print(elem)
}
}
}
@@ -0,0 +1,23 @@
package codegen.controlflow.for_loops_array_side_effects
import kotlin.test.*
private fun <T> sideEffect(array: T): T {
println("side-effect")
return array
}
@Test fun runTest() {
val intArray = intArrayOf(4, 0, 3, 5)
val emptyArray = arrayOf<Any>()
for (element in sideEffect(intArray)) {
print(element)
}
println()
for (element in sideEffect(emptyArray)) {
print(element)
}
println()
}
+2 -2
View File
@@ -5,6 +5,6 @@
// TODO: TestRuner should be able to pass input to stdin
// TODO: remove kotlin_native.io once overrides are in place.
fun main(args : Array<String>) {
print(readLine().toString())
fun main(args: Array<String>) {
print(readLine().toString())
}
@@ -54,7 +54,7 @@ class ForLoopsBenchmark {
fun floatArrayLoop(): Double {
var sum = 0.0
for (e in array) {
for (e in floatArray) {
sum += e
}
return sum