From 81d77c39f4e35f64e4b8649f1b6236fe1503507e Mon Sep 17 00:00:00 2001 From: Vasily Levchenko Date: Mon, 9 Jan 2017 18:26:24 +0300 Subject: [PATCH] IR: default parameter extension support for code ``` fun Int.foo(a:Int, b:Int = a + 1, c:Int = a + b) = a + b +c fun main(arg:Array) { 42.foo(0) } ``` we generate IR representation corresponding to the following code ``` fun Int.foo(a:Int, b:Int, c:Int) = a + b +c fun foo_default(a:Int, b:Int, c:Int, __mask__:Int, __receiver__:Int):Int { var tmpb:Int = 0 if ((__mask__ and (1 shl 1)) != 0) tmpb = a + 1 else tmpb = b var tmpc:Int = 0 if ((__mask__ and (1 shl 2)) != 0) tmpc = a + tmpb else tmpc = c return __receiver__.foo(a, tmpb, tmpc) } fun main(arg:Array) { foo_default(0, 0, 0, 6 /* (1 shl 1) | (1 shl 2)*/, 42) } ``` NOTE: if call provides all arguents we don't emit call of _default function. --- .../kotlin/backend/konan/KonanLower.kt | 4 + .../kotlin/backend/konan/KonanPhases.kt | 1 + .../lower/DefaultParameterStubGenerator.kt | 269 ++++++++++++++++++ 3 files changed, 274 insertions(+) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultParameterStubGenerator.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt index ae7b3109e63..a0461f9977e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt @@ -17,6 +17,10 @@ internal class KonanLower(val context: Context) { fun lower(irFile: IrFile) { val phaser = PhaseManager(context) + phaser.phase(KonanPhase.LOWER_DEFAULT_PARAMETER_EXTENT) { + DefaultParameterStubGenerator(context).runOnFilePostfix(irFile) + DefaultParameterInjector(context).runOnFilePostfix(irFile) + } phaser.phase(KonanPhase.LOWER_BUILTIN_OPERATORS) { BuiltinOperatorLowering(context).runOnFilePostfix(irFile) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt index 5df794ed9d3..017b5547e25 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt @@ -11,6 +11,7 @@ enum class KonanPhase(val description: String, /* */ BACKEND("All backend"), /* ... */ LOWER("IR Lowering"), /* ... ... */ LOWER_BUILTIN_OPERATORS("BuiltIn Operators Lowering"), + /* ... ... */ LOWER_DEFAULT_PARAMETER_EXTENT("Default Parameter Extent Lowering"), /* ... ... */ LOWER_TYPE_OPERATORS("Type operators lowering"), /* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering"), /* ... ... */ LOWER_LOCAL_FUNCTIONS("Local Function Lowering"), diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultParameterStubGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultParameterStubGenerator.kt new file mode 100644 index 00000000000..ae8a6f3fc75 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultParameterStubGenerator.kt @@ -0,0 +1,269 @@ +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.common.BodyLoweringPass +import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass +import org.jetbrains.kotlin.backend.common.lower.createFunctionIrGenerator +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.KonanPlatform +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor +import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer +import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl +import org.jetbrains.kotlin.ir.util.transformFlat +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.utils.addToStdlib.singletonList + +class DefaultParameterStubGenerator internal constructor(val context: Context): DeclarationContainerLoweringPass { + override fun lower(irDeclarationContainer: IrDeclarationContainer) { + irDeclarationContainer.declarations.transformFlat { memberDeclaration -> + if (memberDeclaration is IrFunction) + lower(memberDeclaration) + else + null + } + + } + + object DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER : + IrDeclarationOriginImpl("DEFAULT_PARAMETER_EXTENT") + + private fun lower(irFunction: IrFunction): List { + val bodies = mutableListOf() + irFunction.acceptChildrenVoid(object:IrElementVisitorVoid{ + override fun visitExpressionBody(body: IrExpressionBody) { + bodies.add(body) + } + + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + }) + + val functionDescriptor = irFunction.descriptor + if (bodies.isNotEmpty()) { + val (descriptor, mask, extension, dispatch) = functionDescriptor.generateDefaultsDescriptor() + val generator = context.createFunctionIrGenerator(descriptor) + val builder = generator.createIrBuilder() + builder.apply { + startOffset = irFunction.startOffset + endOffset = irFunction.endOffset + } + val body = generator.irBlockBody { + val params = mutableListOf() + val variables = mutableMapOf() + + for (valueParameter in functionDescriptor.valueParameters) { + val parameterDescriptor = descriptor.valueParameters[valueParameter.index] + if (valueParameter.hasDefaultValue()) { + val variable = scope.createTemporaryVariable( + irExpression = nullConst(valueParameter.type)!!, + isMutable = true) + val variableDescriptor = variable.descriptor + params.add(variableDescriptor) + +variable + val condition = irNotEquals(irCall(intAnd).apply { + dispatchReceiver = irGet(mask) + putValueArgument(0, irInt(1 shl valueParameter.index)) + }, irInt(0)) + val exprBody = getDefaultParameterExpressionBody(irFunction, valueParameter) + /* Use previously calculated values in next expression. */ + exprBody.expression.transformChildrenVoid(object:IrElementTransformerVoid() { + override fun visitGetValue(expression: IrGetValue): IrExpression { + if (!variables.containsKey(expression.descriptor)) + return expression + return irGet(variables[expression.descriptor] as VariableDescriptor) + } + }) + /* Mapping calculated values with its origin variables. */ + variables.put(valueParameter, variableDescriptor) + +irIfThenElse( + type = KonanPlatform.builtIns.unitType, + condition = condition, + thenPart = irSetVar(variableDescriptor, exprBody.expression), + elsePart = irSetVar(variableDescriptor, irGet(parameterDescriptor))) + + } else { + params.add(parameterDescriptor) + } + } + + irReturn(irCall(functionDescriptor).apply { + if (functionDescriptor.dispatchReceiverParameter != null) { + dispatchReceiver = irGet(dispatch!!) + } + if (functionDescriptor.extensionReceiverParameter != null) { + extensionReceiver = irGet(extension!!) + } + params.forEachIndexed { i, variable -> + putValueArgument(i, irGet(variable)) + } + }) + } + // TODO: replace irFunction with new one without expression bodies. + return listOf(irFunction, IrFunctionImpl( + irFunction.startOffset , + irFunction.endOffset, + DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER, + descriptor, body)) + } + return irFunction.singletonList() + } +} + +private fun getDefaultParameterExpressionBody(irFunction: IrFunction, valueParameter: ValueParameterDescriptor):IrExpressionBody { + return irFunction.getDefault(valueParameter) as? IrExpressionBody ?: TODO("FIXME!!!") +} + +private fun nullConst(type: KotlinType): IrExpression? { + when { + KotlinBuiltIns.isFloat(type) -> return IrConstImpl (0, 0, type, IrConstKind.Float, 0.0F) + KotlinBuiltIns.isDouble(type) -> return IrConstImpl (0, 0, type, IrConstKind.Double, 0.0) + KotlinBuiltIns.isBoolean(type) -> return IrConstImpl (0, 0, type, IrConstKind.Boolean, false) + KotlinBuiltIns.isByte(type) -> return IrConstImpl (0, 0, type, IrConstKind.Byte, 0) + KotlinBuiltIns.isShort(type) -> return IrConstImpl (0, 0, type, IrConstKind.Short, 0) + KotlinBuiltIns.isInt(type) -> return IrConstImpl (0, 0, type, IrConstKind.Int, 0) + KotlinBuiltIns.isLong(type) -> return IrConstImpl (0, 0, type, IrConstKind.Long, 0) + else -> return IrConstImpl(0, 0, type, IrConstKind.Null, null) + } +} + +class DefaultParameterInjector internal constructor(val context: Context): BodyLoweringPass { + override fun lower(irBody: IrBody) { + irBody.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitCall(expression: IrCall): IrExpression { + super.visitCall(expression) + val descriptor = expression.descriptor + if (descriptor.valueParameters.none{it.hasDefaultValue()}) + return expression + var argumentsCount = 0 + expression.acceptChildrenVoid(object:IrElementVisitorVoid{ + override fun visitElement(element: IrElement) { + argumentsCount++ + } + }) + if (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null) + argumentsCount-- + if (argumentsCount == descriptor.valueParameters.size) + return expression + var maskValue = 0 + val functionDescriptor = descriptor as FunctionDescriptor + val (defaultFunctiondescriptor, mask, extension, dispatch) = functionDescriptor.generateDefaultsDescriptor() + val params = descriptor.valueParameters.mapIndexed { i, it -> + if (expression.getValueArgument(i) == null) maskValue = maskValue or (1 shl i) + val valueParameterDescriptor = defaultFunctiondescriptor.valueParameters[i] + return@mapIndexed valueParameterDescriptor to (expression.getValueArgument(i) ?: nullConst(valueParameterDescriptor.type)) + } + (mask to IrConstImpl( + startOffset = irBody.startOffset, + endOffset = irBody.endOffset, + type = KonanPlatform.builtIns.intType, + kind = IrConstKind.Int, + value = maskValue)) + return IrCallImpl( + startOffset = irBody.startOffset, + endOffset = irBody.endOffset, + type = descriptor.returnType!!, + descriptor = defaultFunctiondescriptor, + typeArguments = null) + .apply { + params.forEach { + putValueArgument(it.first.index, it.second) + } + extension?.apply { + putValueArgument(extension.index, expression.extensionReceiver) + } + dispatch?.apply { + putValueArgument(dispatch.index, expression.dispatchReceiver) + } + } + } + }) + } + +} + +val intDesctiptor = DescriptorUtils.getClassDescriptorForType(KonanPlatform.builtIns.intType) +val intAnd = DescriptorUtils.getFunctionByName(intDesctiptor.unsubstitutedMemberScope, Name.identifier("and")) + +data class DefaultParameterDescriptor(val function: FunctionDescriptor, val mask:ValueParameterDescriptor, + val extensionReceiver:ValueParameterDescriptor?, val dispatchReceiver: ValueParameterDescriptor?) + +fun FunctionDescriptor.generateDefaultsDescriptor():DefaultParameterDescriptor { + val name = Name.identifier("${this.name.asString()}\$default") + val descriptor = SimpleFunctionDescriptorImpl.create(this.containingDeclaration, Annotations.EMPTY, + name, CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE) + val maskVariable = valueParameter(descriptor, valueParameters.size, "__\$mask\$__", KonanPlatform.builtIns.intType) + + var extensionReceiver:ValueParameterDescriptor? = null + var index = valueParameters.size + 1 + extensionReceiverParameter?.let { + extensionReceiver = valueParameter(descriptor, index++, "__\$ext_receiver\$__", extensionReceiverParameter!!.type) + } + var dispatchReceiver:ValueParameterDescriptor? = null + dispatchReceiverParameter?.let { + dispatchReceiver = valueParameter(descriptor, index++, "__\$dispatch_receiver\$__", dispatchReceiverParameter!!.type) + } + + val parameterList = mutableListOf(*valueParameters.map{ + if (it.hasDefaultValue()) ValueParameterDescriptorImpl( + containingDeclaration = it.containingDeclaration, + original = it.original, + index = it.index, + annotations = it.annotations, + name = it.name, + outType = it.type, + declaresDefaultValue = false, + isCrossinline = it.isCrossinline, + isNoinline = it.isNoinline, + varargElementType = it.varargElementType, + source = it.source) + else it + }.toTypedArray()) + + parameterList.add(maskVariable) + if (extensionReceiver != null) parameterList.add(extensionReceiver) + if (dispatchReceiver != null) parameterList.add(dispatchReceiver) + descriptor.initialize( + /* receiverParameterType = */ null, + /* dispatchReceiverParameterType = */ null, + /* typeParameters = */ typeParameters, + /* unsubstitutedValueParameters = */ parameterList, + /* unsubstitutedReturnType = */ returnType, + /* modality = */ this.modality, + /* visibility = */ this.visibility) + return DefaultParameterDescriptor(descriptor, maskVariable, extensionReceiver, dispatchReceiver) +} + +private fun valueParameter(descriptor: FunctionDescriptor, index: Int, name: String, type: KotlinType):ValueParameterDescriptor { + return ValueParameterDescriptorImpl( + containingDeclaration = descriptor, + original = null, + index = index, + annotations = Annotations.EMPTY, + name = Name.identifier(name), + outType = type, + declaresDefaultValue = false, + isCrossinline = false, + isNoinline = false, + varargElementType = null, + source = SourceElement.NO_SOURCE + ) +} \ No newline at end of file