Add support for arrays in for loops lowering. (#2351)
This commit is contained in:
+7
-11
@@ -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(
|
||||
|
||||
+8
-1
@@ -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) }
|
||||
|
||||
+139
-302
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
+110
@@ -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)
|
||||
}
|
||||
}
|
||||
+270
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
+67
-29
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
-70
@@ -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) }
|
||||
}
|
||||
}
|
||||
+19
@@ -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) }
|
||||
}
|
||||
|
||||
|
||||
+11
-9
@@ -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 ->
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user