KT-23397 Optimize out field for property delegate when it's safe (JVM)
This commit is contained in:
committed by
teamcity
parent
9ee0268197
commit
65b2cee913
@@ -290,6 +290,7 @@ private val jvmFilePhases = listOf(
|
||||
functionReferencePhase,
|
||||
suspendLambdaPhase,
|
||||
propertyReferenceDelegationPhase,
|
||||
singletonOrConstantDelegationPhase,
|
||||
propertyReferencePhase,
|
||||
arrayConstructorPhase,
|
||||
constPhase1,
|
||||
|
||||
+4
-17
@@ -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)
|
||||
|
||||
+104
@@ -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 ||
|
||||
|
||||
Reference in New Issue
Block a user