From d1a54a522d3af3695d7dd71f8e650936be2b55f9 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Thu, 15 Jun 2023 17:15:07 +0200 Subject: [PATCH] [JVM IR] Drop `ConstLowering` It basically can be replaced with IR interpreter. The only half-hack was required in `PropertyReferenceLowering`. Const interpreter is running before it, so we can't optimize some calls on const properties that appear after this lowering. Solution is to inline constants manually during property reference lowering. --- .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 2 - .../kotlin/backend/jvm/lower/ConstLowering.kt | 45 ------------ .../jvm/lower/PropertyReferenceLowering.kt | 13 ++++ .../IrInterpreterConstGetterPreprocessor.kt | 69 +++++++++++++++++++ .../IrInterpreterKCallableNamePreprocessor.kt | 2 - .../transformer/IrConstTransformer.kt | 3 +- .../unsignedTypesInAnnotations.kt | 1 - 7 files changed, 84 insertions(+), 51 deletions(-) delete mode 100644 compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ConstLowering.kt create mode 100644 compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/preprocessor/IrInterpreterConstGetterPreprocessor.kt diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 1adbb8888f2..ba9974fb9f6 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -369,7 +369,6 @@ private val jvmFilePhases = listOf( singletonOrConstantDelegationPhase, propertyReferencePhase, arrayConstructorPhase, - constPhase1, // TODO: merge the next three phases together, as visitors behave incorrectly between them // (backing fields moved out of companion objects are reachable by two paths): @@ -422,7 +421,6 @@ private val jvmFilePhases = listOf( tailCallOptimizationPhase, addContinuationPhase, - constPhase2, // handle const properties in default arguments of "original" suspend funs innerClassesPhase, innerClassesMemberBodyPhase, diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ConstLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ConstLowering.kt deleted file mode 100644 index 4f9542a7157..00000000000 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ConstLowering.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2010-2019 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.lower.InlineConstTransformer -import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase -import org.jetbrains.kotlin.backend.jvm.JvmBackendContext -import org.jetbrains.kotlin.backend.jvm.ir.constantValue -import org.jetbrains.kotlin.config.CommonConfigurationKeys -import org.jetbrains.kotlin.incremental.components.InlineConstTracker -import org.jetbrains.kotlin.ir.declarations.IrField -import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.expressions.IrConst -import org.jetbrains.kotlin.ir.interpreter.transformer.reportOnIr -import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid - -internal val constPhase1 = makeIrFilePhase( - ::ConstLowering, - name = "Const1", - description = "Substitute calls to const properties with constant values" -) - -internal val constPhase2 = makeIrFilePhase( - ::ConstLowering, - name = "Const2", - description = "Substitute calls to const properties with constant values" -) - -class ConstLowering(val context: JvmBackendContext) : FileLoweringPass { - val inlineConstTracker = - context.state.configuration[CommonConfigurationKeys.INLINE_CONST_TRACKER] - - override fun lower(irFile: IrFile) = irFile.transformChildrenVoid(JvmInlineConstTransformer(irFile, inlineConstTracker)) -} - -private class JvmInlineConstTransformer(val irFile: IrFile, val inlineConstTracker: InlineConstTracker?) : InlineConstTransformer() { - override val IrField.constantInitializer get() = constantValue() - override fun reportInlineConst(field: IrField, value: IrConst<*>) { - inlineConstTracker?.reportOnIr(irFile, field, value) - } -} \ No newline at end of file diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt index 05398bef076..9a45c5c1ce1 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt @@ -65,6 +65,17 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : IrEle private val IrMemberAccessExpression<*>.field: IrFieldSymbol? get() = (this as? IrPropertyReference)?.field + private val IrMemberAccessExpression<*>.constInitializer: IrExpression? + get() { + if (this !is IrPropertyReference) return null + val constPropertyField = if (field == null) { + symbol.owner.takeIf { it.isConst }?.backingField + } else { + field!!.owner.takeIf { it.isFinal && it.isStatic } + } + return constPropertyField?.initializer?.expression?.shallowCopyOrNull() + } + private val arrayItemGetter = context.ir.symbols.array.owner.functions.single { it.name.asString() == "get" } @@ -441,6 +452,7 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : IrEle expression.getter?.owner?.let { getter -> referenceClass.addOverride(get!!) { arguments -> + expression.constInitializer?.let { return@addOverride it } irGet(getter.returnType, null, getter.symbol).apply { setCallArguments(this, arguments) } @@ -469,6 +481,7 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : IrEle } referenceClass.addOverride(get!!) { arguments -> + expression.constInitializer?.let { return@addOverride it } irGetField(fieldReceiver(arguments), field) } diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/preprocessor/IrInterpreterConstGetterPreprocessor.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/preprocessor/IrInterpreterConstGetterPreprocessor.kt new file mode 100644 index 00000000000..a5562a3789b --- /dev/null +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/preprocessor/IrInterpreterConstGetterPreprocessor.kt @@ -0,0 +1,69 @@ +/* + * 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.ir.interpreter.preprocessor + +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin +import org.jetbrains.kotlin.ir.declarations.IrField +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl +import org.jetbrains.kotlin.ir.types.classOrFail + +class IrInterpreterConstGetterPreprocessor : IrInterpreterPreprocessor { + override fun visitFunction(declaration: IrFunction, data: IrInterpreterPreprocessorData): IrStatement { + // It is useless to visit default accessor, we probably want to leave code there as it is + if (declaration.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) return declaration + return super.visitFunction(declaration, data) + } + + override fun visitCall(expression: IrCall, data: IrInterpreterPreprocessorData): IrElement { + val function = (expression.symbol.owner as? IrSimpleFunction) ?: return super.visitCall(expression, data) + val field = function.correspondingPropertySymbol?.owner?.backingField ?: return super.visitCall(expression, data) + return expression.lowerConstRead(field, data) ?: super.visitCall(expression, data) + } + + override fun visitGetField(expression: IrGetField, data: IrInterpreterPreprocessorData): IrExpression { + return expression.lowerConstRead(expression.symbol.owner, data) ?: super.visitGetField(expression, data) + } + + private fun IrExpression.lowerConstRead(field: IrField, data: IrInterpreterPreprocessorData): IrExpression? { + val receiver = when (this) { + is IrCall -> dispatchReceiver + is IrGetField -> receiver + else -> return null + } + + if (receiver == null || !field.hasConstantValue()) return null + + transformChildren(this@IrInterpreterConstGetterPreprocessor, data) + + val getObject = IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, receiver.type, receiver.type.classOrFail) + when (this) { + is IrCall -> this.dispatchReceiver = getObject + is IrGetField -> this.receiver = getObject + } + + return if (receiver.shouldDropConstReceiver()) { + this + } else { + IrCompositeImpl(startOffset, endOffset, this.type, null, listOf(receiver, this)) + } + } + + private fun IrExpression.shouldDropConstReceiver(): Boolean { + return this is IrGetValue || this is IrGetObjectValue + } + + fun IrField.hasConstantValue(): Boolean { + val implicitConst = isFinal && isStatic && origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && initializer != null + return implicitConst || correspondingPropertySymbol?.owner?.isConst == true + } +} \ No newline at end of file diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/preprocessor/IrInterpreterKCallableNamePreprocessor.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/preprocessor/IrInterpreterKCallableNamePreprocessor.kt index 3a528e8d93a..b2b28e566eb 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/preprocessor/IrInterpreterKCallableNamePreprocessor.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/preprocessor/IrInterpreterKCallableNamePreprocessor.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.ir.interpreter.preprocessor -import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrClass @@ -18,7 +17,6 @@ import org.jetbrains.kotlin.ir.expressions.IrGetValue import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl import org.jetbrains.kotlin.ir.interpreter.property import org.jetbrains.kotlin.ir.types.IrSimpleType -import org.jetbrains.kotlin.ir.types.classFqName import org.jetbrains.kotlin.ir.types.typeOrNull import org.jetbrains.kotlin.ir.types.typeWith import org.jetbrains.kotlin.ir.util.isKFunction diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/transformer/IrConstTransformer.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/transformer/IrConstTransformer.kt index 5fd8e491201..2248f564a9c 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/transformer/IrConstTransformer.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/transformer/IrConstTransformer.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.interpreter.IrInterpreter import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration import org.jetbrains.kotlin.ir.interpreter.checker.* +import org.jetbrains.kotlin.ir.interpreter.preprocessor.IrInterpreterConstGetterPreprocessor import org.jetbrains.kotlin.ir.interpreter.preprocessor.IrInterpreterKCallableNamePreprocessor import org.jetbrains.kotlin.ir.interpreter.preprocessor.IrInterpreterPreprocessorData import org.jetbrains.kotlin.ir.interpreter.property @@ -72,7 +73,7 @@ fun IrFile.preprocessForConstTransformer( interpreter: IrInterpreter, mode: EvaluationMode, ): IrFile { - val preprocessors = setOf(IrInterpreterKCallableNamePreprocessor()) + val preprocessors = setOf(IrInterpreterKCallableNamePreprocessor(), IrInterpreterConstGetterPreprocessor()) val preprocessedFile = preprocessors.fold(this) { file, preprocessor -> preprocessor.preprocess(file, IrInterpreterPreprocessorData(mode, interpreter.irBuiltIns)) } diff --git a/compiler/testData/codegen/box/compileKotlinAgainstKotlin/unsignedTypesInAnnotations.kt b/compiler/testData/codegen/box/compileKotlinAgainstKotlin/unsignedTypesInAnnotations.kt index 61ad12ad6bb..0c6934db3ac 100644 --- a/compiler/testData/codegen/box/compileKotlinAgainstKotlin/unsignedTypesInAnnotations.kt +++ b/compiler/testData/codegen/box/compileKotlinAgainstKotlin/unsignedTypesInAnnotations.kt @@ -1,6 +1,5 @@ // IGNORE_BACKEND: NATIVE // WITH_STDLIB -// WITH_STDLIB // WITH_REFLECT // MODULE: lib