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)
}
}
}
@@ -1,5 +1,4 @@
// IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -10,7 +10,6 @@ class My {
}
// 1 GETSTATIC My.my
// 1 PUTSTATIC My.my
// 0 INVOKESTATIC My\$Companion.access\$getMy\$p
// 0 INVOKESTATIC My.access\$getMy\$cp
// 0 INVOKESPECIAL My\$Companion.getMy
@@ -1,5 +1,4 @@
// Generates ICONST_1
val a = 1
val a = 2
fun main() {
// Generates IF_ICMPNE
@@ -34,7 +33,7 @@ fun main() {
}
//0 ICONST_0
//1 ICONST_1
//0 ICONST_1
//2 IF_ICMPNE
//1 IF_ICMPLE
//1 IF_ICMPLT
@@ -1,5 +1,4 @@
// Generates ICONST_1
val a = 1
val a = 2
fun main() {
// Generates IF_ICMPEQ and GOTO
@@ -46,7 +45,7 @@ fun main() {
}
//0 ICONST_0
//1 ICONST_1
//0 ICONST_1
//2 IF_ICMPEQ
//1 IF_ICMPLE
//1 IF_ICMPLT
@@ -1,5 +1,4 @@
// Generates ICONST_1
val a = 1
val a = 2
fun main() {
// Generates IF_ICMPEQ and GOTO
@@ -34,7 +33,7 @@ fun main() {
}
//0 ICONST_0
//1 ICONST_1
//0 ICONST_1
//2 IF_ICMPEQ
//1 IF_ICMPLE
//1 IF_ICMPLT
@@ -1,5 +1,4 @@
// Generates ICONST_1
val a = 1
val a = 2
fun main() {
// Generates IFNE
@@ -34,7 +33,7 @@ fun main() {
}
//0 ICONST_0
//1 ICONST_1
//0 ICONST_1
//2 IFNE
//1 IFLE
//1 IFLT
@@ -1,5 +1,4 @@
// Generates ICONST_1
val a = 1
val a = 2
fun main() {
// Generates IFEQ and GOTO
@@ -46,7 +45,7 @@ fun main() {
}
//0 ICONST_0
//1 ICONST_1
//0 ICONST_1
//2 IFEQ
//1 IFLE
//1 IFLT
@@ -1,5 +1,4 @@
// Generates ICONST_1
val a = 1
val a = 2
fun main() {
// Generates IFEQ and GOTO
@@ -34,7 +33,7 @@ fun main() {
}
//0 ICONST_0
//1 ICONST_1
//0 ICONST_1
//2 IFEQ
//1 IFLE
//1 IFLT
@@ -1,5 +1,4 @@
// Generates ICONST_1
val a = 1
val a = 2
fun main() {
// Generates IF_ICMPEQ
@@ -29,7 +28,7 @@ fun main() {
}
//0 ICONST_0
//1 ICONST_1
//0 ICONST_1
//1 IF_ICMPEQ
//1 IF_ICMPLE
//1 IF_ICMPLT
@@ -1,5 +1,4 @@
// Generates ICONST_1
val a = 1
val a = 2
fun main() {
// Generates IF_ICMPNE and GOTO
@@ -39,7 +38,7 @@ fun main() {
}
//0 ICONST_0
//1 ICONST_1
//0 ICONST_1
//1 IF_ICMPNE
//1 IF_ICMPLE
//1 IF_ICMPLT
@@ -1,5 +1,4 @@
// Generates ICONST_1
val a = 1
val a = 2
fun main() {
// Generates IF_ICMPNE and GOTO
@@ -29,7 +28,7 @@ fun main() {
}
//0 ICONST_0
//1 ICONST_1
//0 ICONST_1
//1 IF_ICMPNE
//1 IF_ICMPLE
//1 IF_ICMPLT
@@ -1,5 +1,4 @@
// Generates ICONST_1
val a = 1
val a = 2
fun main() {
// Generates IFEQ
@@ -29,7 +28,7 @@ fun main() {
}
//0 ICONST_0
//1 ICONST_1
//0 ICONST_1
//1 IFEQ
//1 IFLE
//1 IFLT
@@ -1,5 +1,4 @@
// Generates ICONST_1
val a = 1
val a = 0
fun main() {
// Generates IFNE and GOTO
@@ -39,7 +38,7 @@ fun main() {
}
//0 ICONST_0
//1 ICONST_1
//0 ICONST_1
//1 IFNE
//1 IFLE
//1 IFLT
@@ -1,5 +1,4 @@
// Generates ICONST_1
val a = 1
val a = 2
fun main() {
// Generates IFNE and GOTO
@@ -29,7 +28,7 @@ fun main() {
}
//0 ICONST_0
//1 ICONST_1
//0 ICONST_1
//1 IFNE
//1 IFLE
//1 IFLT
@@ -1,3 +1,4 @@
// !LANGUAGE: +NoConstantValueAttributeForNonConstVals
val a = 1.0 + 10
val b = 2 + 10.0
val c = 3.0F + 10
@@ -1,5 +1,5 @@
val s = "1" + "2" + 3 + 4L + 5.0 + 6F + '7'
val c = "${"1"}2${3}${4L}${5.0}${6F}${'7'}"
fun s() = "1" + "2" + 3 + 4L + 5.0 + 6F + '7'
fun c() = "${"1"}2${3}${4L}${5.0}${6F}${'7'}"
// 0 NEW java/lang/StringBuilder
// 2 LDC "12345.06.07"
@@ -5,8 +5,8 @@ const val double = 5.0
const val float = 6F
const val char = '7'
val s = "1" + string + int + long + double + float + char
val c = "1$string$int$long$double$float$char"
fun s() = "1" + string + int + long + double + float + char
fun c() = "1$string$int$long$double$float$char"
// 0 NEW java/lang/StringBuilder
// 2 LDC "12345.06.07"