backend: implement tailrec lowering
This commit is contained in:
committed by
SvyatoslavScherbina
parent
701cbebf5c
commit
9a28d648a8
+35
-11
@@ -3,24 +3,48 @@ package org.jetbrains.kotlin.backend.common.descriptors
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
|
||||
/**
|
||||
* @return naturally-ordered list of all parameters available inside the function body.
|
||||
*/
|
||||
internal val CallableDescriptor.allParameters: List<ParameterDescriptor>
|
||||
get() = if (this is ConstructorDescriptor) {
|
||||
listOf(this.constructedClass.thisAsReceiverParameter) + explicitParameters
|
||||
} else {
|
||||
explicitParameters
|
||||
}
|
||||
|
||||
/**
|
||||
* @return naturally-ordered list of the parameters that can have values specified at call site.
|
||||
*/
|
||||
internal val CallableDescriptor.explicitParameters: List<ParameterDescriptor>
|
||||
get() {
|
||||
val receivers = mutableListOf<ParameterDescriptor>()
|
||||
val result = ArrayList<ParameterDescriptor>(valueParameters.size + 2)
|
||||
|
||||
if (this is ConstructorDescriptor)
|
||||
receivers.add(this.constructedClass.thisAsReceiverParameter)
|
||||
this.dispatchReceiverParameter?.let {
|
||||
result.add(it)
|
||||
}
|
||||
|
||||
val dispatchReceiverParameter = this.dispatchReceiverParameter
|
||||
if (dispatchReceiverParameter != null)
|
||||
receivers.add(dispatchReceiverParameter)
|
||||
this.extensionReceiverParameter?.let {
|
||||
result.add(it)
|
||||
}
|
||||
|
||||
val extensionReceiverParameter = this.extensionReceiverParameter
|
||||
if (extensionReceiverParameter != null)
|
||||
receivers.add(extensionReceiverParameter)
|
||||
result.addAll(valueParameters)
|
||||
|
||||
return receivers + this.valueParameters
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parameter in the original function corresponding to given parameter of this function.
|
||||
*
|
||||
* Note: `parameter.original` doesn't seem to be always the parameter of `this.original`.
|
||||
*
|
||||
* @param parameter must be declared in this function
|
||||
*/
|
||||
fun CallableDescriptor.getOriginalParameter(parameter: ParameterDescriptor): ParameterDescriptor = when (parameter) {
|
||||
is ValueParameterDescriptor -> this.original.valueParameters[parameter.index]
|
||||
this.dispatchReceiverParameter -> this.original.dispatchReceiverParameter!!
|
||||
this.extensionReceiverParameter -> this.original.extensionReceiverParameter!!
|
||||
else -> TODO("$parameter in $this")
|
||||
}
|
||||
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
package org.jetbrains.kotlin.backend.common.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.BackendContext
|
||||
import org.jetbrains.kotlin.backend.common.DeepCopyIrTreeWithDeclarations
|
||||
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.collectTailRecursionCalls
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.explicitParameters
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.getOriginalParameter
|
||||
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.util.getArguments
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
|
||||
/**
|
||||
* This pass lowers tail recursion calls in `tailrec` functions.
|
||||
*
|
||||
* Note: it currently can't handle local functions and classes declared in default arguments.
|
||||
* See [DeepCopyIrTreeWithDeclarations].
|
||||
*/
|
||||
internal class TailrecLowering(val context: BackendContext) : FunctionLoweringPass {
|
||||
override fun lower(irFunction: IrFunction) {
|
||||
lowerTailRecursionCalls(context, irFunction)
|
||||
}
|
||||
}
|
||||
|
||||
private fun lowerTailRecursionCalls(context: BackendContext, irFunction: IrFunction) {
|
||||
val tailRecursionCalls = collectTailRecursionCalls(irFunction)
|
||||
if (tailRecursionCalls.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val descriptor = irFunction.descriptor
|
||||
val oldBody = irFunction.body as IrBlockBody
|
||||
val builder = context.createIrBuilder(descriptor).at(oldBody)
|
||||
|
||||
val parameters = descriptor.explicitParameters
|
||||
|
||||
irFunction.body = builder.irBlockBody {
|
||||
// Define variables containing current values of parameters:
|
||||
val parameterToVariable = parameters.associate {
|
||||
it to defineTemporaryVar(irGet(it), nameHint = it.suggestVariableName())
|
||||
}
|
||||
// (these variables are to be updated on any tail call).
|
||||
|
||||
+irWhile().apply {
|
||||
val loop = this
|
||||
condition = irTrue()
|
||||
|
||||
body = irBlock(startOffset, endOffset, resultType = context.builtIns.unitType) {
|
||||
// Read variables containing current values of parameters:
|
||||
val parameterToNew = parameters.associate {
|
||||
val variable = parameterToVariable[it]!!
|
||||
it to defineTemporary(irGet(variable), nameHint = it.suggestVariableName())
|
||||
}
|
||||
|
||||
val transformer = BodyTransformer(builder, irFunction, loop,
|
||||
parameterToNew, parameterToVariable, tailRecursionCalls)
|
||||
|
||||
oldBody.statements.forEach {
|
||||
+it.transform(transformer, null)
|
||||
}
|
||||
|
||||
+irBreak(loop)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class BodyTransformer(
|
||||
val builder: IrBuilderWithScope,
|
||||
val irFunction: IrFunction,
|
||||
val loop: IrLoop,
|
||||
val parameterToNew: Map<ParameterDescriptor, ValueDescriptor>,
|
||||
val parameterToVariable: Map<ParameterDescriptor, VariableDescriptor>,
|
||||
val tailRecursionCalls: Set<IrCall>
|
||||
) : IrElementTransformerVoid() {
|
||||
|
||||
val parameters = irFunction.descriptor.explicitParameters
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
val value = parameterToNew[expression.descriptor] ?: return expression
|
||||
return builder.at(expression).irGet(value)
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
if (expression !in tailRecursionCalls) {
|
||||
return expression
|
||||
}
|
||||
|
||||
return builder.at(expression).genTailCall(expression)
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.genTailCall(expression: IrCall) = this.irBlock(expression) {
|
||||
// Get all specified arguments:
|
||||
val parameterToArgument = expression.getArguments().map { (parameter, argument) ->
|
||||
expression.descriptor.getOriginalParameter(parameter) to 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`.
|
||||
+irSetVar(parameterToVariable[parameter]!!, argument)
|
||||
}
|
||||
|
||||
val specifiedParameters = parameterToArgument.map { (parameter, _) -> parameter }.toSet()
|
||||
|
||||
// For each unspecified argument set the corresponding variable to default:
|
||||
parameters.filter { it !in specifiedParameters }.forEach { parameter ->
|
||||
|
||||
val originalDefaultValue = irFunction.getDefaultArgumentExpression(parameter) ?:
|
||||
throw Error("no argument specified for $parameter")
|
||||
|
||||
// Copy default value, mapping parameters to variables containing freshly computed arguments:
|
||||
val defaultValue = originalDefaultValue.transform(object : DeepCopyIrTreeWithDeclarations() {
|
||||
override fun mapValueReference(descriptor: ValueDescriptor): ValueDescriptor {
|
||||
return parameterToVariable[descriptor] ?: super.mapValueReference(descriptor)
|
||||
}
|
||||
}, data = null)
|
||||
|
||||
+irSetVar(parameterToVariable[parameter]!!, defaultValue)
|
||||
}
|
||||
|
||||
// Jump to the entry:
|
||||
+irContinue(loop)
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrFunction.getDefaultArgumentExpression(parameter: ParameterDescriptor): IrExpression? {
|
||||
if (parameter !is ValueParameterDescriptor) {
|
||||
return null
|
||||
}
|
||||
|
||||
val body = this.getDefault(parameter) ?: return null
|
||||
|
||||
if (body !is IrExpressionBody) {
|
||||
throw Error("unexpected default argument body: $body")
|
||||
}
|
||||
|
||||
return body.expression
|
||||
}
|
||||
|
||||
private fun ParameterDescriptor.suggestVariableName(): String = if (name.isSpecial) {
|
||||
val oldNameStr = name.asString()
|
||||
"$" + oldNameStr.substring(1, oldNameStr.length - 1)
|
||||
} else {
|
||||
name.identifier
|
||||
}
|
||||
+10
-7
@@ -25,13 +25,6 @@ internal class KonanLower(val context: Context) {
|
||||
phaser.phase(KonanPhase.LOWER_ENUMS) {
|
||||
EnumClassLowering(context).run(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_DEFAULT_PARAMETER_EXTENT) {
|
||||
DefaultArgumentStubGenerator(context).runOnFilePostfix(irFile)
|
||||
DefaultParameterInjector(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_BUILTIN_OPERATORS) {
|
||||
BuiltinOperatorLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_SHARED_VARIABLES) {
|
||||
SharedVariablesLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
@@ -48,6 +41,16 @@ internal class KonanLower(val context: Context) {
|
||||
phaser.phase(KonanPhase.LOWER_LOCAL_FUNCTIONS) {
|
||||
LocalDeclarationsLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_TAILREC) {
|
||||
TailrecLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_DEFAULT_PARAMETER_EXTENT) {
|
||||
DefaultArgumentStubGenerator(context).runOnFilePostfix(irFile)
|
||||
DefaultParameterInjector(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_BUILTIN_OPERATORS) {
|
||||
BuiltinOperatorLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_INNER_CLASSES) {
|
||||
InnerClassLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
|
||||
+1
@@ -26,6 +26,7 @@ enum class KonanPhase(val description: String,
|
||||
/* ... ... */ LOWER_INITIALIZERS("Initializers lowering"),
|
||||
/* ... ... */ BRIDGES_BUILDING("Bridges building"),
|
||||
/* ... ... */ LOWER_DELEGATION("Delegation lowering"),
|
||||
/* ... ... */ LOWER_TAILREC("tailrec lowering"),
|
||||
/* ... */ BITCODE("LLVM BitCode Generation"),
|
||||
/* ... ... */ RTTI("RTTI Generation"),
|
||||
/* ... ... */ CODEGEN("Code Generation"),
|
||||
|
||||
+2
-3
@@ -1,6 +1,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.explicitParameters
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.konan.KonanBackendContext
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalClass
|
||||
@@ -109,9 +110,7 @@ private class CallableReferencesUnbinder(val lower: CallableReferenceLowering,
|
||||
val boundArgs = expression.getArguments()
|
||||
val boundParams = boundArgs.map { it.first }
|
||||
|
||||
val allParams = with (descriptor) {
|
||||
listOf(dispatchReceiverParameter, extensionReceiverParameter).filterNotNull() + valueParameters
|
||||
}
|
||||
val allParams = descriptor.explicitParameters
|
||||
val unboundParams = allParams - boundParams
|
||||
|
||||
val startOffset = expression.startOffset
|
||||
|
||||
+18
-1
@@ -1,8 +1,11 @@
|
||||
package org.jetbrains.kotlin.ir.builders
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ValueDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrLoop
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
inline fun IrBuilderWithScope.irLetSequence(
|
||||
@@ -16,4 +19,18 @@ inline fun IrBuilderWithScope.irLetSequence(
|
||||
): IrExpression = irBlock(startOffset, endOffset, origin, resultType) {
|
||||
val irTemporary = defineTemporary(value, nameHint)
|
||||
this.body(irTemporary)
|
||||
}
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.irWhile(origin: IrStatementOrigin? = null) =
|
||||
IrWhileLoopImpl(startOffset, endOffset, context.builtIns.unitType, origin)
|
||||
|
||||
fun IrBuilderWithScope.irBreak(loop: IrLoop) =
|
||||
IrBreakImpl(startOffset, endOffset, context.builtIns.nothingType, loop)
|
||||
|
||||
fun IrBuilderWithScope.irContinue(loop: IrLoop) =
|
||||
IrContinueImpl(startOffset, endOffset, context.builtIns.nothingType, loop)
|
||||
|
||||
fun IrBuilderWithScope.irTrue() = IrConstImpl.boolean(startOffset, endOffset, context.builtIns.booleanType, true)
|
||||
|
||||
fun IrBuilderWithScope.irGet(value: ValueDescriptor) =
|
||||
IrGetValueImpl(startOffset, endOffset, value)
|
||||
|
||||
Reference in New Issue
Block a user