JVM_IR: set ConstantValue on static final primitive fields

This commit is contained in:
pyos
2019-04-11 12:44:38 +02:00
committed by max-kammerer
parent 14485c0e33
commit b23f2a4dbb
20 changed files with 40 additions and 29 deletions
@@ -66,8 +66,8 @@ val jvmPhases = namedIrFilePhase(
jvmLateinitPhase then jvmLateinitPhase then
moveCompanionObjectFieldsPhase then moveCompanionObjectFieldsPhase then
constPhase then
propertyReferencePhase then propertyReferencePhase then
constPhase then
propertiesToFieldsPhase then propertiesToFieldsPhase then
propertiesPhase then propertiesPhase then
renameFieldsPhase then renameFieldsPhase then
@@ -18,17 +18,17 @@ package org.jetbrains.kotlin.backend.jvm.codegen
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.lower.constantValue
import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.inline.DefaultSourceMapper import org.jetbrains.kotlin.codegen.inline.DefaultSourceMapper
import org.jetbrains.kotlin.codegen.inline.SourceMapper import org.jetbrains.kotlin.codegen.inline.SourceMapper
import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils.isTopLevelDeclaration import org.jetbrains.kotlin.resolve.DescriptorUtils.isTopLevelDeclaration
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.resolve.source.getPsi
@@ -218,9 +219,13 @@ open class ClassCodegen protected constructor(
val fieldType = typeMapper.mapType(field.descriptor) val fieldType = typeMapper.mapType(field.descriptor)
val fieldSignature = typeMapper.mapFieldSignature(field.descriptor.type, field.descriptor) val fieldSignature = typeMapper.mapFieldSignature(field.descriptor.type, field.descriptor)
val fieldName = field.descriptor.name.asString() val fieldName = field.descriptor.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( val fv = visitor.newField(
field.OtherOrigin, field.flags, fieldName, fieldType.descriptor, field.OtherOrigin, field.flags, fieldName, fieldType.descriptor,
fieldSignature, null/*TODO support default values*/ fieldSignature, field.constantValue(implicitConst)?.value
) )
AnnotationCodegen(this, state, fv::visitAnnotation).genAnnotations(field, fieldType) AnnotationCodegen(this, state, fv::visitAnnotation).genAnnotations(field, fieldType)
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.intrinsics.JavaClassProperty import org.jetbrains.kotlin.backend.jvm.intrinsics.JavaClassProperty
import org.jetbrains.kotlin.backend.jvm.intrinsics.Not import org.jetbrains.kotlin.backend.jvm.intrinsics.Not
import org.jetbrains.kotlin.backend.jvm.lower.CrIrType import org.jetbrains.kotlin.backend.jvm.lower.CrIrType
import org.jetbrains.kotlin.backend.jvm.lower.constantValue
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.AsmUtil.* import org.jetbrains.kotlin.codegen.AsmUtil.*
@@ -421,6 +422,14 @@ class ExpressionCodegen(
} }
override fun visitFieldAccess(expression: IrFieldAccessExpression, data: BlockInfo): PromisedValue { override fun visitFieldAccess(expression: IrFieldAccessExpression, data: BlockInfo): PromisedValue {
expression.symbol.owner.constantValue()?.let {
// Handling const reads before codegen is important for constant folding.
assert(expression is IrSetField) { "read of const val ${expression.symbol.owner.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 voidValue.coerce(expression.asmType)
}
val realDescriptor = DescriptorUtils.unwrapFakeOverride(expression.descriptor) val realDescriptor = DescriptorUtils.unwrapFakeOverride(expression.descriptor)
val fieldType = typeMapper.mapType(realDescriptor.original.type) val fieldType = typeMapper.mapType(realDescriptor.original.type)
val ownerType = typeMapper.mapImplementationOwner(expression.descriptor).internalName val ownerType = typeMapper.mapImplementationOwner(expression.descriptor).internalName
@@ -8,11 +8,14 @@ package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetField
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
@@ -22,19 +25,27 @@ internal val constPhase = makeIrFilePhase(
description = "Substitute calls to const properties with constant values" 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<*> else null
}
class ConstLowering(val context: CommonBackendContext) : IrElementTransformerVoid(), FileLoweringPass { class ConstLowering(val context: CommonBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(this) irFile.transformChildrenVoid(this)
} }
override fun visitCall(expression: IrCall): IrExpression { override fun visitCall(expression: IrCall): IrExpression {
val irSimpleFunction = (expression.symbol.owner as? IrSimpleFunction) ?: return super.visitCall(expression) val function = (expression.symbol.owner as? IrSimpleFunction) ?: return super.visitCall(expression)
val irProperty = irSimpleFunction.correspondingProperty ?: return super.visitCall(expression) val property = function.correspondingPropertySymbol?.owner ?: return super.visitCall(expression)
if (function != property.getter)
if (irProperty.isConst) { return super.visitCall(expression)
(irProperty.backingField!!.initializer!!.expression as IrConst<*>).let { return it } return property.backingField?.constantValue() ?: super.visitCall(expression)
}
return super.visitCall(expression)
} }
override fun visitGetField(expression: IrGetField): IrExpression =
expression.symbol.owner.constantValue() ?: super.visitGetField(expression)
} }
@@ -101,6 +101,7 @@ private class MoveCompanionObjectFieldsLowering(val context: CommonBackendContex
val newField = createStaticBackingField(oldField, propertyParent, fieldParent) val newField = createStaticBackingField(oldField, propertyParent, fieldParent)
irProperty.backingField = newField irProperty.backingField = newField
newField.correspondingPropertySymbol = irProperty.symbol
fieldReplacementMap[oldField.symbol] = newField.symbol fieldReplacementMap[oldField.symbol] = newField.symbol
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// WITH_REFLECT // WITH_REFLECT
// FILE: 1.kt // FILE: 1.kt
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// IGNORE_LIGHT_ANALYSIS // IGNORE_LIGHT_ANALYSIS
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
object A { object A {
private const val a = "$" private const val a = "$"
private const val b = "1234$a" private const val b = "1234$a"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JClass.java // FILE: JClass.java
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: first/Foo.java // FILE: first/Foo.java
package first; package first;
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JClass.java // FILE: JClass.java
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
@file:JvmName("ABC") @file:JvmName("ABC")
package test; package test;
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
package test package test
annotation class AString(val value: String) annotation class AString(val value: String)
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
package test package test
class KotlinClass { class KotlinClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// FILE: A.kt // FILE: A.kt
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// FILE: A.kt // FILE: A.kt
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// FILE: A.kt // FILE: A.kt
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// FILE: A.kt // FILE: A.kt
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: NATIVE // IGNORE_BACKEND: NATIVE
// WITH_RUNTIME // WITH_RUNTIME