IR: create more temporary vals when optimizing tailrec calls
This is needed so that SharedVariablesLowering doesn't get confused, and SharedVariablesLowering should run after TailrecLowering to properly optimize tailrec calls in inline lambdas.
This commit is contained in:
+1
-8
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.ir.expressions.*
|
|||||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||||
import org.jetbrains.kotlin.ir.types.IdSignatureValues
|
import org.jetbrains.kotlin.ir.types.IdSignatureValues
|
||||||
import org.jetbrains.kotlin.ir.types.isUnit
|
import org.jetbrains.kotlin.ir.types.isUnit
|
||||||
import org.jetbrains.kotlin.ir.util.parentClassOrNull
|
|
||||||
import org.jetbrains.kotlin.ir.util.usesDefaultArguments
|
import org.jetbrains.kotlin.ir.util.usesDefaultArguments
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
|
|
||||||
@@ -99,13 +98,7 @@ fun collectTailRecursionCalls(irFunction: IrFunction): Set<IrCall> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun IrExpression.isUnitRead(): Boolean =
|
private fun IrExpression.isUnitRead(): Boolean =
|
||||||
when (this) {
|
this is IrGetObjectValue && symbol.signature == IdSignatureValues.unit
|
||||||
is IrGetObjectValue -> symbol
|
|
||||||
// On the JVM, if SingletonReferencesLowering has already finished, a `Unit` reference
|
|
||||||
// is now an IrGetField to the INSTANCE field.
|
|
||||||
is IrGetField -> symbol.owner.parentClassOrNull?.symbol
|
|
||||||
else -> null
|
|
||||||
}?.signature == IdSignatureValues.unit
|
|
||||||
|
|
||||||
override fun visitWhen(expression: IrWhen, data: ElementKind) {
|
override fun visitWhen(expression: IrWhen, data: ElementKind) {
|
||||||
expression.branches.forEach {
|
expression.branches.forEach {
|
||||||
|
|||||||
+24
-39
@@ -24,12 +24,13 @@ import org.jetbrains.kotlin.ir.IrElement
|
|||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
|
||||||
import org.jetbrains.kotlin.ir.transformStatement
|
import org.jetbrains.kotlin.ir.transformStatement
|
||||||
|
import org.jetbrains.kotlin.ir.types.makeNullable
|
||||||
import org.jetbrains.kotlin.ir.util.explicitParameters
|
import org.jetbrains.kotlin.ir.util.explicitParameters
|
||||||
import org.jetbrains.kotlin.ir.util.getArgumentsWithIr
|
import org.jetbrains.kotlin.ir.util.getArgumentsWithIr
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
@@ -124,68 +125,52 @@ private class BodyTransformer(
|
|||||||
val parameterToVariable: Map<IrValueParameter, IrVariable>,
|
val parameterToVariable: Map<IrValueParameter, IrVariable>,
|
||||||
val tailRecursionCalls: Set<IrCall>,
|
val tailRecursionCalls: Set<IrCall>,
|
||||||
val properComputationOrderOfTailrecDefaultParameters: Boolean
|
val properComputationOrderOfTailrecDefaultParameters: Boolean
|
||||||
) : IrElementTransformerVoid() {
|
) : VariableRemapper(parameterToNew) {
|
||||||
|
|
||||||
val parameters = irFunction.explicitParameters
|
val parameters = irFunction.explicitParameters
|
||||||
|
|
||||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
|
||||||
expression.transformChildrenVoid(this)
|
|
||||||
val value = parameterToNew[expression.symbol.owner] ?: return expression
|
|
||||||
return builder.at(expression).irGet(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall): IrExpression {
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
expression.transformChildrenVoid(this)
|
expression.transformChildrenVoid(this)
|
||||||
if (expression !in tailRecursionCalls) {
|
if (expression !in tailRecursionCalls) {
|
||||||
return expression
|
return expression
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.at(expression).genTailCall(expression)
|
return builder.at(expression).genTailCall(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrBuilderWithScope.genTailCall(expression: IrCall) = this.irBlock(expression) {
|
private fun IrBuilderWithScope.genTailCall(expression: IrCall) = this.irBlock(expression) {
|
||||||
// Get all specified arguments:
|
// Get all specified arguments:
|
||||||
val parameterToArgument = expression.getArgumentsWithIr().map { (parameter, argument) ->
|
val parameterToArgument = expression.getArgumentsWithIr().associateTo(mutableMapOf()) { (parameter, argument) ->
|
||||||
parameter to argument
|
// Note that we create `val`s for those parameters so that if some default value contains an object
|
||||||
|
// that captures another parameter, it won't capture it as a mutable ref.
|
||||||
|
parameter to irTemporary(argument)
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each specified argument set the corresponding variable to it in the correct order:
|
|
||||||
parameterToArgument.forEach { (parameter, argument) ->
|
|
||||||
at(argument)
|
|
||||||
// Note that argument can use values of parameters, so it is important that
|
|
||||||
// references to parameters are mapped using `parameterToNew`, not `parameterToVariable`.
|
|
||||||
+irSet(parameterToVariable[parameter]!!.symbol, argument)
|
|
||||||
}
|
|
||||||
|
|
||||||
val specifiedParameters = parameterToArgument.map { (parameter, _) -> parameter }.toSet()
|
|
||||||
|
|
||||||
// For each unspecified argument set the corresponding variable to default:
|
// For each unspecified argument set the corresponding variable to default:
|
||||||
parameters
|
parameters
|
||||||
.filter { it !in specifiedParameters }
|
.filter { it !in parameterToArgument }
|
||||||
.let { if (properComputationOrderOfTailrecDefaultParameters) it else it.asReversed() }
|
.let { if (properComputationOrderOfTailrecDefaultParameters) it else it.asReversed() }
|
||||||
.forEach { parameter ->
|
.associateWithTo(parameterToArgument) { parameter ->
|
||||||
|
|
||||||
val originalDefaultValue = parameter.defaultValue?.expression ?: throw Error("no argument specified for $parameter")
|
val originalDefaultValue = parameter.defaultValue?.expression ?: throw Error("no argument specified for $parameter")
|
||||||
|
|
||||||
// Copy default value, mapping parameters to variables containing freshly computed arguments:
|
// Copy default value, mapping parameters to variables containing freshly computed arguments:
|
||||||
val defaultValue = originalDefaultValue
|
val defaultValue = originalDefaultValue
|
||||||
.deepCopyWithVariables()
|
.deepCopyWithVariables().patchDeclarationParents(parent)
|
||||||
.transform(object : IrElementTransformerVoid() {
|
.transform(object : VariableRemapper(parameterToArgument) {
|
||||||
|
|
||||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||||
expression.transformChildrenVoid(this)
|
// If this parameter references a different parameter declared later, produce null:
|
||||||
|
if (expression.symbol.owner.let { it is IrValueParameter && it.parent == irFunction && it !in parameterToArgument })
|
||||||
val variable = parameterToVariable[expression.symbol.owner] ?: return expression
|
return IrConstImpl.defaultValueForType(startOffset, endOffset, expression.type.makeNullable())
|
||||||
return IrGetValueImpl(
|
return super.visitGetValue(expression)
|
||||||
expression.startOffset, expression.endOffset, variable.type,
|
|
||||||
variable.symbol, expression.origin
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}, data = null)
|
}, null)
|
||||||
|
irTemporary(defaultValue)
|
||||||
+irSet(parameterToVariable[parameter]!!.symbol, defaultValue)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy the new `val`s into the `var`s declared outside the loop:
|
||||||
|
parameterToArgument.forEach { (parameter, argument) ->
|
||||||
|
at(argument)
|
||||||
|
+irSet(parameterToVariable[parameter]!!.symbol, irGet(argument))
|
||||||
|
}
|
||||||
|
|
||||||
// Jump to the entry:
|
// Jump to the entry:
|
||||||
+irContinue(loop)
|
+irContinue(loop)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -269,7 +269,6 @@ private val tailrecPhase = makeIrFilePhase(
|
|||||||
::JvmTailrecLowering,
|
::JvmTailrecLowering,
|
||||||
name = "Tailrec",
|
name = "Tailrec",
|
||||||
description = "Handle tailrec calls",
|
description = "Handle tailrec calls",
|
||||||
prerequisite = setOf(localDeclarationsPhase)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private val kotlinNothingValueExceptionPhase = makeIrFilePhase<CommonBackendContext>(
|
private val kotlinNothingValueExceptionPhase = makeIrFilePhase<CommonBackendContext>(
|
||||||
@@ -354,6 +353,7 @@ private val jvmFilePhases = listOf(
|
|||||||
forLoopsPhase,
|
forLoopsPhase,
|
||||||
collectionStubMethodLowering,
|
collectionStubMethodLowering,
|
||||||
jvmInlineClassPhase,
|
jvmInlineClassPhase,
|
||||||
|
tailrecPhase,
|
||||||
makePatchParentsPhase(1),
|
makePatchParentsPhase(1),
|
||||||
|
|
||||||
enumWhenPhase,
|
enumWhenPhase,
|
||||||
@@ -364,8 +364,6 @@ private val jvmFilePhases = listOf(
|
|||||||
returnableBlocksPhase,
|
returnableBlocksPhase,
|
||||||
sharedVariablesPhase,
|
sharedVariablesPhase,
|
||||||
localDeclarationsPhase,
|
localDeclarationsPhase,
|
||||||
|
|
||||||
tailrecPhase,
|
|
||||||
makePatchParentsPhase(2),
|
makePatchParentsPhase(2),
|
||||||
|
|
||||||
jvmLocalClassExtractionPhase,
|
jvmLocalClassExtractionPhase,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR, WASM
|
|
||||||
|
|
||||||
interface IFoo {
|
interface IFoo {
|
||||||
fun foo(): String
|
fun foo(): String
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR, WASM
|
|
||||||
|
|
||||||
interface IFoo {
|
interface IFoo {
|
||||||
fun foo(): String
|
fun foo(): String
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
|
|
||||||
tailrec fun tailrecDefault(fake: Int, fn: () -> String = { "OK" }): String {
|
tailrec fun tailrecDefault(fake: Int, fn: () -> String = { "OK" }): String {
|
||||||
return if (fake == 0)
|
return if (fake == 0)
|
||||||
tailrecDefault(1)
|
tailrecDefault(1)
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// IGNORE_BACKEND: WASM
|
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
|
|
||||||
class C
|
class C
|
||||||
|
|
||||||
fun box(): String =
|
fun box(): String =
|
||||||
|
|||||||
Reference in New Issue
Block a user