JVM_IR: optimize tailrec calls in inline lambdas
^KT-48600 Fixed
This commit is contained in:
+16
-1
@@ -36,7 +36,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
|||||||
* It is also not guaranteed that each returned call is detected as tail recursion by the frontend.
|
* It is also not guaranteed that each returned call is detected as tail recursion by the frontend.
|
||||||
* However any returned call can be correctly optimized as tail recursion.
|
* However any returned call can be correctly optimized as tail recursion.
|
||||||
*/
|
*/
|
||||||
fun collectTailRecursionCalls(irFunction: IrFunction): Set<IrCall> {
|
fun collectTailRecursionCalls(irFunction: IrFunction, followFunctionReference: (IrFunctionReference) -> Boolean): Set<IrCall> {
|
||||||
if ((irFunction as? IrSimpleFunction)?.isTailrec != true) {
|
if ((irFunction as? IrSimpleFunction)?.isTailrec != true) {
|
||||||
return emptySet()
|
return emptySet()
|
||||||
}
|
}
|
||||||
@@ -147,6 +147,21 @@ fun collectTailRecursionCalls(irFunction: IrFunction): Set<IrCall> {
|
|||||||
|
|
||||||
result.add(expression)
|
result.add(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visitFunctionReference(expression: IrFunctionReference, data: ElementKind) {
|
||||||
|
expression.acceptChildren(this, ElementKind.NOT_SURE)
|
||||||
|
// This should match inline lambdas:
|
||||||
|
// tailrec fun foo() {
|
||||||
|
// run { return foo() } // non-local return from `foo`, so this *is* a tail call
|
||||||
|
// }
|
||||||
|
// Whether crossinline lambdas are matched is unimportant, as they can't contain any returns
|
||||||
|
// from `foo` anyway.
|
||||||
|
if (followFunctionReference(expression)) {
|
||||||
|
// If control reaches end of lambda, it will *not* end the current function by default,
|
||||||
|
// so the lambda's body itself is not a tail statement.
|
||||||
|
expression.symbol.owner.body?.accept(this, ElementKind.NOT_SURE)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
irFunction.body?.accept(visitor, ElementKind.TAIL_STATEMENT)
|
irFunction.body?.accept(visitor, ElementKind.TAIL_STATEMENT)
|
||||||
|
|||||||
+17
-5
@@ -62,17 +62,20 @@ open class TailrecLowering(val context: BackendContext) : BodyLoweringPass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun lowerTailRecursionCalls(function: IrFunction) =
|
private fun lowerTailRecursionCalls(function: IrFunction) =
|
||||||
lowerTailRecursionCalls(context, function, useProperComputationOrderOfTailrecDefaultParameters())
|
lowerTailRecursionCalls(context, function, useProperComputationOrderOfTailrecDefaultParameters(), ::followFunctionReference)
|
||||||
|
|
||||||
open fun useProperComputationOrderOfTailrecDefaultParameters() = true
|
open fun useProperComputationOrderOfTailrecDefaultParameters() = true
|
||||||
|
|
||||||
|
open fun followFunctionReference(reference: IrFunctionReference): Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun lowerTailRecursionCalls(
|
private fun lowerTailRecursionCalls(
|
||||||
context: BackendContext,
|
context: BackendContext,
|
||||||
irFunction: IrFunction,
|
irFunction: IrFunction,
|
||||||
properComputationOrderOfTailrecDefaultParameters: Boolean
|
properComputationOrderOfTailrecDefaultParameters: Boolean,
|
||||||
|
followFunctionReference: (IrFunctionReference) -> Boolean
|
||||||
) {
|
) {
|
||||||
val tailRecursionCalls = collectTailRecursionCalls(irFunction)
|
val tailRecursionCalls = collectTailRecursionCalls(irFunction, followFunctionReference)
|
||||||
if (tailRecursionCalls.isEmpty()) {
|
if (tailRecursionCalls.isEmpty()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -104,7 +107,8 @@ private fun lowerTailRecursionCalls(
|
|||||||
val transformer = BodyTransformer(
|
val transformer = BodyTransformer(
|
||||||
builder, irFunction, loop,
|
builder, irFunction, loop,
|
||||||
parameterToNew, parameterToVariable, tailRecursionCalls,
|
parameterToNew, parameterToVariable, tailRecursionCalls,
|
||||||
properComputationOrderOfTailrecDefaultParameters
|
properComputationOrderOfTailrecDefaultParameters,
|
||||||
|
followFunctionReference
|
||||||
)
|
)
|
||||||
|
|
||||||
oldBodyStatements.forEach {
|
oldBodyStatements.forEach {
|
||||||
@@ -124,7 +128,8 @@ private class BodyTransformer(
|
|||||||
val parameterToNew: Map<IrValueParameter, IrValueDeclaration>,
|
val parameterToNew: Map<IrValueParameter, IrValueDeclaration>,
|
||||||
val parameterToVariable: Map<IrValueParameter, IrVariable>,
|
val parameterToVariable: Map<IrValueParameter, IrVariable>,
|
||||||
val tailRecursionCalls: Set<IrCall>,
|
val tailRecursionCalls: Set<IrCall>,
|
||||||
val properComputationOrderOfTailrecDefaultParameters: Boolean
|
val properComputationOrderOfTailrecDefaultParameters: Boolean,
|
||||||
|
val followFunctionReference: (IrFunctionReference) -> Boolean
|
||||||
) : VariableRemapper(parameterToNew) {
|
) : VariableRemapper(parameterToNew) {
|
||||||
|
|
||||||
val parameters = irFunction.explicitParameters
|
val parameters = irFunction.explicitParameters
|
||||||
@@ -137,6 +142,13 @@ private class BodyTransformer(
|
|||||||
return builder.at(expression).genTailCall(expression)
|
return builder.at(expression).genTailCall(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||||
|
if (followFunctionReference(expression)) {
|
||||||
|
expression.symbol.owner.body?.transformChildrenVoid(this)
|
||||||
|
}
|
||||||
|
return super.visitFunctionReference(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().associateTo(mutableMapOf()) { (parameter, argument) ->
|
val parameterToArgument = expression.getArgumentsWithIr().associateTo(mutableMapOf()) { (parameter, argument) ->
|
||||||
|
|||||||
+6
-1
@@ -7,10 +7,15 @@ package org.jetbrains.kotlin.backend.jvm.lower
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.lower.TailrecLowering
|
import org.jetbrains.kotlin.backend.common.lower.TailrecLowering
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.JvmLoweredStatementOrigin
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||||
|
|
||||||
class JvmTailrecLowering(context: JvmBackendContext) : TailrecLowering(context) {
|
class JvmTailrecLowering(context: JvmBackendContext) : TailrecLowering(context) {
|
||||||
override fun useProperComputationOrderOfTailrecDefaultParameters(): Boolean =
|
override fun useProperComputationOrderOfTailrecDefaultParameters(): Boolean =
|
||||||
context.ir.context.configuration.languageVersionSettings.supportsFeature(LanguageFeature.ProperComputationOrderOfTailrecDefaultParameters)
|
context.ir.context.configuration.languageVersionSettings.supportsFeature(LanguageFeature.ProperComputationOrderOfTailrecDefaultParameters)
|
||||||
}
|
|
||||||
|
override fun followFunctionReference(reference: IrFunctionReference): Boolean =
|
||||||
|
reference.origin == JvmLoweredStatementOrigin.INLINE_LAMBDA
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
// KT-16549
|
// KT-16549
|
||||||
// IGNORE_BACKEND: JVM
|
// IGNORE_BACKEND: JVM, JS
|
||||||
// IGNORE_FIR_DIAGNOSTICS_DIFF
|
// IGNORE_FIR_DIAGNOSTICS_DIFF
|
||||||
|
|
||||||
class TailInline {
|
class TailInline {
|
||||||
@@ -7,7 +7,7 @@ class TailInline {
|
|||||||
return action()
|
return action()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var countDown = 10
|
private var countDown = 100000
|
||||||
|
|
||||||
tailrec fun test(): Int {
|
tailrec fun test(): Int {
|
||||||
if (countDown < 5) return countDown
|
if (countDown < 5) return countDown
|
||||||
|
|||||||
Reference in New Issue
Block a user