diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt index 7c2853f49c7..99fa29c9898 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt @@ -50,4 +50,7 @@ interface CommonBackendContext : BackendContext, LoggingContext { fun isSideEffectFree(call: IrCall): Boolean { return false } + + val preferJavaLikeCounterLoop: Boolean + get() = false } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt index aec822a1a2b..bd934c36ded 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt @@ -252,6 +252,8 @@ class ProgressionLoopHeader( context: CommonBackendContext ) : NumericForLoopHeader(headerInfo, builder, context) { + private val preferJavaLikeCounterLoop = context.preferJavaLikeCounterLoop + // For this loop: // // for (i in first()..last() step step()) @@ -331,7 +333,7 @@ class ProgressionLoopHeader( val loopCondition = buildLoopCondition(this@with) LoopReplacement(newLoop, irIfThen(loopCondition, newLoop)) - } else if (!headerInfo.isLastInclusive) { + } else if (preferJavaLikeCounterLoop && !headerInfo.isLastInclusive) { // It is critically important for loop code performance on JVM to "look like" a simple counter loop in Java when possible // (`for (int i = first; i < lastExclusive; ++i) { ... }`). // Otherwise loop-related optimizations will not kick in, resulting in significant performance degradation. diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/IndicesHandlers.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/IndicesHandlers.kt index 2559b08bd7f..72ae87a8e47 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/IndicesHandlers.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/IndicesHandlers.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.builders.irInt import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.getPropertyGetter @@ -23,21 +24,38 @@ import org.jetbrains.kotlin.name.FqName internal abstract class IndicesHandler(protected val context: CommonBackendContext) : ProgressionHandler { + private val preferJavaLikeCounterLoop = context.preferJavaLikeCounterLoop + override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? = with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { - // `last = array.size - 1` (last is inclusive) for the loop `for (i in array.indices)`. - val last = irCall(expression.symbol.owner.extensionReceiverParameter!!.type.sizePropertyGetter) - .apply { dispatchReceiver = expression.extensionReceiver!! } + val last: IrExpression + val lastInclusive: IrExpression? + val isLastInclusive: Boolean + + if (preferJavaLikeCounterLoop) { + // Convert range with inclusive upper bound to exclusive upper bound if possible. + // This affects loop code performance on JVM. + last = irCall(expression.symbol.owner.extensionReceiverParameter!!.type.sizePropertyGetter) + .apply { dispatchReceiver = expression.extensionReceiver!! } + lastInclusive = last.decrement() + isLastInclusive = false + } else { + last = irCall(expression.symbol.owner.extensionReceiverParameter!!.type.sizePropertyGetter) + .apply { dispatchReceiver = expression.extensionReceiver!! } + .decrement() + lastInclusive = null + isLastInclusive = true + } ProgressionHeaderInfo( data, first = irInt(0), last = last, step = irInt(1), - isLastInclusive = false, + isLastInclusive = isLastInclusive, canOverflow = false, direction = ProgressionDirection.INCREASING, - originalLastInclusive = last.decrement() + originalLastInclusive = lastInclusive ) } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/RangeToHandler.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/RangeToHandler.kt index 8c29f0ae5fc..d8a7cfd4afd 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/RangeToHandler.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/handlers/RangeToHandler.kt @@ -26,6 +26,8 @@ import org.jetbrains.kotlin.util.OperatorNameConventions internal class RangeToHandler(private val context: CommonBackendContext) : ProgressionHandler { + private val preferJavaLikeCounterLoop = context.preferJavaLikeCounterLoop + private val progressionElementTypes = context.ir.symbols.progressionElementTypes override val matcher = SimpleCalleeMatcher { @@ -39,9 +41,9 @@ internal class RangeToHandler(private val context: CommonBackendContext) : with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { val last = expression.getValueArgument(0)!! - // Convert range with inclusive upper bound to exclusive upper bound if possible. - // This affects loop code performance on JVM. - if (canUseExclusiveUpperBound(last, data)) { + if (preferJavaLikeCounterLoop && canUseExclusiveUpperBound(last, data)) { + // Convert range with inclusive upper bound to exclusive upper bound if possible. + // This affects loop code performance on JVM. val lastExclusive = last.convertToExclusiveUpperBound() if (lastExclusive != null) { return@with ProgressionHeaderInfo( diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt index 4e660bf4093..d0702ea45eb 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt @@ -212,6 +212,9 @@ class JvmBackendContext( super.handleDeepCopy(fileSymbolMap, classSymbolMap, functionSymbolMap) } + override val preferJavaLikeCounterLoop: Boolean + get() = true + inner class JvmIr( irModuleFragment: IrModuleFragment, symbolTable: SymbolTable