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:
+2
-1
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+2
-7
@@ -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)
|
||||
|
||||
+3
-8
@@ -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()!!
|
||||
|
||||
+18
-10
@@ -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)
|
||||
}
|
||||
|
||||
+7
-6
@@ -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
@@ -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
|
||||
+2
-3
@@ -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
|
||||
|
||||
+2
-3
@@ -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
|
||||
|
||||
+2
-3
@@ -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
|
||||
|
||||
+2
-3
@@ -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
|
||||
|
||||
+2
-3
@@ -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
|
||||
|
||||
+2
-3
@@ -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
|
||||
|
||||
+2
-3
@@ -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"
|
||||
Reference in New Issue
Block a user