From 2535a87f85d13938dfbdd7ddfb2ba50d06061293 Mon Sep 17 00:00:00 2001 From: "Evgeniy.Zhelenskiy" Date: Wed, 29 Mar 2023 07:46:05 +0200 Subject: [PATCH] [IR] Change value classes lowering dispatching strategy Signed-off-by: Evgeniy.Zhelenskiy #KT-1179 --- ...LightTreeBlackBoxCodegenTestGenerated.java | 6 + .../FirPsiBlackBoxCodegenTestGenerated.java | 6 + .../jvm/lower/JvmInlineClassLowering.kt | 2 - .../lower/JvmMultiFieldValueClassLowering.kt | 2 - .../lower/JvmValueClassAbstractLowering.kt | 67 +---- .../lower/JvmValueClassLoweringDispatcher.kt | 266 ++++++++++-------- .../kotlin/backend/jvm/JvmBackendContext.kt | 5 +- .../jvm/MemoizedInlineClassReplacements.kt | 10 +- ...emoizedMultiFieldValueClassReplacements.kt | 12 +- .../MemoizedValueClassAbstractReplacements.kt | 5 +- ...dValueClassLoweringDispatcherSharedData.kt | 20 ++ .../box/valueClasses/nestedLoweringStart.kt | 35 +++ .../box/valueClasses/nestedLoweringStart.txt | 40 +++ .../IrBlackBoxCodegenTestGenerated.java | 6 + ...kBoxCodegenWithIrInlinerTestGenerated.java | 6 + 15 files changed, 276 insertions(+), 212 deletions(-) create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedValueClassLoweringDispatcherSharedData.kt create mode 100644 compiler/testData/codegen/box/valueClasses/nestedLoweringStart.kt create mode 100644 compiler/testData/codegen/box/valueClasses/nestedLoweringStart.txt diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java index b4249e61f89..38b3fc23aef 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java @@ -52149,6 +52149,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr runTest("compiler/testData/codegen/box/valueClasses/mutableSharedMfvcVar.kt"); } + @Test + @TestMetadata("nestedLoweringStart.kt") + public void testNestedLoweringStart() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/nestedLoweringStart.kt"); + } + @Test @TestMetadata("nothingAsParameterType.kt") public void testNothingAsParameterType() throws Exception { diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java index d4e83b745d9..e7805635098 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java @@ -52149,6 +52149,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo runTest("compiler/testData/codegen/box/valueClasses/mutableSharedMfvcVar.kt"); } + @Test + @TestMetadata("nestedLoweringStart.kt") + public void testNestedLoweringStart() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/nestedLoweringStart.kt"); + } + @Test @TestMetadata("nothingAsParameterType.kt") public void testNothingAsParameterType() throws Exception { diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt index 21f1bb8bbd1..c2aa4395bdd 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt @@ -10,7 +10,6 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irBlockBody import org.jetbrains.kotlin.backend.jvm.* import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound -import org.jetbrains.kotlin.backend.jvm.ir.isInlineClassType import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.descriptors.DescriptorVisibilities @@ -72,7 +71,6 @@ internal class JvmInlineClassLowering( override val specificMangle: SpecificMangle get() = SpecificMangle.Inline - override val IrType.needsHandling get() = isInlineClassType() override fun visitClassNewDeclarationsWhenParallel(declaration: IrDeclaration) = Unit override fun visitClassNew(declaration: IrClass): IrClass { 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 790e44664fb..a18c720dd5d 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 @@ -46,8 +46,6 @@ internal class JvmMultiFieldValueClassLowering( context: JvmBackendContext, scopeStack: MutableList, ) : JvmValueClassAbstractLowering(context, scopeStack) { - override val IrType.needsHandling: Boolean - get() = needsMfvcFlattening() private sealed class MfvcNodeInstanceAccessor { abstract val instance: MfvcNodeInstance diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt index e8d00c93a52..5f0ddf64575 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.jvm.lower import org.jetbrains.kotlin.backend.common.* import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.jvm.* +import org.jetbrains.kotlin.backend.jvm.ir.isValueClassType import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.irReturn @@ -15,6 +16,8 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.transformStatement import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.isNothing +import org.jetbrains.kotlin.ir.types.makeNotNull import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.load.java.JvmAbi @@ -243,68 +246,6 @@ internal abstract class JvmValueClassAbstractLowering( protected abstract fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, original: IrFunction, inverted: Boolean) - // Functions for common lowering dispatching - private inner class NeedsToVisit : IrElementVisitor { - override fun visitElement(element: IrElement, data: Nothing?): Boolean = false - override fun visitClass(declaration: IrClass, data: Nothing?): Boolean = - declaration.isSpecificLoweringLogicApplicable() || declaration.declarations.any { it.accept(this, null) } - - override fun visitFunction(declaration: IrFunction, data: Nothing?): Boolean = - replacements.quickCheckIfFunctionIsNotApplicable(declaration) - - override fun visitFunctionReference(expression: IrFunctionReference, data: Nothing?): Boolean = - visitFunction(expression.symbol.owner, data) - - override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: Nothing?): Boolean = - visitFunction(expression.symbol.owner, data) - - override fun visitField(declaration: IrField, data: Nothing?): Boolean = declaration.type.needsHandling - override fun visitFieldAccess(expression: IrFieldAccessExpression, data: Nothing?): Boolean = - visitField(expression.symbol.owner, data) - - override fun visitVariable(declaration: IrVariable, data: Nothing?): Boolean = visitValueDeclaration(declaration) - - private fun visitValueDeclaration(declaration: IrValueDeclaration) = declaration.type.needsHandling - override fun visitValueParameter(declaration: IrValueParameter, data: Nothing?): Boolean = visitValueDeclaration(declaration) - override fun visitValueAccess(expression: IrValueAccessExpression, data: Nothing?): Boolean = - visitValueDeclaration(expression.symbol.owner) - - override fun visitStringConcatenation(expression: IrStringConcatenation, data: Nothing?): Boolean = false - override fun visitReturn(expression: IrReturn, data: Nothing?): Boolean = (expression.returnTargetSymbol.owner as? IrFunction) - ?.let { replacements.quickCheckIfFunctionIsNotApplicable(it) } ?: false - - override fun visitAnonymousInitializer(declaration: IrAnonymousInitializer, data: Nothing?): Boolean = - (declaration.parent as? IrClass)?.isSpecificLoweringLogicApplicable() == true - - private fun visitStatementContainer(container: IrStatementContainer) = container.statements.any { it.accept(this, null) } - - override fun visitContainerExpression(expression: IrContainerExpression, data: Nothing?): Boolean = - visitStatementContainer(expression) - - override fun visitBlockBody(body: IrBlockBody, data: Nothing?): Boolean = visitStatementContainer(body) - } - - internal fun needsToVisitClassNew(declaration: IrClass): Boolean = declaration.accept(NeedsToVisit(), null) - - internal fun needsToVisitFunctionReference(expression: IrFunctionReference): Boolean = expression.accept(NeedsToVisit(), null) - - internal fun needsToVisitFunctionAccess(expression: IrFunctionAccessExpression): Boolean = expression.accept(NeedsToVisit(), null) - - internal fun needsToVisitCall(expression: IrCall): Boolean = expression.accept(NeedsToVisit(), null) - - internal fun needsToVisitStringConcatenation(expression: IrStringConcatenation): Boolean = expression.accept(NeedsToVisit(), null) - - internal fun needsToVisitGetField(expression: IrGetField): Boolean = expression.accept(NeedsToVisit(), null) - - internal fun needsToVisitSetField(expression: IrSetField): Boolean = expression.accept(NeedsToVisit(), null) - - internal fun needsToVisitGetValue(expression: IrGetValue): Boolean = expression.accept(NeedsToVisit(), null) - - internal fun needsToVisitSetValue(expression: IrSetValue): Boolean = expression.accept(NeedsToVisit(), null) - - internal fun needsToVisitVariable(declaration: IrVariable): Boolean = declaration.accept(NeedsToVisit(), null) - - internal fun needsToVisitReturn(expression: IrReturn): Boolean = expression.accept(NeedsToVisit(), null) internal abstract fun visitClassNewDeclarationsWhenParallel(declaration: IrDeclaration) // forbid other overrides without modifying dispatcher file JvmValueClassLoweringDispatcher.kt @@ -380,6 +321,4 @@ internal abstract class JvmValueClassAbstractLowering( final override fun visitErrorExpression(expression: IrErrorExpression) = super.visitErrorExpression(expression) final override fun visitErrorCallExpression(expression: IrErrorCallExpression) = super.visitErrorCallExpression(expression) - - abstract val IrType.needsHandling: Boolean } diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassLoweringDispatcher.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassLoweringDispatcher.kt index 40b69e242ee..10750ab6f6a 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassLoweringDispatcher.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassLoweringDispatcher.kt @@ -11,15 +11,21 @@ import org.jetbrains.kotlin.backend.common.ScopeWithIr import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.MemoizedValueClassLoweringDispatcherSharedData.Access.Body +import org.jetbrains.kotlin.backend.jvm.MemoizedValueClassLoweringDispatcherSharedData.Access.Header +import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.declarations.IrAnonymousInitializer -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.declarations.IrVariable +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.transformStatement -import org.jetbrains.kotlin.ir.visitors.IrElementVisitor +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrSymbol +import org.jetbrains.kotlin.ir.types.IrSimpleType +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.classifierOrNull +import org.jetbrains.kotlin.ir.types.typeOrNull +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.ir.visitors.acceptVoid val jvmValueClassPhase = makeIrFilePhase( ::JvmValueClassLoweringDispatcher, @@ -32,7 +38,8 @@ val jvmValueClassPhase = makeIrFilePhase( prerequisite = setOf(forLoopsPhase, jvmBuiltInsPhase, collectionStubMethodLowering, singleAbstractMethodPhase), ) -internal class JvmValueClassLoweringDispatcher(context: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass { +internal class JvmValueClassLoweringDispatcher(private val context: JvmBackendContext) : IrElementTransformerVoidWithContext(), + FileLoweringPass { override val scopeStack: MutableList = mutableListOf() private val inlineClassLowering: JvmInlineClassLowering = JvmInlineClassLowering(context, scopeStack) private val multiFieldValueClassLowering: JvmMultiFieldValueClassLowering = JvmMultiFieldValueClassLowering(context, scopeStack) @@ -42,158 +49,177 @@ internal class JvmValueClassLoweringDispatcher(context: JvmBackendContext) : IrE irFile.transformChildrenVoid() } - - private fun IrElement.requiresAnyHandling() = - requiresHandling(multiFieldValueClassLowering) || requiresHandling(inlineClassLowering) - - override fun visitClassNew(declaration: IrClass): IrClass = if (declaration.requiresAnyHandling()) { + override fun visitClassNew(declaration: IrClass): IrClass = if (declaration.requiresHandling()) { declaration .let(multiFieldValueClassLowering::visitClassNew) .let(inlineClassLowering::visitClassNew) } else { - declaration.transformChildrenVoid() - for (innerDeclaration in declaration.declarations) { - multiFieldValueClassLowering.visitClassNewDeclarationsWhenParallel(innerDeclaration) - } - for (innerDeclaration in declaration.declarations) { - inlineClassLowering.visitClassNewDeclarationsWhenParallel(innerDeclaration) - } declaration } - private fun transformExpressionConsequently(expression: IrExpression) = expression - .transform(multiFieldValueClassLowering, null) - .transform(inlineClassLowering, null) + private fun IrElement.requiresHandling(): Boolean { + val visitor = NeedsToVisit(context) + accept(visitor, null) + return visitor.result + } +} - private fun transformStatementConsequently(statement: IrStatement) = statement - .transformStatement(multiFieldValueClassLowering) - .transformStatement(inlineClassLowering) +private class NeedsToVisit(private val context: JvmBackendContext) : IrElementVisitorVoid { + var result = false + private val replacements = context.valueClassLoweringDispatcherSharedData + private val visitedParameters = mutableSetOf() - private fun IrElement.requiresHandling(lowering: JvmValueClassAbstractLowering) = - accept(object : IrElementVisitor { - override fun visitElement(element: IrElement, data: Nothing?): Boolean = false - override fun visitClass(declaration: IrClass, data: Nothing?): Boolean = - lowering.needsToVisitClassNew(declaration) || super.visitClass(declaration, data) + override fun visitElement(element: IrElement) { + if (!result) element.acceptChildrenVoid(this) + } - override fun visitFunctionReference(expression: IrFunctionReference, data: Nothing?): Boolean = - lowering.needsToVisitFunctionReference(expression) || super.visitFunctionReference(expression, data) + private fun IrElement.acceptAndGetResult(): Boolean { + acceptVoid(this@NeedsToVisit) + return result + } - override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: Nothing?): Boolean = - lowering.needsToVisitFunctionAccess(expression) || super.visitFunctionAccess(expression, data) + private val IrClass.needsHandling: Boolean + get() = isValue || typeParameters.any { it.acceptAndGetResult() } + private val IrType.needsHandling: Boolean + get() = classifierOrNull?.isBound == true && erasedUpperBound.needsHandling || this is IrSimpleType && arguments.any { it.typeOrNull?.needsHandling == true } - override fun visitCall(expression: IrCall, data: Nothing?): Boolean = - lowering.needsToVisitCall(expression) || super.visitCall(expression, data) + override fun visitClass(declaration: IrClass) { + visitClassHeader(declaration) + if (result) return + visitClassBody(declaration) + } - override fun visitStringConcatenation(expression: IrStringConcatenation, data: Nothing?): Boolean = - lowering.needsToVisitStringConcatenation(expression) || super.visitStringConcatenation(expression, data) + private fun visitClassBody(declaration: IrClass) { + if (result) return + result = replacements.classResults.getOrPut(Body to declaration) { + declaration.declarations.any { it.acceptAndGetResult() } + } + } - override fun visitGetField(expression: IrGetField, data: Nothing?): Boolean = - lowering.needsToVisitGetField(expression) || super.visitGetField(expression, data) + private fun visitClassHeader(declaration: IrClass) { + if (result) return + result = replacements.classResults.getOrPut(Header to declaration) { + declaration.needsHandling || + declaration.typeParameters.any { it.acceptAndGetResult() } || + declaration.superTypes.mapNotNull { (it.classifierOrNull as? IrClassSymbol)?.owner }.any { visitClassHeader(it); result } + } + } - override fun visitSetField(expression: IrSetField, data: Nothing?): Boolean = - lowering.needsToVisitSetField(expression) || super.visitSetField(expression, data) + override fun visitFunction(declaration: IrFunction) = visitFunction(declaration, withBody = true) - override fun visitGetValue(expression: IrGetValue, data: Nothing?): Boolean = - lowering.needsToVisitGetValue(expression) || super.visitGetValue(expression, data) + private fun visitFunctionHeader(declaration: IrFunction) { + if (result) return + result = replacements.functionResults.getOrPut(Header to declaration) { + declaration.parent.let { it is IrClass && it.needsHandling } || + declaration.typeParameters.any { it.acceptAndGetResult() } || + declaration.dispatchReceiverParameter?.acceptAndGetResult() == true || + declaration.extensionReceiverParameter?.acceptAndGetResult() == true || + declaration.valueParameters.any { it.acceptAndGetResult() } || + declaration.returnType.needsHandling || + (declaration as? IrSimpleFunction)?.overriddenSymbols?.any { visitFunction(it.owner, withBody = false); result } == true + } + } - override fun visitSetValue(expression: IrSetValue, data: Nothing?): Boolean = - lowering.needsToVisitSetValue(expression) || super.visitSetValue(expression, data) + private fun visitFunction(declaration: IrFunction, withBody: Boolean) { + visitFunctionHeader(declaration) + if (result) return + if(withBody) visitFunctionBody(declaration) + } - override fun visitVariable(declaration: IrVariable, data: Nothing?): Boolean = - lowering.needsToVisitVariable(declaration) || super.visitVariable(declaration, data) + private fun visitFunctionBody(declaration: IrFunction) { + if (result) return + result = replacements.functionResults.getOrPut(Body to declaration) { + declaration.body?.acceptAndGetResult() == true + } + } - override fun visitReturn(expression: IrReturn, data: Nothing?): Boolean = - lowering.needsToVisitReturn(expression) || super.visitReturn(expression, data) + override fun visitValueParameter(declaration: IrValueParameter) { + if (result || !visitedParameters.add(declaration.symbol)) return + result = declaration.type.needsHandling + if (result) return + super.visitValueParameter(declaration) + } - override fun visitContainerExpression(expression: IrContainerExpression, data: Nothing?): Boolean = - visitStatementContainer(expression) + override fun visitTypeParameter(declaration: IrTypeParameter) { + if (result || !visitedParameters.add(declaration.symbol)) return + result = declaration.superTypes.any { it.needsHandling } + if (result) return + super.visitTypeParameter(declaration) + } - override fun visitBlockBody(body: IrBlockBody, data: Nothing?): Boolean = - visitStatementContainer(body) - - private fun visitStatementContainer(expression: IrStatementContainer) = expression.statements.any { it.accept(this, null) } - }, null) - - override fun visitFunctionReference(expression: IrFunctionReference): IrExpression = if (expression.requiresAnyHandling()) { - transformExpressionConsequently(expression) - } else { + override fun visitFunctionReference(expression: IrFunctionReference) { + if (result) return + visitFunction(expression.symbol.owner, withBody = false) + if (result) return super.visitFunctionReference(expression) } - override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression = if (expression.requiresAnyHandling()) { - transformExpressionConsequently(expression) - } else { + override fun visitFunctionAccess(expression: IrFunctionAccessExpression) { + if (result) return + visitFunction(expression.symbol.owner, withBody = false) + if (result) return super.visitFunctionAccess(expression) } - override fun visitCall(expression: IrCall): IrExpression = if (expression.requiresAnyHandling()) { - transformExpressionConsequently(expression) - } else { - super.visitCall(expression) + override fun visitField(declaration: IrField) = visitField(declaration, withBody = true) + + private fun visitFieldHeader(declaration: IrField) { + if (result) return + result = replacements.fieldResults.getOrPut(Header to declaration) { + declaration.type.needsHandling + } } - override fun visitStringConcatenation(expression: IrStringConcatenation): IrExpression = if (expression.requiresAnyHandling()) { - transformExpressionConsequently(expression) - } else { - super.visitStringConcatenation(expression) + private fun visitFieldBody(declaration: IrField) { + if (result) return + result = replacements.fieldResults.getOrPut(Body to declaration) { + super.visitField(declaration) + result + } } - override fun visitGetField(expression: IrGetField): IrExpression = if (expression.requiresAnyHandling()) { - transformExpressionConsequently(expression) - } else { - super.visitGetField(expression) + private fun visitField(declaration: IrField, withBody: Boolean) { + visitFieldHeader(declaration) + if (withBody) visitFieldBody(declaration) } - override fun visitSetField(expression: IrSetField): IrExpression = if (expression.requiresAnyHandling()) { - transformExpressionConsequently(expression) - } else { - super.visitSetField(expression) + override fun visitFieldAccess(expression: IrFieldAccessExpression) { + if (result) return + visitField(expression.symbol.owner, withBody = false) + if (result) return + super.visitFieldAccess(expression) } - override fun visitGetValue(expression: IrGetValue): IrExpression = if (expression.requiresAnyHandling()) { - transformExpressionConsequently(expression) - } else { - super.visitGetValue(expression) - } - - override fun visitSetValue(expression: IrSetValue): IrExpression = if (expression.requiresAnyHandling()) { - transformExpressionConsequently(expression) - } else { - super.visitSetValue(expression) - } - - override fun visitVariable(declaration: IrVariable): IrStatement = if (declaration.requiresAnyHandling()) { - transformStatementConsequently(declaration) - } else { + override fun visitVariable(declaration: IrVariable) { + if (result) return + result = declaration.type.needsHandling + if (result) return super.visitVariable(declaration) } - override fun visitReturn(expression: IrReturn): IrExpression = if (expression.requiresAnyHandling()) { - transformExpressionConsequently(expression) - } else { - super.visitReturn(expression) + + override fun visitStringConcatenation(expression: IrStringConcatenation) { + if (result) return + result = expression.arguments.any { it.type.needsHandling } + if (result) return + super.visitStringConcatenation(expression) } - override fun visitAnonymousInitializerNew(declaration: IrAnonymousInitializer): IrStatement = if (declaration.requiresAnyHandling()) { - transformStatementConsequently(declaration) - } else { - super.visitAnonymousInitializerNew(declaration) + override fun visitCall(expression: IrCall) { + if (result) return + if (expression.symbol == context.irBuiltIns.eqeqSymbol) { + for (it in 0 until expression.valueArgumentsCount) { + result = expression.getValueArgument(it)?.type?.needsHandling ?: false + if (result) return + } + } + super.visitCall(expression) } - private fun visitStatementContainer(container: IrStatementContainer) = if (container.statements.any { it.requiresAnyHandling() }) { - multiFieldValueClassLowering.visitStatementContainer(container) - inlineClassLowering.visitStatementContainer(container) - } else { - container.statements.replaceAll { it.transformStatement(this) } + override fun visitExpression(expression: IrExpression) { + if (result) return + result = expression.type.needsHandling + if (result) return + super.visitExpression(expression) } - - override fun visitContainerExpression(expression: IrContainerExpression): IrExpression { - visitStatementContainer(expression) - return expression - } - - override fun visitBlockBody(body: IrBlockBody): IrBody { - visitStatementContainer(body) - return body - } -} \ No newline at end of file +} diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt index 179e2f9aabf..e8a6f8da681 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt @@ -167,8 +167,9 @@ class JvmBackendContext( val inlineClassReplacements = MemoizedInlineClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory, this) - val multiFieldValueClassReplacements = - MemoizedMultiFieldValueClassReplacements(irFactory, this) + val multiFieldValueClassReplacements = MemoizedMultiFieldValueClassReplacements(irFactory, this) + + val valueClassLoweringDispatcherSharedData = MemoizedValueClassLoweringDispatcherSharedData() val continuationClassesVarsCountByType: MutableMap> = hashMapOf() diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt index ee0f02503ba..110206ec93e 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt @@ -13,8 +13,11 @@ import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl +import org.jetbrains.kotlin.ir.types.isNothing +import org.jetbrains.kotlin.ir.types.makeNotNull import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver @@ -83,13 +86,6 @@ class MemoizedInlineClassReplacements( } } - override fun quickCheckIfFunctionIsNotApplicable(function: IrFunction) = !( - function.parent.let { (it is IrClass && it.isSingleFieldValueClass) } || - function.dispatchReceiverParameter?.type?.isInlineClassType() == true || - function.extensionReceiverParameter?.type?.isInlineClassType() == true || - function.valueParameters.any { it.type.isInlineClassType() } || function.returnType.isInlineClassType() - ) - /** * Get the box function for an inline class. Concretely, this is a synthetic * static function named "box-impl" which takes an unboxed value and returns diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt index 805fe2b104c..54cf13f7e7d 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt @@ -22,10 +22,7 @@ import org.jetbrains.kotlin.ir.expressions.IrComposite import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrStatementOriginImpl import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol -import org.jetbrains.kotlin.ir.types.IrSimpleType -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.defaultType -import org.jetbrains.kotlin.ir.types.isNothing +import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver @@ -290,13 +287,6 @@ class MemoizedMultiFieldValueClassReplacements( } } - override fun quickCheckIfFunctionIsNotApplicable(function: IrFunction): Boolean = !( - function.parent.let { it is IrClass && it.isMultiFieldValueClass } || - function.dispatchReceiverParameter?.type?.needsMfvcFlattening() == true || - function.extensionReceiverParameter?.type?.needsMfvcFlattening() == true || - function.valueParameters.any { it.type.needsMfvcFlattening() } - ) - private val getReplacementForRegularClassConstructorImpl: (IrConstructor) -> IrConstructor? = storageManager.createMemoizedFunctionWithNullableValues { constructor -> when { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedValueClassAbstractReplacements.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedValueClassAbstractReplacements.kt index 14319deecb1..d0531c2df28 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedValueClassAbstractReplacements.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedValueClassAbstractReplacements.kt @@ -29,8 +29,7 @@ abstract class MemoizedValueClassAbstractReplacements( /** * Get a replacement for a function or a constructor. */ - fun getReplacementFunction(function: IrFunction) = - if (quickCheckIfFunctionIsNotApplicable(function)) null else getReplacementFunctionImpl(function) + fun getReplacementFunction(function: IrFunction) = getReplacementFunctionImpl(function) protected abstract val getReplacementFunctionImpl: (IrFunction) -> IrSimpleFunction? @@ -138,6 +137,4 @@ abstract class MemoizedValueClassAbstractReplacements( getReplacementFunction(function) ?: function.also { function.overriddenSymbols = replaceOverriddenSymbols(function) } - - abstract fun quickCheckIfFunctionIsNotApplicable(function: IrFunction): Boolean } \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedValueClassLoweringDispatcherSharedData.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedValueClassLoweringDispatcherSharedData.kt new file mode 100644 index 00000000000..35b0a894827 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedValueClassLoweringDispatcherSharedData.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2023 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 + +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrField +import org.jetbrains.kotlin.ir.declarations.IrFunction +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ConcurrentMap + +class MemoizedValueClassLoweringDispatcherSharedData { + enum class Access { Header, Body } + + val functionResults: ConcurrentMap, Boolean> = ConcurrentHashMap() + val classResults: ConcurrentMap, Boolean> = ConcurrentHashMap() + val fieldResults: ConcurrentMap, Boolean> = ConcurrentHashMap() +} diff --git a/compiler/testData/codegen/box/valueClasses/nestedLoweringStart.kt b/compiler/testData/codegen/box/valueClasses/nestedLoweringStart.kt new file mode 100644 index 00000000000..7e4012afca7 --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/nestedLoweringStart.kt @@ -0,0 +1,35 @@ +// WITH_STDLIB +// TARGET_BACKEND: JVM_IR +// LANGUAGE: +ValueClasses +// CHECK_BYTECODE_LISTING +// FIR_IDENTICAL + +@JvmInline +value class Point(val x: Double, val y: Double) + + +class A { + fun b(p: Point) { + res = p + } +} + +var res: Any? = null + + +fun toA(out: A): A { + + fun g(p: List) { + out.b(p.first()) + } + + g(listOf(Point(1.0, 2.0))) + return out +} + +fun box(): String { + val a = A() + toA(a) + require(res.toString() == Point(1.0, 2.0).toString()) + return "OK" +} diff --git a/compiler/testData/codegen/box/valueClasses/nestedLoweringStart.txt b/compiler/testData/codegen/box/valueClasses/nestedLoweringStart.txt new file mode 100644 index 00000000000..001bb52abb5 --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/nestedLoweringStart.txt @@ -0,0 +1,40 @@ +@kotlin.Metadata +public final class A { + // source: 'nestedLoweringStart.kt' + public method (): void + public final method b-1Nq-99U(p0: double, p1: double): void +} + +@kotlin.Metadata +public final class NestedLoweringStartKt { + // source: 'nestedLoweringStart.kt' + private static @org.jetbrains.annotations.Nullable field res: java.lang.Object + static method (): void + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static @org.jetbrains.annotations.Nullable method getRes(): java.lang.Object + public final static method setRes(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void + private final static method toA$g(p0: A, p1: java.util.List): void + public final static @org.jetbrains.annotations.NotNull method toA(@org.jetbrains.annotations.NotNull p0: A): A +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class Point { + // source: 'nestedLoweringStart.kt' + private final field x: double + private final field y: double + private synthetic method (p0: double, p1: double): void + public synthetic final static method box-impl(p0: double, p1: double): Point + public final static method constructor-impl(p0: double, p1: double): void + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public static method equals-impl(p0: double, p1: double, p2: java.lang.Object): boolean + public final static method equals-impl0(p0: double, p1: double, p2: double, p3: double): boolean + public final method getX(): double + public final method getY(): double + public method hashCode(): int + public static method hashCode-impl(p0: double, p1: double): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String + public static method toString-impl(p0: double, p1: double): java.lang.String + public synthetic final method unbox-impl-x(): double + public synthetic final method unbox-impl-y(): double +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 7704a2b9a2e..ee3cfcf901f 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -52149,6 +52149,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/valueClasses/mutableSharedMfvcVar.kt"); } + @Test + @TestMetadata("nestedLoweringStart.kt") + public void testNestedLoweringStart() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/nestedLoweringStart.kt"); + } + @Test @TestMetadata("nothingAsParameterType.kt") public void testNothingAsParameterType() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java index f13e0c1539a..149bc5c825f 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java @@ -52149,6 +52149,12 @@ public class IrBlackBoxCodegenWithIrInlinerTestGenerated extends AbstractIrBlack runTest("compiler/testData/codegen/box/valueClasses/mutableSharedMfvcVar.kt"); } + @Test + @TestMetadata("nestedLoweringStart.kt") + public void testNestedLoweringStart() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/nestedLoweringStart.kt"); + } + @Test @TestMetadata("nothingAsParameterType.kt") public void testNothingAsParameterType() throws Exception {