JVM_IR: move const initialization handling to lowering

Had to edit some bytecodeText tests to account for the fact that JVM_IR
no longer generates explicit initializations for ConstantValue fields,
but NoConstantValueAttributeForNonConstVals is not the default yet.
This commit is contained in:
pyos
2019-11-23 14:26:32 +01:00
committed by Alexander Udalov
parent 2ebe269448
commit fc6b03b08f
22 changed files with 61 additions and 74 deletions
@@ -39,7 +39,7 @@ open class InitializersLowering(context: CommonBackendContext) : InitializersLow
}
}
abstract class InitializersLoweringBase(val context: CommonBackendContext) : ClassLoweringPass {
abstract class InitializersLoweringBase(open val context: CommonBackendContext) : ClassLoweringPass {
protected fun extractInitializers(irClass: IrClass, filter: (IrDeclaration) -> Boolean) =
irClass.declarations.filter(filter).mapNotNull {
when (it) {
@@ -57,6 +57,7 @@ abstract class InitializersLoweringBase(val context: CommonBackendContext) : Cla
IrGetValueImpl(startOffset, endOffset, irClass.thisReceiver!!.type, irClass.thisReceiver!!.symbol)
else
null
declaration.initializer = null
IrSetFieldImpl(startOffset, endOffset, declaration.symbol, receiver, expression, context.irBuiltIns.unitType)
}
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.backend.jvm.codegen
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
import org.jetbrains.kotlin.backend.jvm.lower.buildAssertionsDisabledField
import org.jetbrains.kotlin.backend.jvm.lower.constantValue
import org.jetbrains.kotlin.backend.jvm.lower.hasAssertionsDisabledField
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
@@ -18,13 +17,13 @@ import org.jetbrains.kotlin.codegen.inline.ReifiedTypeParametersUsages
import org.jetbrains.kotlin.codegen.inline.SourceMapper
import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.WrappedClassDescriptor
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
@@ -332,13 +331,9 @@ open class ClassCodegen protected constructor(
if (field.origin == IrDeclarationOrigin.DELEGATE) null
else methodSignatureMapper.mapFieldSignature(field)
val fieldName = field.name.asString()
// The ConstantValue attribute makes the initializer part of the ABI, which is why since 1.4
// it is no longer set unless the property is explicitly `const`.
val implicitConst = !state.languageVersionSettings.supportsFeature(LanguageFeature.NoConstantValueAttributeForNonConstVals) &&
(AsmUtil.isPrimitive(fieldType) || fieldType == AsmTypes.JAVA_STRING_TYPE)
val fv = visitor.newField(
field.OtherOrigin, field.flags, fieldName, fieldType.descriptor,
fieldSignature, field.constantValue(implicitConst)?.value
fieldSignature, (field.initializer?.expression as? IrConst<*>)?.value
)
AnnotationCodegen(this, context, fv::visitAnnotation).genAnnotations(field, fieldType)
@@ -463,14 +463,9 @@ class ExpressionCodegen(
override fun visitFieldAccess(expression: IrFieldAccessExpression, data: BlockInfo): PromisedValue {
val callee = expression.symbol.owner
callee.constantValue()?.let {
if (context.state.shouldInlineConstVals) {
// Handling const reads before codegen is important for constant folding.
assert(expression is IrSetField) { "read of const val ${callee.name} not inlined by ConstLowering" }
// This can only be the field's initializer; JVM implementations are required
// to generate those for ConstantValue-marked fields automatically, so this is redundant.
return defaultValue(expression.type)
}
if (context.state.shouldInlineConstVals) {
// Const fields should only have reads, and those should have been transformed by ConstLowering.
assert(callee.constantValue() == null) { "access of const val: ${expression.dump()}" }
}
val realField = callee.resolveFakeOverride()!!
@@ -8,6 +8,7 @@ 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.config.LanguageFeature
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFile
@@ -17,6 +18,8 @@ import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetField
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.types.isPrimitiveType
import org.jetbrains.kotlin.ir.types.isStringClassType
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
internal val constPhase = makeIrFilePhase(
@@ -25,23 +28,28 @@ internal val constPhase = makeIrFilePhase(
description = "Substitute calls to const properties with constant values"
)
fun IrField.constantValue(implicitConst: Boolean = false): IrConst<*>? {
// javac always inlines reads of static final fields, so do that for ones imported from Java.
// Kotlin fields are only inlined if explicitly `const` to avoid making the values part of the ABI.
val inline = correspondingPropertySymbol?.owner?.isConst == true ||
(isStatic && isFinal && (origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB || implicitConst))
return if (inline) (initializer?.expression as? IrConst<*>)?.copy() else null
fun IrField.constantValue(context: JvmBackendContext? = null): IrConst<*>? {
val value = initializer?.expression as? IrConst<*> ?: return null
// JVM has a ConstantValue attribute which does two things:
// 1. allows the field to be inlined into other modules;
// 2. implicitly generates an initialization of that field in <clinit>
// It is only allowed on static final fields of primitive/string types. Java and Kotlin < 1.4
// apply it whenever possible; Kotlin >= 1.4 only applies it to `const val`s to avoid making
// values part of the library's ABI unless explicitly requested by the author.
val allowImplicitConst =
context != null && !context.state.languageVersionSettings.supportsFeature(LanguageFeature.NoConstantValueAttributeForNonConstVals)
val implicitConst = isStatic && isFinal && (origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB ||
(allowImplicitConst && (type.isPrimitiveType() || type.isStringClassType())))
return if (implicitConst || correspondingPropertySymbol?.owner?.isConst == true) value else null
}
class ConstLowering(val context: JvmBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid()
private fun IrExpression.lowerConstRead(field: IrField?): IrExpression? {
val value = field?.constantValue()
?: return null
val value = field?.constantValue() ?: return null
return if (context.state.shouldInlineConstVals)
value
value.copy()
else
IrGetFieldImpl(startOffset, endOffset, field.symbol, field.type)
}
@@ -5,8 +5,8 @@
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.InitializersLoweringBase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.declarations.IrAnonymousInitializer
@@ -15,13 +15,15 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.expressions.IrSetField
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.name.Name
class StaticInitializersLowering(context: CommonBackendContext) : InitializersLoweringBase(context) {
class StaticInitializersLowering(override val context: JvmBackendContext) : InitializersLoweringBase(context) {
override fun lower(irClass: IrClass) {
val staticInitializerStatements = extractInitializers(irClass) {
(it is IrField && it.isStatic) || (it is IrAnonymousInitializer && it.isStatic)
// JVM implementations are required to generate initializers for all static fields with ConstantValue,
// so don't add any to <clinit>.
(it is IrField && it.isStatic && it.constantValue(context) == null) || (it is IrAnonymousInitializer && it.isStatic)
}.toMutableList()
if (staticInitializerStatements.isNotEmpty()) {
staticInitializerStatements.sortBy {
@@ -40,8 +42,7 @@ class StaticInitializersLowering(context: CommonBackendContext) : InitializersLo
origin = JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER
returnType = context.irBuiltIns.unitType
}.apply {
body = IrBlockBodyImpl(irClass.startOffset, irClass.endOffset, staticInitializerStatements)
.deepCopyWithSymbols(this)
body = IrBlockBodyImpl(irClass.startOffset, irClass.endOffset, staticInitializerStatements).patchDeclarationParents(this)
}
}
}