From ce7d8a6874b52cb749a21251f10b1c3d67f4ebd6 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 5 Feb 2019 15:19:20 +0100 Subject: [PATCH] JVM IR: replace unnecessary property accessor calls with field access Split the ConstAndJvmFieldPropertiesLowering into two: ConstLowering which replaces const vals with their values, and PropertiesToFieldsLowering which removes unnecessary property accessors (such as for JvmField or private properties with default accessors) and replaces calls to those accessors with field access --- .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 5 +- .../kotlin/backend/jvm/lower/ConstLowering.kt | 40 ++++++++++++++ ...ering.kt => PropertiesToFieldsLowering.kt} | 54 ++++++++++++------- .../codegen/box/multifileClasses/kt16077.kt | 1 - .../bytecodeText/companion/kt14258_1.kt | 1 - .../bytecodeText/redundantInitializer.kt | 1 - .../redundantInitializerNumber.kt | 1 - 7 files changed, 77 insertions(+), 26 deletions(-) create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ConstLowering.kt rename compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/{ConstAndJvmFieldPropertiesLowering.kt => PropertiesToFieldsLowering.kt} (65%) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 0f8c9d9e396..503b34be87b 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -16,7 +16,7 @@ package org.jetbrains.kotlin.backend.jvm -import org.jetbrains.kotlin.backend.common.* +import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.lower.* import org.jetbrains.kotlin.backend.common.phaser.* import org.jetbrains.kotlin.backend.jvm.lower.* @@ -46,7 +46,8 @@ internal val jvmPhases = namedIrFilePhase( jvmLateinitPhase then moveCompanionObjectFieldsPhase then - constAndJvmFieldPropertiesPhase then + constPhase then + propertiesToFieldsPhase then propertiesPhase then annotationPhase then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ConstLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ConstLowering.kt new file mode 100644 index 00000000000..bcc94f05153 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ConstLowering.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. 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.CommonBackendContext +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrConst +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid + +internal val constPhase = makeIrFilePhase( + ::ConstLowering, + name = "Const", + description = "Substitute calls to const properties with constant values" +) + +class ConstLowering(val context: CommonBackendContext) : IrElementTransformerVoid(), FileLoweringPass { + override fun lower(irFile: IrFile) { + irFile.transformChildrenVoid(this) + } + + override fun visitCall(expression: IrCall): IrExpression { + val irSimpleFunction = (expression.symbol.owner as? IrSimpleFunction) ?: return super.visitCall(expression) + val irProperty = irSimpleFunction.correspondingProperty ?: return super.visitCall(expression) + + if (irProperty.isConst) { + (irProperty.backingField!!.initializer!!.expression as IrConst<*>).let { return it } + } + + return super.visitCall(expression) + } +} diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ConstAndJvmFieldPropertiesLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertiesToFieldsLowering.kt similarity index 65% rename from compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ConstAndJvmFieldPropertiesLowering.kt rename to compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertiesToFieldsLowering.kt index e31eb1141cc..1020f6364a6 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ConstAndJvmFieldPropertiesLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertiesToFieldsLowering.kt @@ -10,9 +10,14 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irBlock import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrFieldAccessExpression +import org.jetbrains.kotlin.ir.expressions.IrTypeOperator import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.util.hasAnnotation @@ -20,45 +25,54 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.load.java.JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME -internal val constAndJvmFieldPropertiesPhase = makeIrFilePhase( - ::ConstAndJvmFieldPropertiesLowering, - name = "ConstAndJvmFieldProperties", - description = "Substitute calls to const and Jvm>Field properties with const/field access" +internal val propertiesToFieldsPhase = makeIrFilePhase( + ::PropertiesToFieldsLowering, + name = "PropertiesToFields", + description = "Replace calls to default property accessors with field access and remove those accessors" ) -private class ConstAndJvmFieldPropertiesLowering(val context: CommonBackendContext) : IrElementTransformerVoid(), FileLoweringPass { +class PropertiesToFieldsLowering(val context: CommonBackendContext) : IrElementTransformerVoid(), FileLoweringPass { override fun lower(irFile: IrFile) { irFile.transformChildrenVoid(this) } override fun visitProperty(declaration: IrProperty): IrStatement { - if (declaration.isConst || declaration.backingField?.hasAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) == true) { - /*Safe or need copy?*/ + if (declaration.isConst || shouldSubstituteAccessorWithField(declaration, declaration.getter)) { declaration.getter = null + } + if (declaration.isConst || shouldSubstituteAccessorWithField(declaration, declaration.setter)) { declaration.setter = null } return super.visitProperty(declaration) } override fun visitCall(expression: IrCall): IrExpression { - val irSimpleFunction = (expression.symbol.owner as? IrSimpleFunction) ?: return super.visitCall(expression) - val irProperty = irSimpleFunction.correspondingProperty ?: return super.visitCall(expression) + val simpleFunction = (expression.symbol.owner as? IrSimpleFunction) ?: return super.visitCall(expression) + val property = simpleFunction.correspondingProperty ?: return super.visitCall(expression) - if (irProperty.isConst) { - (irProperty.backingField!!.initializer!!.expression as IrConst<*>).let { return it } - } - - if (irProperty.backingField?.hasAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) == true) { - return if (expression is IrGetterCallImpl) { - substituteGetter(irProperty, expression) - } else { - assert(expression is IrSetterCallImpl) - substituteSetter(irProperty, expression) + if (shouldSubstituteAccessorWithField(property, simpleFunction)) { + when (expression) { + is IrGetterCallImpl -> return substituteGetter(property, expression) + is IrSetterCallImpl -> return substituteSetter(property, expression) } } + return super.visitCall(expression) } + private fun shouldSubstituteAccessorWithField(property: IrProperty, accessor: IrSimpleFunction?): Boolean { + if (accessor == null) return false + + // In contrast to the old backend, we do generate getters for lateinit properties, which fixes KT-28331 + if (property.isLateinit) return false + + if ((property.parent as? IrClass)?.kind == ClassKind.ANNOTATION_CLASS) return false + + if (property.backingField?.hasAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) == true) return true + + return accessor.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR && Visibilities.isPrivate(accessor.visibility) + } + private fun substituteSetter(irProperty: IrProperty, expression: IrCall): IrExpression { val backingField = irProperty.backingField!! val receiver = expression.dispatchReceiver?.transform(this, null) diff --git a/compiler/testData/codegen/box/multifileClasses/kt16077.kt b/compiler/testData/codegen/box/multifileClasses/kt16077.kt index 7dc257128ca..3639809c63d 100644 --- a/compiler/testData/codegen/box/multifileClasses/kt16077.kt +++ b/compiler/testData/codegen/box/multifileClasses/kt16077.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // WITH_RUNTIME diff --git a/compiler/testData/codegen/bytecodeText/companion/kt14258_1.kt b/compiler/testData/codegen/bytecodeText/companion/kt14258_1.kt index 9032de3a1cc..a409a0669aa 100644 --- a/compiler/testData/codegen/bytecodeText/companion/kt14258_1.kt +++ b/compiler/testData/codegen/bytecodeText/companion/kt14258_1.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // Checks that methods 'access$getMy$p', 'access$getMy$cp' and 'getMy' are not generated and // that backed field 'my' is directly used through a 'getstatic' diff --git a/compiler/testData/codegen/bytecodeText/redundantInitializer.kt b/compiler/testData/codegen/bytecodeText/redundantInitializer.kt index e45ab299e74..b074ba3c380 100644 --- a/compiler/testData/codegen/bytecodeText/redundantInitializer.kt +++ b/compiler/testData/codegen/bytecodeText/redundantInitializer.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR package a class A { diff --git a/compiler/testData/codegen/bytecodeText/redundantInitializerNumber.kt b/compiler/testData/codegen/bytecodeText/redundantInitializerNumber.kt index 864372dda21..a63ee553776 100644 --- a/compiler/testData/codegen/bytecodeText/redundantInitializerNumber.kt +++ b/compiler/testData/codegen/bytecodeText/redundantInitializerNumber.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR package a class A {