From 9503627864a6f5cb33ff7328ed5020bd13c2a2fe Mon Sep 17 00:00:00 2001 From: Elena Lepilkina Date: Tue, 22 Jun 2021 12:57:13 +0300 Subject: [PATCH] Changed an order of processing loops in ForLoopsLowering --- .../jetbrains/kotlin/backend/common/ir/Ir.kt | 4 +- .../common/lower/loops/ForLoopsLowering.kt | 40 ++----- .../handlers/IndexedGetIterationHandlers.kt | 2 +- .../backend/konan/KonanLoweringPhases.kt | 23 ++-- .../kotlin/backend/konan/ToplevelPhases.kt | 2 +- ...=> FunctionsWithoutBoundCheckGenerator.kt} | 8 +- .../jetbrains/kotlin/backend/konan/ir/Ir.kt | 8 +- .../backend/konan/lower/VarargLowering.kt | 2 +- .../KonanBCEForLoopBodyTransformer.kt | 107 ++++++++++-------- .../tests/codegen/bce/arraysForLoops.kt | 18 +-- kotlin-native/runtime/src/main/cpp/Arrays.cpp | 40 +++---- 11 files changed, 122 insertions(+), 132 deletions(-) rename kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/{FunctionsWithoutBCGenerator.kt => FunctionsWithoutBoundCheckGenerator.kt} (94%) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt index d2d5b42a30b..16f861cb98a 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt @@ -332,9 +332,9 @@ abstract class Symbols(val context: T, irBuiltIns: open val unsafeCoerceIntrinsic: IrSimpleFunctionSymbol? = null - open val getWithoutBCName: Name? = null + open val getWithoutBoundCheckName: Name? = null - open val setWithoutBCName: Name? = null + open val setWithoutBoundCheckName: Name? = null companion object { fun isLateinitIsInitializedPropertyGetter(symbol: IrFunctionSymbol): Boolean = diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt index 7560a3a5991..9591ddd3f58 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt @@ -118,35 +118,14 @@ class ForLoopsLowering(val context: CommonBackendContext, val loopBodyTransforme * Abstract class for additional for-loop bodies transformations. */ abstract class ForLoopBodyTransformer : IrElementTransformerVoid() { - protected lateinit var mainLoopVariable: IrVariable - protected lateinit var loopHeader: ForLoopHeader - protected lateinit var loopVariableComponents: Map - protected lateinit var context: CommonBackendContext - open fun initialize( - context: CommonBackendContext, - loopVariable: IrVariable, - forLoopHeader: ForLoopHeader, - loopComponents: Map - ) { - this.context = context - mainLoopVariable = loopVariable - loopHeader = forLoopHeader - loopVariableComponents = loopComponents - } - - fun transform( + abstract fun transform( context: CommonBackendContext, irExpression: IrExpression, loopVariable: IrVariable, forLoopHeader: ForLoopHeader, loopComponents: Map - ) { - initialize(context, loopVariable, forLoopHeader, loopComponents) - irExpression.transformChildrenVoid(this) - } - - open fun shouldTransform(context: CommonBackendContext) = true + ) } private class RangeLoopTransformer( @@ -162,6 +141,7 @@ private class RangeLoopTransformer( fun getScopeOwnerSymbol() = currentScope?.scope?.scopeOwnerSymbol ?: container.symbol override fun visitBlock(expression: IrBlock): IrExpression { + val returnExpression = super.visitBlock(expression) as IrBlock // LoopExpressionGenerator in psi2ir lowers `for (loopVar in ) { // Loop body }` into an IrBlock with origin FOR_LOOP. // This block has 2 statements: // @@ -178,21 +158,21 @@ private class RangeLoopTransformer( // `withIndex()` call, a progression such as `10 downTo 1`). However in some cases (e.g., for `withIndex()`), we also need to // examine the while loop to determine if we CAN optimize the loop. if (expression.origin != IrStatementOrigin.FOR_LOOP) { - return super.visitBlock(expression) // Not a for-loop block. + return returnExpression // Not a for-loop block. } - with(expression.statements) { - assert(size == 2) { "Expected 2 statements in for-loop block, was:\n${expression.dump()}" } + with(returnExpression.statements) { + assert(size == 2) { "Expected 2 statements in for-loop block, was:\n${returnExpression.dump()}" } val iteratorVariable = get(0) as IrVariable assert(iteratorVariable.origin == IrDeclarationOrigin.FOR_LOOP_ITERATOR) { "Expected FOR_LOOP_ITERATOR origin for iterator variable, was:\n${iteratorVariable.dump()}" } val loopHeader = headerProcessor.extractHeader(iteratorVariable) - ?: return super.visitBlock(expression) // The iterable in the header is not supported. + ?: return returnExpression // The iterable in the header is not supported. val loweredHeader = lowerHeader(iteratorVariable, loopHeader) val oldLoop = get(1) as IrWhileLoop assert(oldLoop.origin == IrStatementOrigin.FOR_LOOP_INNER_WHILE) { "Expected FOR_LOOP_INNER_WHILE origin for while loop, was:\n${oldLoop.dump()}" } val (newLoop, loopReplacementExpression) = lowerWhileLoop(oldLoop, loopHeader) - ?: return super.visitBlock(expression) // Cannot lower the loop. + ?: return returnExpression // Cannot lower the loop. // We can lower both the header and while loop. // Update mapping from old to new loop so we can later update references in break/continue. @@ -202,7 +182,7 @@ private class RangeLoopTransformer( set(1, loopReplacementExpression) } - return super.visitBlock(expression) + return returnExpression } /** @@ -296,7 +276,7 @@ private class RangeLoopTransformer( it } } - if (newBody != null && loopBodyTransformer != null && loopBodyTransformer.shouldTransform(context)) { + if (newBody != null && loopBodyTransformer != null) { loopBodyTransformer.transform(context, newBody, mainLoopVariable, loopHeader, loopVariableComponents) } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/IndexedGetIterationHandlers.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/IndexedGetIterationHandlers.kt index 349c70d246a..e670c8651cc 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/IndexedGetIterationHandlers.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/IndexedGetIterationHandlers.kt @@ -81,7 +81,7 @@ internal class ArrayIterationHandler(context: CommonBackendContext) : IndexedGet get() = getClass()!!.getPropertyGetter("size")!!.owner private val getFunctionName: Name - get() = context.ir.symbols.getWithoutBCName ?: OperatorNameConventions.GET + get() = context.ir.symbols.getWithoutBoundCheckName ?: OperatorNameConventions.GET override val IrType.getFunction get() = getClass()!!.functions.single { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt index e8aa96957e0..94c2fa0060d 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt @@ -10,7 +10,7 @@ import org.jetbrains.kotlin.backend.common.lower.loops.ForLoopsLowering import org.jetbrains.kotlin.backend.common.lower.optimizations.FoldConstantLowering import org.jetbrains.kotlin.backend.common.lower.optimizations.PropertyAccessorInlineLowering import org.jetbrains.kotlin.backend.common.phaser.* -import org.jetbrains.kotlin.backend.konan.ir.FunctionsWithoutBCGenerator +import org.jetbrains.kotlin.backend.konan.ir.FunctionsWithoutBoundCheckGenerator import org.jetbrains.kotlin.backend.konan.lower.* import org.jetbrains.kotlin.backend.konan.lower.FinallyBlocksLowering import org.jetbrains.kotlin.backend.konan.lower.InitializersLowering @@ -258,12 +258,21 @@ internal val rangeContainsLoweringPhase = makeKonanFileLoweringPhase( description = "Optimizes calls to contains() for ClosedRanges" ) +internal val functionsWithoutBoundCheck = makeKonanModuleOpPhase( + name = "FunctionsWithoutBoundCheckGenerator", + description = "Functions without bounds check generation", + op = { context, _ -> + FunctionsWithoutBoundCheckGenerator(context).generate() + } +) + internal val forLoopsPhase = makeKonanFileOpPhase( { context, irFile -> ForLoopsLowering(context, KonanBCEForLoopBodyTransformer()).lower(irFile) }, name = "ForLoops", - description = "For loops lowering" + description = "For loops lowering", + prerequisite = setOf(functionsWithoutBoundCheck) ) internal val dataClassesPhase = makeKonanFileLoweringPhase( @@ -341,19 +350,11 @@ internal val interopPhase = makeKonanFileLoweringPhase( prerequisite = setOf(inlinePhase, localFunctionsPhase, functionReferencePhase) ) -internal val functionsWithoutBC = makeKonanModuleOpPhase( - name = "FunctionsWithoutBCGenerator", - description = "Functions without bounds check generation", - op = { context, _ -> - FunctionsWithoutBCGenerator(context).generate() - } -) - internal val varargPhase = makeKonanFileLoweringPhase( ::VarargInjectionLowering, name = "Vararg", description = "Vararg lowering", - prerequisite = setOf(functionReferencePhase, defaultParameterExtentPhase, interopPhase, functionsWithoutBC) + prerequisite = setOf(functionReferencePhase, defaultParameterExtentPhase, interopPhase, functionsWithoutBoundCheck) ) internal val compileTimeEvaluatePhase = makeKonanFileLoweringPhase( diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index 37ea3d6f1e6..8fbbae080c3 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -393,7 +393,7 @@ private val backendCodegen = namedUnitPhase( name = "Backend codegen", description = "Backend code generation", lower = takeFromContext { it.irModule!! } then - functionsWithoutBC then + functionsWithoutBoundCheck then allLoweringsPhase then // Lower current module first. dependenciesLowerPhase then // Then lower all libraries in topological order. // With that we guarantee that inline functions are unlowered while being inlined. diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/FunctionsWithoutBCGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/FunctionsWithoutBoundCheckGenerator.kt similarity index 94% rename from kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/FunctionsWithoutBCGenerator.kt rename to kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/FunctionsWithoutBoundCheckGenerator.kt index 841c21c3246..af9cab7e52a 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/FunctionsWithoutBCGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/FunctionsWithoutBoundCheckGenerator.kt @@ -27,7 +27,7 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.util.OperatorNameConventions // Generate additional functions for array set and get operators without bounds checking. -internal class FunctionsWithoutBCGenerator(val context: KonanBackendContext) { +internal class FunctionsWithoutBoundCheckGenerator(val context: KonanBackendContext) { private val symbols = context.ir.symbols private fun generateFunction(baseFunction: IrSimpleFunction, functionName: Name) = @@ -55,7 +55,7 @@ internal class FunctionsWithoutBCGenerator(val context: KonanBackendContext) { val setWithoutBEAnnotations = baseFunction.annotations.map { annotation -> annotation.deepCopyWithSymbols().also { copy -> if (copy.isAnnotationWithEqualFqName(KonanFqNames.gcUnsafeCall)) { - val value = "${annotation.getAnnotationStringValue("callee")}_without_BC" + val value = "${annotation.getAnnotationStringValue("callee")}_without_BoundCheck" copy.putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, value)) } @@ -69,10 +69,10 @@ internal class FunctionsWithoutBCGenerator(val context: KonanBackendContext) { val arraysClasses = symbols.primitiveArrays.values + symbols.array arraysClasses.forEach { classSymbol -> val setFunction = classSymbol.owner.functions.single { it.name == OperatorNameConventions.SET } - classSymbol.owner.addMember(generateFunction(setFunction, KonanNameConventions.setWithoutBC)) + classSymbol.owner.addMember(generateFunction(setFunction, KonanNameConventions.setWithoutBoundCheck)) val getFunction = classSymbol.owner.functions.single { it.descriptor.name == OperatorNameConventions.GET } - classSymbol.owner.addMember(generateFunction(getFunction, KonanNameConventions.getWithoutBC)) + classSymbol.owner.addMember(generateFunction(getFunction, KonanNameConventions.getWithoutBoundCheck)) } } } \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt index 2da05476d42..4c3afc6129b 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt @@ -43,8 +43,8 @@ import org.jetbrains.kotlin.types.Variance import kotlin.properties.Delegates object KonanNameConventions { - val setWithoutBC = Name.special("") - val getWithoutBC = Name.special("") + val setWithoutBoundCheck = Name.special("") + val getWithoutBoundCheck = Name.special("") } // This is what Context collects about IR. @@ -575,9 +575,9 @@ internal class KonanSymbols( val topLevelSuite = getKonanTestClass("TopLevelSuite") val testFunctionKind = getKonanTestClass("TestFunctionKind") - override val getWithoutBCName: Name? = KonanNameConventions.getWithoutBC + override val getWithoutBoundCheckName: Name? = KonanNameConventions.getWithoutBoundCheck - override val setWithoutBCName: Name? = KonanNameConventions.setWithoutBC + override val setWithoutBoundCheckName: Name? = KonanNameConventions.setWithoutBoundCheck private val testFunctionKindCache = TestProcessor.FunctionKind.values().associate { val symbol = if (it.runtimeKindString.isEmpty()) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/VarargLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/VarargLowering.kt index d3085880de4..ca00dcf4925 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/VarargLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/VarargLowering.kt @@ -208,7 +208,7 @@ internal class VarargInjectionLowering constructor(val context: KonanBackendCont abstract inner class ArrayHandle(val arraySymbol: IrClassSymbol) { val setMethodSymbol = with(arraySymbol.owner.functions) { // For unsigned types use set method. - singleOrNull { it.name == KonanNameConventions.setWithoutBC } ?: single { it.name == OperatorNameConventions.SET } + singleOrNull { it.name == KonanNameConventions.setWithoutBoundCheck } ?: single { it.name == OperatorNameConventions.SET } } val sizeGetterSymbol = arraySymbol.getPropertyGetter("size")!! val copyIntoSymbol = symbols.copyInto[arraySymbol.descriptor]!! diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/KonanBCEForLoopBodyTransformer.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/KonanBCEForLoopBodyTransformer.kt index d4f6322a84a..0767cfe2b65 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/KonanBCEForLoopBodyTransformer.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/KonanBCEForLoopBodyTransformer.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.ir.util.IdSignature import org.jetbrains.kotlin.ir.util.functions import org.jetbrains.kotlin.ir.util.getPropertyGetter import org.jetbrains.kotlin.ir.util.isPrimitiveArray +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.util.OperatorNameConventions // Class contains information about analyzed loop. @@ -30,16 +31,22 @@ internal data class BoundsCheckAnalysisResult(val boundsAreSafe: Boolean, val ar * Transformer for for loops bodies replacing get/set operators on analogs without bounds check where it's possible. */ class KonanBCEForLoopBodyTransformer : ForLoopBodyTransformer() { + lateinit var mainLoopVariable: IrVariable + lateinit var loopHeader: ForLoopHeader + lateinit var loopVariableComponents: Map + lateinit var context: CommonBackendContext + private var analysisResult: BoundsCheckAnalysisResult = BoundsCheckAnalysisResult(false, null) - override fun shouldTransform(context: CommonBackendContext): Boolean { - return context is Context && context.shouldOptimize() - } - - override fun initialize(context: CommonBackendContext, loopVariable: IrVariable, - forLoopHeader: ForLoopHeader, loopComponents: Map) { - super.initialize(context, loopVariable, forLoopHeader, loopComponents) + override fun transform(context: CommonBackendContext, irExpression: IrExpression, loopVariable: IrVariable, + forLoopHeader: ForLoopHeader, loopComponents: Map) { + this.context = context + mainLoopVariable = loopVariable + loopHeader = forLoopHeader + loopVariableComponents = loopComponents analysisResult = analyzeLoopHeader(loopHeader) + if (analysisResult.boundsAreSafe && analysisResult.arrayInLoop != null) + irExpression.transformChildrenVoid(this) } private fun IrExpression.compareIntegerNumericConst(compare: (Long) -> Boolean): Boolean { @@ -60,46 +67,45 @@ class KonanBCEForLoopBodyTransformer : ForLoopBodyTransformer() { private fun IrCall.dispatchReceiverIsGetSizeCall() = (dispatchReceiver as? IrCall)?.let { it.isGetSizeCall() } ?: false private fun lessThanSize(functionCall: IrCall): BoundsCheckAnalysisResult { - var result = false - when (functionCall.symbol.owner.name) { + val boundsAreSafe = when (functionCall.symbol.owner.name) { OperatorNameConventions.DEC -> - if (functionCall.dispatchReceiverIsGetSizeCall()) { - result = true - } + functionCall.dispatchReceiverIsGetSizeCall() OperatorNameConventions.MINUS -> { val value = functionCall.getValueArgument(0) - result = functionCall.dispatchReceiverIsGetSizeCall() && + functionCall.dispatchReceiverIsGetSizeCall() && value?.compareIntegerNumericConst { it > 0 } == true } - OperatorNameConventions.DIV-> { + OperatorNameConventions.DIV -> { val value = functionCall.getValueArgument(0) - result = functionCall.dispatchReceiverIsGetSizeCall() && + functionCall.dispatchReceiverIsGetSizeCall() && value?.compareFloatNumericConst { it > 1 } == true } + else -> false } val array = ((functionCall.dispatchReceiver as? IrCall)?.dispatchReceiver as? IrGetValue)?.symbol - return BoundsCheckAnalysisResult(result, array) + return BoundsCheckAnalysisResult(boundsAreSafe, array) } private fun checkLastElement(last: IrExpression, loopHeader: ProgressionLoopHeader): BoundsCheckAnalysisResult { - var result = BoundsCheckAnalysisResult(false, null) - if (last is IrCall) { - result = lessThanSize(last) + return if (last is IrCall) { if (last.isGetSizeCall() && !loopHeader.headerInfo.isLastInclusive) { - result = BoundsCheckAnalysisResult(true, (last.dispatchReceiver as? IrGetValue)?.symbol) + BoundsCheckAnalysisResult(true, (last.dispatchReceiver as? IrGetValue)?.symbol) + } else { + lessThanSize(last) } + } else { + BoundsCheckAnalysisResult(false, null) } - return result } private fun IrExpression.isProgressionPropertyGetter(propertyName: String) = this is IrCall && symbol.owner.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR && (symbol.signature as? IdSignature.AccessorSignature)?.propertySignature?.asPublic()?.shortName == propertyName && - dispatchReceiver?.type?.getClass()?.symbol in context.ir.symbols.progressionClasses + dispatchReceiver?.type?.getClass()?.symbol in context.ir.symbols.progressionClasses private fun analyzeLoopHeader(loopHeader: ForLoopHeader): BoundsCheckAnalysisResult { - analysisResult = BoundsCheckAnalysisResult(false, null) - when(loopHeader) { + var analysisResult = BoundsCheckAnalysisResult(false, null) + when (loopHeader) { is ProgressionLoopHeader -> when (loopHeader.headerInfo.direction) { ProgressionDirection.INCREASING -> { @@ -112,6 +118,8 @@ class KonanBCEForLoopBodyTransformer : ForLoopBodyTransformer() { if (loopHeader.headerInfo.last is IrCall) { val functionCall = (loopHeader.headerInfo.last as IrCall) // Case of range with step - `for (i in 0..array.size - 1 step n)`. + // There is a temporary variable `val nestedLast = array.size - 1` + // and `last` is computed as `getProgressionLastElement(0, nestedLast, n)` if (loopHeader.headerInfo.progressionType.getProgressionLastElementFunction == functionCall.symbol) { val nestedLastVariable = functionCall.getValueArgument(1) if (nestedLastVariable is IrGetValue && nestedLastVariable.symbol.owner is IrVariable) { @@ -130,6 +138,8 @@ class KonanBCEForLoopBodyTransformer : ForLoopBodyTransformer() { if (loopHeader.headerInfo.last is IrCall) { val functionCall = (loopHeader.headerInfo.last as IrCall) // Case of range with step - for (i in array.size - 1 downTo 0 step n). + // There is a temporary variable `val nestedFirst = array.size - 1` + // and `last` is computed as `getProgressionLastElement(nestedFirst, 0, n)` if (loopHeader.headerInfo.progressionType.getProgressionLastElementFunction == functionCall.symbol) { if (functionCall.getValueArgument(1)?.compareIntegerNumericConst { it >= valueToCompare } == true) { boundsAreSafe = true @@ -140,16 +150,13 @@ class KonanBCEForLoopBodyTransformer : ForLoopBodyTransformer() { } if (!boundsAreSafe) return analysisResult - when (loopHeader.headerInfo.first) { - is IrCall -> { - val functionCall = (loopHeader.headerInfo.first as IrCall) - analysisResult = lessThanSize(functionCall) - } - is IrGetValue -> { - (((loopHeader.headerInfo.first as IrGetValue).symbol.owner as? IrVariable)?.initializer as? IrCall)?.let { + when (val first = loopHeader.headerInfo.first) { + is IrCall -> + analysisResult = lessThanSize(first) + is IrGetValue -> + ((first.symbol.owner as? IrVariable)?.initializer as? IrCall)?.let { analysisResult = lessThanSize(it) } - } } } ProgressionDirection.UNKNOWN -> @@ -177,7 +184,7 @@ class KonanBCEForLoopBodyTransformer : ForLoopBodyTransformer() { } is WithIndexLoopHeader -> - when(loopHeader.nestedLoopHeader) { + when (loopHeader.nestedLoopHeader) { is IndexedGetLoopHeader -> { analysisResult = BoundsCheckAnalysisResult(true, ((loopHeader.loopInitStatements[0] as? IrVariable)?.initializer as? IrGetValue)?.symbol) @@ -190,14 +197,14 @@ class KonanBCEForLoopBodyTransformer : ForLoopBodyTransformer() { private fun replaceOperators(expression: IrCall, index: IrExpression, safeIndexVariables: List): IrExpression { if (index is IrGetValue && index.symbol.owner in safeIndexVariables) { - val operatorWithoutBC = expression.dispatchReceiver!!.type.getClass()!!.functions.singleOrNull { + val operatorWithoutBoundCheck = expression.dispatchReceiver!!.type.getClass()!!.functions.singleOrNull { if (expression.symbol.owner.name == OperatorNameConventions.SET) - it.name == KonanNameConventions.setWithoutBC + it.name == KonanNameConventions.setWithoutBoundCheck else - it.name == KonanNameConventions.getWithoutBC + it.name == KonanNameConventions.getWithoutBoundCheck } ?: return expression return IrCallImpl( - expression.startOffset, expression.endOffset, expression.type, operatorWithoutBC.symbol, + expression.startOffset, expression.endOffset, expression.type, operatorWithoutBoundCheck.symbol, typeArgumentsCount = expression.typeArgumentsCount, valueArgumentsCount = expression.valueArgumentsCount).apply { dispatchReceiver = expression.dispatchReceiver @@ -210,28 +217,28 @@ class KonanBCEForLoopBodyTransformer : ForLoopBodyTransformer() { } override fun visitCall(expression: IrCall): IrExpression { - if (!analysisResult.boundsAreSafe || analysisResult.arrayInLoop == null) - return expression - if (expression.symbol.owner.name != OperatorNameConventions.SET && expression.symbol.owner.name != OperatorNameConventions.GET) { - expression.transformChildrenVoid() - return expression - } + if (expression.symbol.owner.name != OperatorNameConventions.SET && expression.symbol.owner.name != OperatorNameConventions.GET) + return super.visitCall(expression) if (expression.dispatchReceiver?.type?.isBasicArray() != true || (expression.dispatchReceiver as? IrGetValue)?.symbol != analysisResult.arrayInLoop) - return expression + return super.visitCall(expression) // Analyze arguments of set/get operator. - val index = expression.getValueArgument(0) - return when(loopHeader) { + val index = expression.getValueArgument(0)!! + return when (loopHeader) { is ProgressionLoopHeader -> with(loopHeader as ProgressionLoopHeader) { - replaceOperators(expression, index!!, listOf(mainLoopVariable, inductionVariable)) + replaceOperators(expression, index, listOf(mainLoopVariable, inductionVariable)) } is WithIndexLoopHeader -> with(loopHeader as WithIndexLoopHeader) { - when(nestedLoopHeader) { + when (nestedLoopHeader) { is IndexedGetLoopHeader -> - replaceOperators(expression, index!!, listOfNotNull(indexVariable, loopVariableComponents[1])) + replaceOperators(expression, index, listOfNotNull(indexVariable, loopVariableComponents[1])) is ProgressionLoopHeader -> - replaceOperators(expression, index!!, + // Case of `for ((index, value) in (0..array.size - 1 step n).withIndex())`. + // Both `index` (progression size less than array size) + // and `value` (progression start and end element are inside bounds) + // are safe variables if use them in get/set operators. + replaceOperators(expression, index, listOfNotNull(indexVariable, loopVariableComponents[1], loopVariableComponents[2]) ) else -> expression diff --git a/kotlin-native/backend.native/tests/codegen/bce/arraysForLoops.kt b/kotlin-native/backend.native/tests/codegen/bce/arraysForLoops.kt index 7bdeca6f047..fbc9aa36014 100644 --- a/kotlin-native/backend.native/tests/codegen/bce/arraysForLoops.kt +++ b/kotlin-native/backend.native/tests/codegen/bce/arraysForLoops.kt @@ -2,6 +2,8 @@ * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license * that can be found in the LICENSE file. */ +package codegen.bce.arraysForLoops + import kotlin.test.* @Test fun forEachIndexedTest() { @@ -159,7 +161,7 @@ import kotlin.test.* @Test fun forRangeToWithStep() { val array = Array(10) { 0L } val array1 = Array(3) { 0L } - var j = 4 + var j = 8 assertFailsWith { for (i in 0..array.size - 1 step 2) { @@ -202,7 +204,7 @@ import kotlin.test.* @Test fun forUntilWithStep() { val array = CharArray(10) { '0' } val array1 = CharArray(3) { '0' } - var j = 4 + var j = 8 assertFailsWith { for (i in 0 until array.size step 2) { @@ -213,7 +215,7 @@ import kotlin.test.* assertFailsWith { for (i in 0 until array.size step 2) { - array[i + 1] = '6' + array[i + 3] = '6' } } @@ -239,7 +241,7 @@ import kotlin.test.* @Test fun forDownToWithStep() { val array = Array(10) { 0L } val array1 = Array(3) { 0L } - var j = 4 + var j = 8 assertFailsWith { for (i in array.size - 1 downTo 0 step 2) { @@ -282,7 +284,7 @@ import kotlin.test.* @Test fun forIndiciesWithStep() { val array = Array(10) { 0L } val array1 = Array(3) { 0L } - var j = 4 + var j = 8 assertFailsWith { for (i in array.indices step 2) { @@ -307,7 +309,7 @@ import kotlin.test.* @Test fun forWithIndex() { val array = Array(10) { 100 } val array1 = Array(3) { 0 } - var j = 4 + var j = 8 assertFailsWith { for ((index, value) in array.withIndex()) { @@ -329,7 +331,7 @@ import kotlin.test.* } assertFailsWith { - for ((i, v) in (0..array.size + 1 step 2).withIndex()) { + for ((i, v) in (0..array.size + 30 step 2).withIndex()) { array[i] = 6 } } @@ -344,7 +346,7 @@ import kotlin.test.* @Test fun forReversed() { val array = Array(10) { 100 } val array1 = Array(3) { 0 } - var j = 4 + var j = 8 assertFailsWith { for (i in (0..array.size-1).reversed()) { diff --git a/kotlin-native/runtime/src/main/cpp/Arrays.cpp b/kotlin-native/runtime/src/main/cpp/Arrays.cpp index 159a86f370a..3c9d58adc03 100644 --- a/kotlin-native/runtime/src/main/cpp/Arrays.cpp +++ b/kotlin-native/runtime/src/main/cpp/Arrays.cpp @@ -136,7 +136,7 @@ OBJ_GETTER(Kotlin_Array_get, KConstRef thiz, KInt index) { RETURN_OBJ(*Kotlin_Array_get_value(thiz, index)); } -OBJ_GETTER(Kotlin_Array_get_without_BC, KConstRef thiz, KInt index){ +OBJ_GETTER(Kotlin_Array_get_without_BoundCheck, KConstRef thiz, KInt index){ RETURN_OBJ(*Kotlin_Array_get_value(thiz, index)); } @@ -144,7 +144,7 @@ void Kotlin_Array_set(KRef thiz, KInt index, KConstRef value) { Kotlin_Array_set_value(thiz, index, value); } -void Kotlin_Array_set_without_BC(KRef thiz, KInt index, KConstRef value) { +void Kotlin_Array_set_without_BoundCheck(KRef thiz, KInt index, KConstRef value) { Kotlin_Array_set_value(thiz, index, value); } @@ -199,7 +199,7 @@ KByte Kotlin_ByteArray_get(KConstRef thiz, KInt index) { return Kotlin_ByteArray_get_value(thiz, index); } -KByte Kotlin_ByteArray_get_without_BC(KConstRef thiz, KInt index) { +KByte Kotlin_ByteArray_get_without_BoundCheck(KConstRef thiz, KInt index) { return Kotlin_ByteArray_get_value(thiz, index); } @@ -207,7 +207,7 @@ void Kotlin_ByteArray_set(KRef thiz, KInt index, KByte value) { Kotlin_ByteArray_set_value(thiz, index, value); } -void Kotlin_ByteArray_set_without_BC(KRef thiz, KInt index, KByte value) { +void Kotlin_ByteArray_set_without_BoundCheck(KRef thiz, KInt index, KByte value) { Kotlin_ByteArray_set_value(thiz, index, value); } @@ -489,7 +489,7 @@ KChar Kotlin_CharArray_get(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } -KChar Kotlin_CharArray_get_without_BC(KConstRef thiz, KInt index) { +KChar Kotlin_CharArray_get_without_BoundCheck(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } @@ -497,7 +497,7 @@ void Kotlin_CharArray_set(KRef thiz, KInt index, KChar value) { PrimitiveArraySet(thiz, index, value); } -void Kotlin_CharArray_set_without_BC(KRef thiz, KInt index, KChar value) { +void Kotlin_CharArray_set_without_BoundCheck(KRef thiz, KInt index, KChar value) { PrimitiveArraySet(thiz, index, value); } @@ -524,7 +524,7 @@ KShort Kotlin_ShortArray_get(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } -KShort Kotlin_ShortArray_get_without_BC(KConstRef thiz, KInt index) { +KShort Kotlin_ShortArray_get_without_BoundCheck(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } @@ -532,7 +532,7 @@ void Kotlin_ShortArray_set(KRef thiz, KInt index, KShort value) { PrimitiveArraySet(thiz, index, value); } -void Kotlin_ShortArray_set_without_BC(KRef thiz, KInt index, KShort value) { +void Kotlin_ShortArray_set_without_BoundCheck(KRef thiz, KInt index, KShort value) { PrimitiveArraySet(thiz, index, value); } @@ -545,7 +545,7 @@ KInt Kotlin_IntArray_get(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } -KInt Kotlin_IntArray_get_without_BC(KConstRef thiz, KInt index) { +KInt Kotlin_IntArray_get_without_BoundCheck(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } @@ -553,7 +553,7 @@ void Kotlin_IntArray_set(KRef thiz, KInt index, KInt value) { PrimitiveArraySet(thiz, index, value); } -void Kotlin_IntArray_set_without_BC(KRef thiz, KInt index, KInt value) { +void Kotlin_IntArray_set_without_BoundCheck(KRef thiz, KInt index, KInt value) { PrimitiveArraySet(thiz, index, value); } @@ -638,7 +638,7 @@ KLong Kotlin_LongArray_get(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } -KLong Kotlin_LongArray_get_without_BC(KConstRef thiz, KInt index) { +KLong Kotlin_LongArray_get_without_BoundCheck(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } @@ -646,7 +646,7 @@ void Kotlin_LongArray_set(KRef thiz, KInt index, KLong value) { PrimitiveArraySet(thiz, index, value); } -void Kotlin_LongArray_set_without_BC(KRef thiz, KInt index, KLong value) { +void Kotlin_LongArray_set_without_BoundCheck(KRef thiz, KInt index, KLong value) { PrimitiveArraySet(thiz, index, value); } @@ -659,7 +659,7 @@ KFloat Kotlin_FloatArray_get(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } -KFloat Kotlin_FloatArray_get_without_BC(KConstRef thiz, KInt index) { +KFloat Kotlin_FloatArray_get_without_BoundCheck(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } @@ -667,7 +667,7 @@ void Kotlin_FloatArray_set(KRef thiz, KInt index, KFloat value) { PrimitiveArraySet(thiz, index, value); } -void Kotlin_FloatArray_set_without_BC(KRef thiz, KInt index, KFloat value) { +void Kotlin_FloatArray_set_without_BoundCheck(KRef thiz, KInt index, KFloat value) { PrimitiveArraySet(thiz, index, value); } @@ -680,7 +680,7 @@ KDouble Kotlin_DoubleArray_get(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } -KDouble Kotlin_DoubleArray_get_without_BC(KConstRef thiz, KInt index) { +KDouble Kotlin_DoubleArray_get_without_BoundCheck(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } @@ -688,7 +688,7 @@ void Kotlin_DoubleArray_set(KRef thiz, KInt index, KDouble value) { PrimitiveArraySet(thiz, index, value); } -void Kotlin_DoubleArray_set_without_BC(KRef thiz, KInt index, KDouble value) { +void Kotlin_DoubleArray_set_without_BoundCheck(KRef thiz, KInt index, KDouble value) { PrimitiveArraySet(thiz, index, value); } @@ -701,7 +701,7 @@ KBoolean Kotlin_BooleanArray_get(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } -KBoolean Kotlin_BooleanArray_get_without_BC(KConstRef thiz, KInt index) { +KBoolean Kotlin_BooleanArray_get_without_BoundCheck(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } @@ -709,7 +709,7 @@ void Kotlin_BooleanArray_set(KRef thiz, KInt index, KBoolean value) { PrimitiveArraySet(thiz, index, value); } -void Kotlin_BooleanArray_set_without_BC(KRef thiz, KInt index, KBoolean value) { +void Kotlin_BooleanArray_set_without_BoundCheck(KRef thiz, KInt index, KBoolean value) { PrimitiveArraySet(thiz, index, value); } @@ -722,7 +722,7 @@ KNativePtr Kotlin_NativePtrArray_get(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } -KNativePtr Kotlin_NativePtrArray_get_without_BC(KConstRef thiz, KInt index) { +KNativePtr Kotlin_NativePtrArray_get_without_BoundCheck(KConstRef thiz, KInt index) { return PrimitiveArrayGet(thiz, index); } @@ -730,7 +730,7 @@ void Kotlin_NativePtrArray_set(KRef thiz, KInt index, KNativePtr value) { PrimitiveArraySet(thiz, index, value); } -void Kotlin_NativePtrArray_set_without_BC(KRef thiz, KInt index, KNativePtr value) { +void Kotlin_NativePtrArray_set_without_BoundCheck(KRef thiz, KInt index, KNativePtr value) { PrimitiveArraySet(thiz, index, value); }