From 01f0540fac01e246630d9289febec120effb0b31 Mon Sep 17 00:00:00 2001 From: Ilya Matveev Date: Thu, 13 Jul 2017 15:51:41 +0700 Subject: [PATCH] backend: Don't cast step in 'for' loop lowering --- .../backend/konan/lower/ForLoopsLowering.kt | 49 ++++++++++------- backend.native/tests/build.gradle | 4 +- .../tests/codegen/basics/for_loops_types.kt | 52 ++++++++++++++++++- 3 files changed, 85 insertions(+), 20 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ForLoopsLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ForLoopsLowering.kt index 9b5ea503479..85d83730d0b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ForLoopsLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ForLoopsLowering.kt @@ -74,12 +74,6 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo private val scopeOwnerSymbol get() = currentScope!!.scope.scopeOwnerSymbol - private fun irConstOne(startOffset: Int, endOffset: Int) = - IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, 1) - - private fun irConstMinusOne(startOffset: Int, endOffset: Int) = - IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, -1) - private val progressionElementClasses: Set = mutableSetOf(symbols.char).apply { addAll(symbols.integerClasses) } @@ -149,6 +143,18 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo dispatchReceiver = this@unaryMinus } + private fun ProgressionInfo.defaultStep(startOffset: Int, endOffset: Int): IrExpression = + progressionType.elementType.let { type -> + val step = if (increasing) 1 else -1 + when { + KotlinBuiltIns.isInt(type) || KotlinBuiltIns.isChar(type) -> + IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, step) + KotlinBuiltIns.isLong(type) -> + IrConstImpl.long(startOffset, endOffset, context.builtIns.longType, step.toLong()) + else -> throw IllegalArgumentException() + } + } + private fun IrConst<*>.isOne() = when (kind) { IrConstKind.Long -> value as Long == 1L @@ -156,6 +162,11 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo else -> false } + // Used only by the assert. + private fun stepHasRightType(step: IrExpression, progressionType: ProgressionType) = + ((progressionType.isCharProgression() || progressionType.isIntProgression()) && KotlinBuiltIns.isInt(step.type)) || + (progressionType.isLongProgression() && KotlinBuiltIns.isLong(step.type)) + private fun irCheckProgressionStep(progressionType: ProgressionType, step: IrExpression): Pair { if (step is IrConst<*> && @@ -163,11 +174,14 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo (step.kind == IrConstKind.Int && step.value as Int > 0))) { return step to !step.isOne() } - val castedStep = step.castIfNecessary(progressionType, false) - val symbol = symbols.checkProgressionStep[castedStep.type] + // The frontend checks if the step has a right type (Long for LongProgression and Int for {Int/Char}Progression) + // so there is no need to cast it. + assert(stepHasRightType(step, progressionType)) + + val symbol = symbols.checkProgressionStep[step.type] ?: throw IllegalArgumentException("Unknown progression element type: ${step.type}") return IrCallImpl(step.startOffset, step.endOffset, symbol).apply { - putValueArgument(0, castedStep) + putValueArgument(0, step) } to true } @@ -190,7 +204,11 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo //region Util classes ============================================================================================== // TODO: Replace with a cast when such support is added in the boxing lowering. private data class ProgressionType(val elementType: KotlinType, - val numberCastFunctionName: Name) + val numberCastFunctionName: Name) { + fun isIntProgression() = KotlinBuiltIns.isInt(elementType) + fun isLongProgression() = KotlinBuiltIns.isLong(elementType) + fun isCharProgression() = KotlinBuiltIns.isChar(elementType) + } private data class ProgressionInfo( val progressionType: ProgressionType, @@ -287,11 +305,7 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo return builder.irBlock { with(progressionInfo) { val inductionVariable = irTemporaryVar(first.castIfNecessary(progressionType), "inductionVariable") - val stepExpression = if (increasing) { // TODO: Remove the casts. - step ?: irConstOne(startOffset, endOffset).castIfNecessary(progressionType, false) - } else { - step?.unaryMinus() ?: irConstMinusOne(startOffset, endOffset).castIfNecessary(progressionType, false) - } + val stepExpression = (if (increasing) step else step?.unaryMinus()) ?: defaultStep(startOffset, endOffset) val stepValue = irTemporary(stepExpression, "step") // Don't call progression bound calculation if the step is 1. val boundExpression = if (needBoundCalculation) { @@ -357,11 +371,11 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo val irIteratorAccess = oldCondition.dispatchReceiver as? IrGetValue ?: throw AssertionError() // Return null if we didn't lower a corresponding header. val forLoopInfo = iteratorToLoopInfo[irIteratorAccess.symbol] ?: return null - val loopVariable = forLoopInfo.loopVariable!! // TODO: Check! + assert(forLoopInfo.loopVariable != null) return irCall(context.irBuiltIns.booleanNotSymbol).apply { val eqeqCall = irCall(context.irBuiltIns.eqeqSymbol).apply { - putValueArgument(0, irGet(loopVariable)) + putValueArgument(0, irGet(forLoopInfo.loopVariable!!)) putValueArgument(1, irGet(forLoopInfo.bound)) } putValueArgument(0, eqeqCall) @@ -400,7 +414,6 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo // Transform accesses to the old iterator (see visitVariable method). Store loopVariable in loopInfo. val newBody = loop.body?.transform(this@ForLoopsTransformer, null) val (newCondition, forLoopInfo) = buildNewCondition(loop.condition) ?: return super.visitWhileLoop(loop) - assert(forLoopInfo.loopVariable != null) val newLoop = IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply { label = loop.label diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index c8652785493..16968964da3 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -317,7 +317,9 @@ task codegen_basics_for_loops_types(type: RunKonanTest) { goldValue = "01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n" + "01234\n01234\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n" + "0123\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n" + - "43210\n43210\nabcd\nabc\ndcba\n" + "43210\n43210\nabcd\nabc\ndcba\n024\n024\n024\n024\n024\n024\n024\n024\n024\n024\n024\n024\n024\n024\n" + + "024\n024\n02\n02\n02\n02\n02\n02\n02\n02\n02\n02\n02\n02\n02\n02\n02\n02\n420\n420\n420\n420\n420\n420\n" + + "420\n420\n420\n420\n420\n420\n420\n420\n420\n420\nac\nac\ndb\n" } task codegen_basics_for_loops_overflow(type: RunKonanTest) { diff --git a/backend.native/tests/codegen/basics/for_loops_types.kt b/backend.native/tests/codegen/basics/for_loops_types.kt index 6ffb760b0a5..943706daff7 100644 --- a/backend.native/tests/codegen/basics/for_loops_types.kt +++ b/backend.native/tests/codegen/basics/for_loops_types.kt @@ -51,5 +51,55 @@ fun main(args: Array) { for (i in 'a' until 'd') print(i); println() for (i in 'd' downTo 'a') print(i); println() - // TODO: Add step tests. + for (i in 0.toByte() .. 4.toByte() step 2) print(i); println() + for (i in 0.toByte() .. 4.toShort() step 2) print(i); println() + for (i in 0.toByte() .. 4.toInt() step 2) print(i); println() + for (i in 0.toByte() .. 4.toLong() step 2L) print(i); println() + for (i in 0.toShort() .. 4.toByte() step 2) print(i); println() + for (i in 0.toShort() .. 4.toShort() step 2) print(i); println() + for (i in 0.toShort() .. 4.toInt() step 2) print(i); println() + for (i in 0.toShort() .. 4.toLong() step 2L) print(i); println() + for (i in 0.toInt() .. 4.toByte() step 2) print(i); println() + for (i in 0.toInt() .. 4.toShort() step 2) print(i); println() + for (i in 0.toInt() .. 4.toInt() step 2) print(i); println() + for (i in 0.toInt() .. 4.toLong() step 2L) print(i); println() + for (i in 0.toLong() .. 4.toByte() step 2L) print(i); println() + for (i in 0.toLong() .. 4.toShort() step 2L) print(i); println() + for (i in 0.toLong() .. 4.toInt() step 2L) print(i); println() + for (i in 0.toLong() .. 4.toLong() step 2L) print(i); println() + for (i in 0.toByte() until 4.toByte() step 2) print(i); println() + for (i in 0.toByte() until 4.toShort() step 2) print(i); println() + for (i in 0.toByte() until 4.toInt() step 2) print(i); println() + for (i in 0.toByte() until 4.toLong() step 2L) print(i); println() + for (i in 0.toShort() until 4.toByte() step 2) print(i); println() + for (i in 0.toShort() until 4.toShort() step 2) print(i); println() + for (i in 0.toShort() until 4.toInt() step 2) print(i); println() + for (i in 0.toShort() until 4.toLong() step 2L) print(i); println() + for (i in 0.toInt() until 4.toByte() step 2) print(i); println() + for (i in 0.toInt() until 4.toShort() step 2) print(i); println() + for (i in 0.toInt() until 4.toInt() step 2) print(i); println() + for (i in 0.toInt() until 4.toLong() step 2L) print(i); println() + for (i in 0.toLong() until 4.toByte() step 2L) print(i); println() + for (i in 0.toLong() until 4.toShort() step 2L) print(i); println() + for (i in 0.toLong() until 4.toInt() step 2L) print(i); println() + for (i in 0.toLong() until 4.toLong() step 2L) print(i); println() + for (i in 4.toByte() downTo 0.toByte() step 2) print(i); println() + for (i in 4.toByte() downTo 0.toShort() step 2) print(i); println() + for (i in 4.toByte() downTo 0.toInt() step 2) print(i); println() + for (i in 4.toByte() downTo 0.toLong() step 2L) print(i); println() + for (i in 4.toShort() downTo 0.toByte() step 2) print(i); println() + for (i in 4.toShort() downTo 0.toShort() step 2) print(i); println() + for (i in 4.toShort() downTo 0.toInt() step 2) print(i); println() + for (i in 4.toShort() downTo 0.toLong() step 2L) print(i); println() + for (i in 4.toInt() downTo 0.toByte() step 2) print(i); println() + for (i in 4.toInt() downTo 0.toShort() step 2) print(i); println() + for (i in 4.toInt() downTo 0.toInt() step 2) print(i); println() + for (i in 4.toInt() downTo 0.toLong() step 2L) print(i); println() + for (i in 4.toLong() downTo 0.toByte() step 2L) print(i); println() + for (i in 4.toLong() downTo 0.toShort() step 2L) print(i); println() + for (i in 4.toLong() downTo 0.toInt() step 2L) print(i); println() + for (i in 4.toLong() downTo 0.toLong() step 2L) print(i); println() + for (i in 'a' .. 'd' step 2) print(i); println() + for (i in 'a' until 'd' step 2) print(i); println() + for (i in 'd' downTo 'a' step 2) print(i); println() } \ No newline at end of file