KT-23397 Optimize out field for property delegate when it's safe (JVM)

This commit is contained in:
Pavel Mikhailovskii
2022-06-03 16:54:12 +02:00
committed by teamcity
parent 9ee0268197
commit 65b2cee913
83 changed files with 3015 additions and 19 deletions
@@ -290,6 +290,7 @@ private val jvmFilePhases = listOf(
functionReferencePhase,
suspendLambdaPhase,
propertyReferenceDelegationPhase,
singletonOrConstantDelegationPhase,
propertyReferencePhase,
arrayConstructorPhase,
constPhase1,
@@ -105,19 +105,6 @@ private class PropertyReferenceDelegationTransformer(val context: JvmBackendCont
correspondingPropertySymbol?.let { it.owner.getter == this && it.owner.setter == null } == true &&
modality == Modality.FINAL
private fun IrExpression.inline(oldReceiver: IrValueParameter?, newReceiver: IrValueParameter?): IrExpression = when (this) {
is IrGetField ->
IrGetFieldImpl(startOffset, endOffset, symbol, type, receiver?.inline(oldReceiver, newReceiver), origin, superQualifierSymbol)
is IrGetValue ->
IrGetValueImpl(startOffset, endOffset, type, newReceiver?.symbol.takeIf { symbol == oldReceiver?.symbol } ?: symbol, origin)
is IrCall ->
IrCallImpl(startOffset, endOffset, type, symbol, typeArgumentsCount, valueArgumentsCount, origin, superQualifierSymbol).apply {
dispatchReceiver = this@inline.dispatchReceiver?.inline(oldReceiver, newReceiver)
extensionReceiver = this@inline.extensionReceiver?.inline(oldReceiver, newReceiver)
}
else -> shallowCopy()
}
override fun visitClass(declaration: IrClass): IrStatement {
declaration.transformChildren(this, null)
declaration.transformDeclarationsFlat {
@@ -150,14 +137,14 @@ private class PropertyReferenceDelegationTransformer(val context: JvmBackendCont
}
}
val originalThis = parentAsClass.thisReceiver
getter?.apply { body = accessorBody(delegate, backingField ?: receiver?.inline(originalThis, dispatchReceiverParameter)) }
setter?.apply { body = accessorBody(delegate, backingField ?: receiver?.inline(originalThis, dispatchReceiverParameter)) }
getter?.apply { body = accessorBody(delegate, backingField ?: receiver?.remapReceiver(originalThis, dispatchReceiverParameter)) }
setter?.apply { body = accessorBody(delegate, backingField ?: receiver?.remapReceiver(originalThis, dispatchReceiverParameter)) }
// The `$delegate` method is generated as instance method here, see MakePropertyDelegateMethodsStaticLowering.
val delegateMethod = context.createSyntheticMethodForPropertyDelegate(this).apply {
body = context.createJvmIrBuilder(symbol).run {
val boundReceiver = backingField?.let { irGetField(dispatchReceiverParameter?.let(::irGet), it) }
?: receiver?.inline(originalThis, dispatchReceiverParameter)
?: receiver?.remapReceiver(originalThis, dispatchReceiverParameter)
irExprBody(with(delegate) {
val origin = PropertyReferenceLowering.REFLECTED_PROPERTY_REFERENCE
IrPropertyReferenceImpl(startOffset, endOffset, type, symbol, typeArgumentsCount, field, getter, setter, origin)
@@ -179,7 +166,7 @@ private class PropertyReferenceDelegationTransformer(val context: JvmBackendCont
symbol,
parentAsClass.isFacadeClass
).apply {
body = context.irFactory.createBlockBody(startOffset, endOffset, listOf(it.inline(null, null)))
body = context.irFactory.createBlockBody(startOffset, endOffset, listOf(it.remapReceiver(null, null)))
}
}
return listOfNotNull(this, delegateMethod, receiverBlock)
@@ -0,0 +1,104 @@
/*
* Copyright 2010-2022 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.FileLoweringPass
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.lower.JvmPropertiesLowering.Companion.createSyntheticMethodForPropertyDelegate
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.builders.irExprBody
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions
internal val singletonOrConstantDelegationPhase = makeIrFilePhase(
::SingletonOrConstantDelegationLowering,
name = "SingletonOrConstantDelegation",
description = "Optimize `val x by ConstOrSingleton`: there is no need to store the value in a field"
)
private class SingletonOrConstantDelegationLowering(val context: JvmBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
if (!context.state.generateOptimizedCallableReferenceSuperClasses) return
irFile.transform(SingletonOrConstantDelegationTransformer(context), null)
}
}
private class SingletonOrConstantDelegationTransformer(val context: JvmBackendContext) : IrElementTransformerVoid() {
override fun visitClass(declaration: IrClass): IrClass {
declaration.transformChildren(this, null)
declaration.transformDeclarationsFlat {
(it as? IrProperty)?.transform()
}
return declaration
}
private fun IrProperty.transform(): List<IrDeclaration>? {
if (!isDelegated || isFakeOverride || backingField == null) return null
val delegate = backingField?.initializer?.expression?.takeIf { it.isInlineable() } ?: return null
val originalThis = parentAsClass.thisReceiver
val receiverMapper = object : IrElementTransformer<Pair<Name, IrExpression>> {
override fun visitCall(expression: IrCall, data: Pair<Name, IrExpression>): IrExpression {
val (name, newReceiver) = data
if (expression.symbol.owner.name == name) {
if ((expression.dispatchReceiver as? IrGetField)?.symbol == backingField?.symbol) {
expression.dispatchReceiver = newReceiver
} else if ((expression.extensionReceiver as? IrGetField)?.symbol == backingField?.symbol) {
expression.extensionReceiver = newReceiver
}
}
return expression
}
}
getter?.transform(receiverMapper,OperatorNameConventions.GET_VALUE to delegate.remapReceiver(originalThis, getter?.dispatchReceiverParameter))
setter?.transform(receiverMapper,OperatorNameConventions.SET_VALUE to delegate.remapReceiver(originalThis, setter?.dispatchReceiverParameter))
backingField = null
val initializerBlock = if (delegate !is IrConst<*> && delegate !is IrGetValue)
context.irFactory.createAnonymousInitializer(
delegate.startOffset,
delegate.endOffset,
IrDeclarationOrigin.DEFINED,
IrAnonymousInitializerSymbolImpl(parentAsClass.symbol),
parentAsClass.isFileClass
).apply {
body = context.irFactory.createBlockBody(delegate.startOffset, delegate.endOffset, listOf(delegate.remapReceiver(null, null)))
}
else null
val delegateMethod = context.createSyntheticMethodForPropertyDelegate(this).apply {
body = context.createJvmIrBuilder(symbol).run { irExprBody(delegate.remapReceiver(originalThis, dispatchReceiverParameter)) }
}
return listOfNotNull(this, initializerBlock, delegateMethod)
}
private fun IrExpression.isInlineable(): Boolean =
when (this) {
is IrConst<*>, is IrGetSingletonValue -> true
is IrCall ->
dispatchReceiver?.isInlineable() != false
&& extensionReceiver?.isInlineable() != false
&& valueArgumentsCount == 0
&& symbol.owner.run {
modality == Modality.FINAL
&& origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
&& ((body?.statements?.singleOrNull() as? IrReturn)?.value as? IrGetField)?.symbol?.owner?.isFinal == true
}
is IrGetValue ->
symbol.owner.origin == IrDeclarationOrigin.INSTANCE_RECEIVER
else -> false
}
}
@@ -600,6 +600,13 @@ fun IrExpression.shallowCopy(): IrExpression =
fun IrExpression.shallowCopyOrNull(): IrExpression? =
when (this) {
is IrConst<*> -> shallowCopy()
is IrGetEnumValue ->
IrGetEnumValueImpl(
startOffset,
endOffset,
type,
symbol
)
is IrGetObjectValue ->
IrGetObjectValueImpl(
startOffset,
@@ -633,6 +640,19 @@ internal fun <T> IrConst<T>.shallowCopy() = IrConstImpl(
value
)
fun IrExpression.remapReceiver(oldReceiver: IrValueParameter?, newReceiver: IrValueParameter?): IrExpression = when (this) {
is IrGetField ->
IrGetFieldImpl(startOffset, endOffset, symbol, type, receiver?.remapReceiver(oldReceiver, newReceiver), origin, superQualifierSymbol)
is IrGetValue ->
IrGetValueImpl(startOffset, endOffset, type, newReceiver?.symbol.takeIf { symbol == oldReceiver?.symbol } ?: symbol, origin)
is IrCall ->
IrCallImpl(startOffset, endOffset, type, symbol, typeArgumentsCount, valueArgumentsCount, origin, superQualifierSymbol).also {
it.dispatchReceiver = dispatchReceiver?.remapReceiver(oldReceiver, newReceiver)
it.extensionReceiver = extensionReceiver?.remapReceiver(oldReceiver, newReceiver)
}
else -> shallowCopy()
}
val IrDeclarationParent.isFacadeClass: Boolean
get() = this is IrClass &&
(origin == IrDeclarationOrigin.JVM_MULTIFILE_CLASS ||