JVM_IR: Backwards compatible handling of default tailrec params.

Before 1.4 tailrec function default arguments were evaluated
right-to-left instead of left-to-right. This is controlled
by a compile-time flag. This change adds support for the
right-to-left evaluation order when that flag is not set.
This commit is contained in:
Mads Ager
2019-12-16 14:16:14 +01:00
committed by max-kammerer
parent 90a1b15b77
commit 82f48cdd11
4 changed files with 59 additions and 33 deletions
@@ -16,10 +16,15 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
import org.jetbrains.kotlin.backend.common.collectTailRecursionCalls
import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
@@ -28,25 +33,21 @@ import org.jetbrains.kotlin.ir.util.getArgumentsWithIr
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
val tailrecPhase = makeIrFilePhase(
::TailrecLowering,
name = "Tailrec",
description = "Handle tailrec calls"
)
/**
* This pass lowers tail recursion calls in `tailrec` functions.
*
* Note: it currently can't handle local functions and classes declared in default arguments.
* See [deepCopyWithVariables].
*/
class TailrecLowering(val context: BackendContext) : FunctionLoweringPass {
open class TailrecLowering(val context: BackendContext) : FunctionLoweringPass {
override fun lower(irFunction: IrFunction) {
lowerTailRecursionCalls(context, irFunction)
lowerTailRecursionCalls(context, irFunction, useProperComputationOrderOfTailrecDefaultParameters())
}
open fun useProperComputationOrderOfTailrecDefaultParameters() = true
}
private fun lowerTailRecursionCalls(context: BackendContext, irFunction: IrFunction) {
private fun lowerTailRecursionCalls(context: BackendContext, irFunction: IrFunction, properComputationOrderOfTailrecDefaultParameters: Boolean) {
val tailRecursionCalls = collectTailRecursionCalls(irFunction)
if (tailRecursionCalls.isEmpty()) {
return
@@ -77,7 +78,8 @@ private fun lowerTailRecursionCalls(context: BackendContext, irFunction: IrFunct
val transformer = BodyTransformer(
builder, irFunction, loop,
parameterToNew, parameterToVariable, tailRecursionCalls
parameterToNew, parameterToVariable, tailRecursionCalls,
properComputationOrderOfTailrecDefaultParameters
)
oldBody.statements.forEach {
@@ -96,7 +98,8 @@ private class BodyTransformer(
val loop: IrLoop,
val parameterToNew: Map<IrValueParameter, IrValueDeclaration>,
val parameterToVariable: Map<IrValueParameter, IrVariable>,
val tailRecursionCalls: Set<IrCall>
val tailRecursionCalls: Set<IrCall>,
val properComputationOrderOfTailrecDefaultParameters: Boolean
) : IrElementTransformerVoid() {
val parameters = irFunction.explicitParameters
@@ -133,28 +136,31 @@ private class BodyTransformer(
val specifiedParameters = parameterToArgument.map { (parameter, _) -> parameter }.toSet()
// For each unspecified argument set the corresponding variable to default:
parameters.filter { it !in specifiedParameters }.forEach { parameter ->
parameters
.filter { it !in specifiedParameters }
.let { if (properComputationOrderOfTailrecDefaultParameters) it else it.asReversed() }
.forEach { 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:
val defaultValue = originalDefaultValue
.deepCopyWithVariables()
.transform(object : IrElementTransformerVoid() {
// Copy default value, mapping parameters to variables containing freshly computed arguments:
val defaultValue = originalDefaultValue
.deepCopyWithVariables()
.transform(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
expression.transformChildrenVoid(this)
override fun visitGetValue(expression: IrGetValue): IrExpression {
expression.transformChildrenVoid(this)
val variable = parameterToVariable[expression.symbol.owner] ?: return expression
return IrGetValueImpl(
expression.startOffset, expression.endOffset, variable.type,
variable.symbol, expression.origin
)
}
}, data = null)
val variable = parameterToVariable[expression.symbol.owner] ?: return expression
return IrGetValueImpl(
expression.startOffset, expression.endOffset, variable.type,
variable.symbol, expression.origin
)
}
}, data = null)
+irSetVar(parameterToVariable[parameter]!!.symbol, defaultValue)
}
+irSetVar(parameterToVariable[parameter]!!.symbol, defaultValue)
}
// Jump to the entry:
+irContinue(loop)
@@ -223,6 +223,12 @@ private val syntheticAccessorPhase = makeIrFilePhase(
prerequisite = setOf(objectClassPhase, staticDefaultFunctionPhase, interfacePhase)
)
private val tailrecPhase = makeIrFilePhase<JvmBackendContext>(
::JvmTailrecLowering,
name = "Tailrec",
description = "Handle tailrec calls"
)
@Suppress("Reformat")
private val jvmFilePhases =
typeAliasAnnotationMethodsPhase then
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.lower.TailrecLowering
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.languageVersionSettings
class JvmTailrecLowering(context: JvmBackendContext) : TailrecLowering(context) {
override fun useProperComputationOrderOfTailrecDefaultParameters(): Boolean =
context.ir.context.configuration.languageVersionSettings.supportsFeature(LanguageFeature.ProperComputationOrderOfTailrecDefaultParameters)
}
@@ -1,7 +1,5 @@
// !LANGUAGE: -ProperComputationOrderOfTailrecDefaultParameters
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
var counter = 0
fun calc(counter: Int) = if (counter % 2 == 0) "K" else "O"