From cfc17849d885cc47d2466b85f01bfe1178b61d0b Mon Sep 17 00:00:00 2001 From: "Evgeniy.Zhelenskiy" Date: Sat, 1 Oct 2022 21:12:12 +0200 Subject: [PATCH] [IR] Document the new MFVC lowering classes when necessary #KT-1179 --- .../lower/JvmMultiFieldValueClassLowering.kt | 56 ++++++++++++++++--- .../kotlin/backend/jvm/MfvcNodeInstance.kt | 10 ++++ 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt index 65c721cf376..f1204350582 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt @@ -42,9 +42,6 @@ val jvmMultiFieldValueClassPhase = makeIrFilePhase( ) private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmValueClassAbstractLowering(context) { - /** - * The class is used to get replacing expression and MFVC instance if present for the given old value declaration. - */ private sealed class MfvcNodeInstanceAccessor { abstract val instance: MfvcNodeInstance abstract operator fun get(name: Name): MfvcNodeInstanceAccessor? @@ -60,6 +57,9 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV } } + /** + * The class is used to get replacing expression and MFVC instance if present for the given old value declaration. + */ private inner class ValueDeclarationRemapper { private val expression2MfvcNodeInstanceAccessor = mutableMapOf() @@ -103,6 +103,9 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV }.also { expression2MfvcNodeInstanceAccessor[it] = MfvcNodeInstanceAccessor.Setter(instance, values!!) } } + /** + * @param safe whether protect from partial (because of a potential exception) initialization or not + */ private fun IrBlockBuilder.makeFlattenedExpressionsWithGivenSafety( node: MfvcNode, safe: Boolean, expression: IrExpression ) = if (safe) { @@ -416,7 +419,6 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV } override fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, original: IrFunction, inverted: Boolean) { - allScopes.push(createScope(source)) source.body = context.createIrBuilder(source.symbol, source.startOffset, source.endOffset).run { val sourceExplicitParameters = source.explicitParameters @@ -666,6 +668,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV val rightNode = if (rightArgument.type.needsMfvcFlattening()) replacements.getRootMfvcNode(rightClass) else null if (leftNode != null) { if (rightNode != null) { + // both are unboxed val leftExpressions = flattenExpression(leftArgument) require((leftExpressions.size > 1) == leftArgument.type.needsMfvcFlattening()) { "Illegal flattening of ${leftArgument.dump()}\n\n${leftExpressions.joinToString("\n") { it.dump() }}" @@ -685,6 +688,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV arguments.forEachIndexed { index, argument -> putValueArgument(index, argument) } } } else { + // left one is unboxed, right is not val equals = leftClass.functions.single { it.name.asString() == "equals" && it.overriddenSymbols.isNotEmpty() } +irCall(equals).apply { copyTypeArgumentsFrom(expression) @@ -693,7 +697,9 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV }.transform(this@JvmMultiFieldValueClassLowering, null) } } else if (rightNode != null) { + // left one is boxed, right one is unboxed if (leftArgument.isNullConst()) { + // left argument is always null, right one is unboxed val hasPureFlattenedGetters = rightNode.mapLeaves { it.hasPureUnboxMethod }.all { it } if (hasPureFlattenedGetters) { val rightExpressions = flattenExpression(rightArgument) @@ -706,6 +712,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV } +irFalse() } else if (leftArgument.type.erasedUpperBound == rightArgument.type.erasedUpperBound && leftArgument.type.isNullable()) { + // left argument can be unboxed if it is not null, right one is unboxed +irBlock { val leftValue = irTemporary(leftArgument) +irIfNull(context.irBuiltIns.booleanType, irGet(leftValue), irFalse(), irBlock { @@ -719,9 +726,11 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV }) }.transform(this@JvmMultiFieldValueClassLowering, null) } else { + // right one is unboxed but left one is boxed and no intrinsics can be used return super.visitCall(expression) } } else { + // both are boxed return super.visitCall(expression) } } @@ -788,6 +797,21 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV return newArguments } + /** + * Inlines initialization of variables when possible and returns their values + * + * Example: + * Before: + * val a = 2 + * val b = 3 + * val c = b + 1 + * [a, b, c] + * + * After: + * val a = 2 + * val b = 3 + * [a, b, b + 1] + */ fun IrBuilderWithScope.removeExtraSetVariablesFromExpressionList( block: IrContainerExpression, variables: List @@ -874,7 +898,9 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV private fun getCurrentScopeSymbol() = (currentScope!!.irElement as IrSymbolOwner).symbol - // expression takes not transformed first argument + /** + * Takes not transformed expression and returns its flattened transformed representation (expressions) + */ fun IrBlockBuilder.flattenExpression(expression: IrExpression): List { if (!expression.type.needsMfvcFlattening()) { return listOf(expression.transform(this@JvmMultiFieldValueClassLowering, null)) @@ -899,6 +925,9 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV return expressions } + /** + * Takes not transformed expression and initialized given MfvcNodeInstance with transformed version of it + */ fun IrBlockBuilder.flattenExpressionTo(expression: IrExpression, instance: MfvcNodeInstance) { val rootNode = replacements.getRootMfvcNode( if (expression is IrConstructorCall) expression.symbol.owner.constructedClass else expression.type.erasedUpperBound @@ -965,14 +994,18 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV private fun hasAccessToPrivateMembersOf(parent: IrClass): Boolean = currentScope?.irElement?.let { hasAccessToPrivateMembersOf(it, parent) } == true + /** + * Removes boxing when the result is not used + */ fun IrBody.removeAllExtraBoxes() { + // data is whether the expression result is used accept(object : IrElementVisitor { override fun visitElement(element: IrElement, data: Boolean) { - element.acceptChildren(this, true) + element.acceptChildren(this, true) // uses what is inside } override fun visitTypeOperator(expression: IrTypeOperatorCall, data: Boolean) { - expression.acceptChildren(this, data) + expression.acceptChildren(this, data) // type operator calls are transparent } private tailrec fun getFunctionCallOrNull(statement: IrStatement): IrCall? = when (statement) { @@ -981,8 +1014,10 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV else -> null } + // inner functions will be handled separately, no need to do it now override fun visitFunction(declaration: IrFunction, data: Boolean) = Unit + // inner classes will be handled separately, no need to do it now override fun visitClass(declaration: IrClass, data: Boolean) = Unit override fun visitContainerExpression(expression: IrContainerExpression, data: Boolean) { @@ -1017,6 +1052,9 @@ private sealed class BlockOrBody { data class Block(val block: IrBlock) : BlockOrBody() } +/** + * Finds the most narrow block or body which contains all usages of each of the given variables + */ private fun findNearestBlocksForVariables(variables: Set, body: IrBody): Map { val variableUsages = mutableMapOf>() val childrenBlocks = mutableMapOf>() @@ -1084,6 +1122,10 @@ private fun IrStatement.containsUsagesOf(variablesSet: Set): Boolean return used } +/** + * Adds declrations of the variables to the most narrow possible block or body. + * It adds them before the first usage within the block and inlines initialization of them when possible. + */ fun IrBody.makeBodyWithAddedVariables( context: JvmBackendContext, variables: Set, diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeInstance.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeInstance.kt index 8d1de218d19..d894bd5adca 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeInstance.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeInstance.kt @@ -227,6 +227,11 @@ val MfvcNodeInstance.size: Int fun IrContainerExpression.unwrap(): IrExpression = statements.singleOrNull() as? IrExpression ?: this +/** + * Creates a variable and doesn't add it to a container. It saves the variable with given saveVariable. + * + * It may be used when the variable will be used outside the current container so the declaration is added later when all usages are known. + */ fun IrBuilderWithScope.savableStandaloneVariable( type: IrType, name: String? = null, @@ -257,6 +262,11 @@ fun IrBuilderWithScope.savableStandaloneVariable( return variable } +/** + * Creates a variable and doesn't add it to a container. It saves the variable with given saveVariable. It adds irSet-based initialization. + * + * It may be used when the variable will be used outside the current container so the declaration is added later when all usages are known. + */ fun IrStatementsBuilder.savableStandaloneVariableWithSetter( expression: IrExpression, name: String? = null,