JVM_IR reuse loop variable as index variable should happen after LDL
We can't apply "reuse loop variable as index variable" transformation before local declarations lowering, otherwise it will affect captured loop variable behavior, resulting in KT-48626. Since it's JVM-specific, move it to JvmOptimizationLowering.
This commit is contained in:
committed by
TeamCityServer
parent
f62ffeaa0a
commit
d9e4dec810
-3
@@ -56,9 +56,6 @@ interface CommonBackendContext : BackendContext, LoggingContext {
|
||||
val preferJavaLikeCounterLoop: Boolean
|
||||
get() = false
|
||||
|
||||
val reuseLoopVariableAsInductionVariable: Boolean
|
||||
get() = false
|
||||
|
||||
val doWhileCounterLoopOrigin: IrStatementOrigin?
|
||||
get() = null
|
||||
|
||||
|
||||
+4
-99
@@ -8,18 +8,16 @@ package org.jetbrains.kotlin.backend.common.lower.loops
|
||||
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.AbstractVariableRemapper
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||
import org.jetbrains.kotlin.ir.types.isNullable
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.ir.visitors.*
|
||||
@@ -190,102 +188,9 @@ private class RangeLoopTransformer(
|
||||
statements[0] = loweredHeader
|
||||
statements[1] = loopReplacementExpression
|
||||
|
||||
if (context.reuseLoopVariableAsInductionVariable && loopHeader.canReuseLoopVariableAsInductionVariable) {
|
||||
reuseLoopVariableAsInductionVariable(expression)
|
||||
}
|
||||
|
||||
return super.visitBlock(expression)
|
||||
}
|
||||
|
||||
private fun reuseLoopVariableAsInductionVariable(irBlock: IrBlock) {
|
||||
// Given a loop in the form:
|
||||
// {
|
||||
// var inductionVariable = <start>
|
||||
// }
|
||||
// do {
|
||||
// if (!(<whileLoopCondition>)) break
|
||||
// val loopVariable = inductionVariable
|
||||
// <originalLoopBody>
|
||||
// } while ( { inductionVariable += <step>; true } )
|
||||
// replace it with:
|
||||
// {
|
||||
// var loopVariable' = <start>
|
||||
// }
|
||||
// do {
|
||||
// if (!(<whileLoopCondition'>)) break
|
||||
// <originalLoopBody'>
|
||||
// } while ( { loopVariable' += <step>; true } )
|
||||
// where whenLoopCondition' and originalLoopBody' are corresponding statements
|
||||
// with inductionVariable and loopVariable remapped to loopVariable'.
|
||||
//
|
||||
// NB we can do so only with a do-while counter loop as described above,
|
||||
// otherwise it changes semantics of 'continue' inside the loop.
|
||||
|
||||
val header = irBlock.statements[0] as? IrStatementContainer ?: return
|
||||
|
||||
val inductionVariableIndex = header.statements.indexOfFirst { it.isInductionVariable(context) }
|
||||
if (inductionVariableIndex < 0) return
|
||||
val inductionVariable = header.statements[inductionVariableIndex] as IrVariable
|
||||
|
||||
val innerLoop = findInnerDoWhileLoop(irBlock.statements[1]) ?: return
|
||||
if (innerLoop.origin != context.doWhileCounterLoopOrigin) return
|
||||
val loopVariableContainerAndIndex = findLoopVariable(innerLoop) ?: return
|
||||
val (loopVariableContainer, loopVariableIndex) = loopVariableContainerAndIndex
|
||||
val loopVariable = loopVariableContainer.statements[loopVariableIndex] as IrVariable
|
||||
|
||||
val inductionVariableType = inductionVariable.type
|
||||
val loopVariableType = loopVariable.type
|
||||
if (loopVariableType.isNullable()) return
|
||||
if (loopVariableType.classifierOrNull != inductionVariableType.classifierOrNull) return
|
||||
|
||||
val newLoopVariable = IrVariableImpl(
|
||||
loopVariable.startOffset, loopVariable.endOffset, loopVariable.origin,
|
||||
IrVariableSymbolImpl(),
|
||||
loopVariable.name, loopVariableType,
|
||||
isVar = true, // NB original loop variable is 'val'
|
||||
isConst = false, isLateinit = false
|
||||
)
|
||||
newLoopVariable.initializer = inductionVariable.initializer
|
||||
|
||||
header.statements[inductionVariableIndex] = newLoopVariable
|
||||
loopVariableContainer.statements.removeAt(loopVariableIndex)
|
||||
|
||||
val remapper = object : AbstractVariableRemapper() {
|
||||
override fun remapVariable(value: IrValueDeclaration): IrValueDeclaration? =
|
||||
if (value == inductionVariable || value == loopVariable) newLoopVariable else null
|
||||
}
|
||||
irBlock.statements[1].transformChildren(remapper, null)
|
||||
}
|
||||
|
||||
private fun findInnerDoWhileLoop(statement: IrStatement): IrDoWhileLoop? {
|
||||
if (statement is IrDoWhileLoop) {
|
||||
return statement
|
||||
}
|
||||
if (statement is IrWhen) {
|
||||
val branch0Result = statement.branches[0].result
|
||||
if (branch0Result is IrDoWhileLoop)
|
||||
return branch0Result
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun findLoopVariable(doWhileLoop: IrDoWhileLoop): Pair<IrContainerExpression, Int>? {
|
||||
val loopBody = doWhileLoop.body as? IrContainerExpression ?: return null
|
||||
for ((index, statement) in loopBody.statements.withIndex()) {
|
||||
if (statement.isLoopVariable())
|
||||
return Pair(loopBody, index)
|
||||
else if (statement is IrContainerExpression && statement.origin == IrStatementOrigin.FOR_LOOP_NEXT) {
|
||||
val loopVarIndex = statement.statements.indexOfFirst { it.isLoopVariable() }
|
||||
if (loopVarIndex < 0) return null
|
||||
return Pair(statement, loopVarIndex)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun IrStatement.isLoopVariable() =
|
||||
this is IrVariable && origin == IrDeclarationOrigin.FOR_LOOP_VARIABLE
|
||||
|
||||
/**
|
||||
* Lowers the "header" statement that stores the iterator into the loop variable
|
||||
* (e.g., `val it = someIterable.iterator()`) and gather information for building the for-loop
|
||||
|
||||
+27
-28
@@ -44,10 +44,6 @@ interface ForLoopHeader {
|
||||
*/
|
||||
val consumesLoopVariableComponents: Boolean
|
||||
|
||||
/** `true` if it's possible to use loop variable as induction variable in this kind of loop */
|
||||
val canReuseLoopVariableAsInductionVariable: Boolean
|
||||
get() = false
|
||||
|
||||
/** Statements used to initialize an iteration of the loop (e.g., assign loop variable). */
|
||||
fun initializeIteration(
|
||||
loopVariable: IrVariable?,
|
||||
@@ -62,7 +58,7 @@ interface ForLoopHeader {
|
||||
|
||||
internal const val inductionVariableName = "inductionVariable"
|
||||
|
||||
internal fun IrStatement.isInductionVariable(context: CommonBackendContext) =
|
||||
fun IrStatement.isInductionVariable(context: CommonBackendContext) =
|
||||
this is IrVariable &&
|
||||
origin == context.inductionVariableOrigin &&
|
||||
name.asString() == inductionVariableName
|
||||
@@ -75,8 +71,6 @@ abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
|
||||
|
||||
override val consumesLoopVariableComponents = false
|
||||
|
||||
override val canReuseLoopVariableAsInductionVariable get() = true
|
||||
|
||||
val inductionVariable: IrVariable
|
||||
|
||||
protected val stepVariable: IrVariable?
|
||||
@@ -445,51 +439,56 @@ class ProgressionLoopHeader(
|
||||
): LoopReplacement {
|
||||
// Transform loop:
|
||||
// while (<newLoopCondition>) {
|
||||
// {
|
||||
// <loopVarAssignments>
|
||||
// inductionVariable += step
|
||||
// { // FOR_LOOP_NEXT
|
||||
// <initializeLoopIteration>
|
||||
// <inductionVariableUpdate>
|
||||
// }
|
||||
// <originalLoopBody>
|
||||
// }
|
||||
// to:
|
||||
// do {
|
||||
// if (!(<newLoopCondition>)) break
|
||||
// val forLoopVariable = inductionVariable
|
||||
// { // FOR_LOOP_NEXT
|
||||
// if (!(<newLoopCondition>)) break
|
||||
// <initializeLoopIteration>
|
||||
// }
|
||||
// <originalLoopBody>
|
||||
// } while ( { inductionVariable += step; true } )
|
||||
// } while (
|
||||
// {
|
||||
// <inductionVariableUpdate>
|
||||
// true
|
||||
// }
|
||||
// )
|
||||
val bodyBlock = newBody as? IrContainerExpression
|
||||
?: throw AssertionError("newBody: ${newBody?.dump()}")
|
||||
val forLoopNextBlock = bodyBlock.statements[0] as? IrContainerExpression
|
||||
?: throw AssertionError("bodyBlock[0]: ${bodyBlock.statements[0].dump()}")
|
||||
if (forLoopNextBlock.origin != IrStatementOrigin.FOR_LOOP_NEXT)
|
||||
throw AssertionError("FOR_LOOP_NEXT expected: ${forLoopNextBlock.dump()}")
|
||||
val loopStep = forLoopNextBlock.statements.last() as? IrSetValue
|
||||
val inductionVariableUpdate = forLoopNextBlock.statements.last() as? IrSetValue
|
||||
?: throw AssertionError("forLoopNextBlock.last: ${forLoopNextBlock.statements.last().dump()}")
|
||||
|
||||
val doWhileLoop = IrDoWhileLoopImpl(oldLoop.startOffset, oldLoop.endOffset, oldLoop.type, context.doWhileCounterLoopOrigin)
|
||||
doWhileLoop.label = oldLoop.label
|
||||
|
||||
val negatedConditionCheck = createNegatedConditionCheck(newLoopCondition, doWhileLoop)
|
||||
bodyBlock.statements[0] = IrCompositeImpl(
|
||||
forLoopNextBlock.startOffset, forLoopNextBlock.endOffset,
|
||||
forLoopNextBlock.type,
|
||||
forLoopNextBlock.origin,
|
||||
).apply {
|
||||
statements.add(createNegatedConditionCheck(newLoopCondition, doWhileLoop))
|
||||
if (forLoopNextBlock.statements.size >= 2)
|
||||
statements.addAll(forLoopNextBlock.statements.subList(0, forLoopNextBlock.statements.lastIndex))
|
||||
}
|
||||
|
||||
bodyBlock.statements[0] = negatedConditionCheck
|
||||
val loopVarAssignments =
|
||||
if (forLoopNextBlock.statements.size == 2)
|
||||
forLoopNextBlock.statements[0]
|
||||
else
|
||||
IrCompositeImpl(
|
||||
forLoopNextBlock.startOffset, forLoopNextBlock.endOffset, forLoopNextBlock.type, null,
|
||||
forLoopNextBlock.statements.subList(0, forLoopNextBlock.statements.lastIndex)
|
||||
)
|
||||
bodyBlock.statements.add(1, loopVarAssignments)
|
||||
doWhileLoop.body = bodyBlock
|
||||
|
||||
val stepStartOffset = loopStep.startOffset
|
||||
val stepEndOffset = loopStep.endOffset
|
||||
val stepStartOffset = inductionVariableUpdate.startOffset
|
||||
val stepEndOffset = inductionVariableUpdate.endOffset
|
||||
val doWhileCondition =
|
||||
IrCompositeImpl(
|
||||
stepStartOffset, stepEndOffset, context.irBuiltIns.booleanType, null,
|
||||
listOf(
|
||||
loopStep,
|
||||
inductionVariableUpdate,
|
||||
IrConstImpl.boolean(stepStartOffset, stepEndOffset, context.irBuiltIns.booleanType, true)
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user