JVM IR: Optimize static property references (KT-36975)
This commit is contained in:
committed by
Alexander Udalov
parent
669fda6b77
commit
4792be2522
@@ -53,6 +53,6 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
|
|||||||
object FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE : IrDeclarationOriginImpl("FOR_INLINE_TEMPLATE_CROSSINLINE")
|
object FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE : IrDeclarationOriginImpl("FOR_INLINE_TEMPLATE_CROSSINLINE")
|
||||||
object CONTINUATION_CLASS_RESULT_FIELD: IrDeclarationOriginImpl("CONTINUATION_CLASS_RESULT_FIELD", isSynthetic = true)
|
object CONTINUATION_CLASS_RESULT_FIELD: IrDeclarationOriginImpl("CONTINUATION_CLASS_RESULT_FIELD", isSynthetic = true)
|
||||||
object COMPANION_PROPERTY_BACKING_FIELD : IrDeclarationOriginImpl("COMPANION_MOVED_PROPERTY_BACKING_FIELD")
|
object COMPANION_PROPERTY_BACKING_FIELD : IrDeclarationOriginImpl("COMPANION_MOVED_PROPERTY_BACKING_FIELD")
|
||||||
object FIELD_FOR_STATIC_LAMBDA_INSTANCE : IrDeclarationOriginImpl("FIELD_FOR_STATIC_LAMBDA_INSTANCE")
|
object FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE : IrDeclarationOriginImpl("FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE")
|
||||||
object ABSTRACT_BRIDGE_STUB : IrDeclarationOriginImpl("ABSTRACT_BRIDGE_STUB")
|
object ABSTRACT_BRIDGE_STUB : IrDeclarationOriginImpl("ABSTRACT_BRIDGE_STUB")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ private val jvmFilePhases = listOf(
|
|||||||
returnableBlocksPhase,
|
returnableBlocksPhase,
|
||||||
localDeclarationsPhase,
|
localDeclarationsPhase,
|
||||||
jvmLocalClassExtractionPhase,
|
jvmLocalClassExtractionPhase,
|
||||||
staticLambdaPhase,
|
staticCallableReferencePhase,
|
||||||
|
|
||||||
jvmDefaultConstructorPhase,
|
jvmDefaultConstructorPhase,
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -13,8 +13,8 @@ import org.jetbrains.kotlin.backend.jvm.lower.hasAssertionsDisabledField
|
|||||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||||
import org.jetbrains.kotlin.codegen.inline.*
|
import org.jetbrains.kotlin.codegen.inline.*
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
|
||||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
|
||||||
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
||||||
@@ -270,7 +270,7 @@ abstract class ClassCodegen protected constructor(
|
|||||||
if (field.origin != JvmLoweredDeclarationOrigin.CONTINUATION_CLASS_RESULT_FIELD) {
|
if (field.origin != JvmLoweredDeclarationOrigin.CONTINUATION_CLASS_RESULT_FIELD) {
|
||||||
val skipNullabilityAnnotations =
|
val skipNullabilityAnnotations =
|
||||||
flags and (Opcodes.ACC_SYNTHETIC or Opcodes.ACC_ENUM) != 0 ||
|
flags and (Opcodes.ACC_SYNTHETIC or Opcodes.ACC_ENUM) != 0 ||
|
||||||
field.origin == JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_LAMBDA_INSTANCE
|
field.origin == JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE
|
||||||
object : AnnotationCodegen(this@ClassCodegen, context, skipNullabilityAnnotations) {
|
object : AnnotationCodegen(this@ClassCodegen, context, skipNullabilityAnnotations) {
|
||||||
override fun visitAnnotation(descr: String?, visible: Boolean): AnnotationVisitor {
|
override fun visitAnnotation(descr: String?, visible: Boolean): AnnotationVisitor {
|
||||||
return fv.visitAnnotation(descr, visible)
|
return fv.visitAnnotation(descr, visible)
|
||||||
|
|||||||
+15
-13
@@ -35,21 +35,21 @@ import org.jetbrains.kotlin.name.Name
|
|||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
internal val staticLambdaPhase = makeIrFilePhase(
|
internal val staticCallableReferencePhase = makeIrFilePhase(
|
||||||
::StaticLambdaLowering,
|
::StaticCallableReferenceLowering,
|
||||||
name = "StaticLambdaPhase",
|
name = "StaticCallableReferencePhase",
|
||||||
description = "Turn static callable references into singletons"
|
description = "Turn static callable references into singletons"
|
||||||
)
|
)
|
||||||
|
|
||||||
class StaticLambdaLowering(val backendContext: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() {
|
class StaticCallableReferenceLowering(val backendContext: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() {
|
||||||
private val staticLambdaFields = HashMap<IrClass, IrField>()
|
private val staticInstanceFields = HashMap<IrClass, IrField>()
|
||||||
|
|
||||||
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid()
|
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid()
|
||||||
|
|
||||||
override fun visitClass(declaration: IrClass): IrStatement {
|
override fun visitClass(declaration: IrClass): IrStatement {
|
||||||
declaration.transformChildrenVoid()
|
declaration.transformChildrenVoid()
|
||||||
if (declaration.isSyntheticSingleton) {
|
if (declaration.isSyntheticSingleton) {
|
||||||
declaration.declarations += getFieldForStaticLambdaInstance(declaration).also { field ->
|
declaration.declarations += getFieldForStaticCallableReferenceInstance(declaration).also { field ->
|
||||||
field.initializer = backendContext.createIrBuilder(field.symbol).run {
|
field.initializer = backendContext.createIrBuilder(field.symbol).run {
|
||||||
irExprBody(irCall(declaration.primaryConstructor!!))
|
irExprBody(irCall(declaration.primaryConstructor!!))
|
||||||
}
|
}
|
||||||
@@ -58,17 +58,17 @@ class StaticLambdaLowering(val backendContext: JvmBackendContext) : FileLowering
|
|||||||
return declaration
|
return declaration
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFieldForStaticLambdaInstance(lambdaClass: IrClass): IrField =
|
private fun getFieldForStaticCallableReferenceInstance(irClass: IrClass): IrField =
|
||||||
staticLambdaFields.getOrPut(lambdaClass) {
|
staticInstanceFields.getOrPut(irClass) {
|
||||||
backendContext.irFactory.buildField {
|
backendContext.irFactory.buildField {
|
||||||
name = Name.identifier(JvmAbi.INSTANCE_FIELD)
|
name = Name.identifier(JvmAbi.INSTANCE_FIELD)
|
||||||
type = lambdaClass.defaultType
|
type = irClass.defaultType
|
||||||
origin = JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_LAMBDA_INSTANCE
|
origin = JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE
|
||||||
isFinal = true
|
isFinal = true
|
||||||
isStatic = true
|
isStatic = true
|
||||||
visibility = DescriptorVisibilities.PUBLIC
|
visibility = DescriptorVisibilities.PUBLIC
|
||||||
}.apply {
|
}.apply {
|
||||||
parent = lambdaClass
|
parent = irClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,14 +77,16 @@ class StaticLambdaLowering(val backendContext: JvmBackendContext) : FileLowering
|
|||||||
if (!constructor.constructedClass.isSyntheticSingleton)
|
if (!constructor.constructedClass.isSyntheticSingleton)
|
||||||
return super.visitConstructorCall(expression)
|
return super.visitConstructorCall(expression)
|
||||||
|
|
||||||
val instanceField = getFieldForStaticLambdaInstance(constructor.constructedClass)
|
val instanceField = getFieldForStaticCallableReferenceInstance(constructor.constructedClass)
|
||||||
return IrGetFieldImpl(expression.startOffset, expression.endOffset, instanceField.symbol, expression.type)
|
return IrGetFieldImpl(expression.startOffset, expression.endOffset, instanceField.symbol, expression.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recognize callable references with no value or type arguments. The only type arguments in Kotlin stem from usages of
|
// Recognize callable references with no value or type arguments. The only type arguments in Kotlin stem from usages of
|
||||||
// reified type parameters, which we unfortunately don't record as parameters so we have to check the body of the class.
|
// reified type parameters, which we unfortunately don't record as parameters so we have to check the body of the class.
|
||||||
private val IrClass.isSyntheticSingleton: Boolean
|
private val IrClass.isSyntheticSingleton: Boolean
|
||||||
get() = (origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL || origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL)
|
get() = (origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL
|
||||||
|
|| origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
|
||||||
|
|| origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE)
|
||||||
&& primaryConstructor!!.valueParameters.isEmpty()
|
&& primaryConstructor!!.valueParameters.isEmpty()
|
||||||
&& !containsReifiedTypeParameters
|
&& !containsReifiedTypeParameters
|
||||||
|
|
||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
class A {
|
class A {
|
||||||
fun foo() {}
|
fun foo() {}
|
||||||
val bar = 42
|
val bar = 42
|
||||||
|
|||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
@kotlin.Metadata
|
||||||
|
public final class A {
|
||||||
|
// source: 'noReceiverInCallableReferenceClasses.kt'
|
||||||
|
private final field bar: int
|
||||||
|
public method <init>(): void
|
||||||
|
public final method foo(): void
|
||||||
|
public final method getBar(): int
|
||||||
|
}
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
synthetic final class NoReceiverInCallableReferenceClassesKt$A_bar$1 {
|
||||||
|
// source: 'noReceiverInCallableReferenceClasses.kt'
|
||||||
|
enclosing method NoReceiverInCallableReferenceClassesKt.<clinit>()V
|
||||||
|
public final static field INSTANCE: NoReceiverInCallableReferenceClassesKt$A_bar$1
|
||||||
|
inner (anonymous) class NoReceiverInCallableReferenceClassesKt$A_bar$1
|
||||||
|
static method <clinit>(): void
|
||||||
|
method <init>(): void
|
||||||
|
public @org.jetbrains.annotations.Nullable method get(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object
|
||||||
|
}
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
synthetic final class NoReceiverInCallableReferenceClassesKt$A_foo$1 {
|
||||||
|
// source: 'noReceiverInCallableReferenceClasses.kt'
|
||||||
|
enclosing method NoReceiverInCallableReferenceClassesKt.<clinit>()V
|
||||||
|
public final static field INSTANCE: NoReceiverInCallableReferenceClassesKt$A_foo$1
|
||||||
|
inner (anonymous) class NoReceiverInCallableReferenceClassesKt$A_foo$1
|
||||||
|
static method <clinit>(): void
|
||||||
|
method <init>(): void
|
||||||
|
public final method invoke(@org.jetbrains.annotations.NotNull p0: A): void
|
||||||
|
public synthetic bridge method invoke(p0: java.lang.Object): java.lang.Object
|
||||||
|
}
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
synthetic final class NoReceiverInCallableReferenceClassesKt$aBar$1 {
|
||||||
|
// source: 'noReceiverInCallableReferenceClasses.kt'
|
||||||
|
enclosing method NoReceiverInCallableReferenceClassesKt.<clinit>()V
|
||||||
|
inner (anonymous) class NoReceiverInCallableReferenceClassesKt$aBar$1
|
||||||
|
method <init>(p0: java.lang.Object): void
|
||||||
|
public @org.jetbrains.annotations.Nullable method get(): java.lang.Object
|
||||||
|
}
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
synthetic final class NoReceiverInCallableReferenceClassesKt$aFoo$1 {
|
||||||
|
// source: 'noReceiverInCallableReferenceClasses.kt'
|
||||||
|
enclosing method NoReceiverInCallableReferenceClassesKt.<clinit>()V
|
||||||
|
inner (anonymous) class NoReceiverInCallableReferenceClassesKt$aFoo$1
|
||||||
|
method <init>(p0: A): void
|
||||||
|
public synthetic bridge method invoke(): java.lang.Object
|
||||||
|
public final method invoke(): void
|
||||||
|
}
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
public final class NoReceiverInCallableReferenceClassesKt {
|
||||||
|
// source: 'noReceiverInCallableReferenceClasses.kt'
|
||||||
|
private final static @org.jetbrains.annotations.NotNull field A_bar: kotlin.reflect.KProperty1
|
||||||
|
private final static @org.jetbrains.annotations.NotNull field A_foo: kotlin.reflect.KFunction
|
||||||
|
private final static @org.jetbrains.annotations.NotNull field aBar: kotlin.reflect.KProperty0
|
||||||
|
private final static @org.jetbrains.annotations.NotNull field aFoo: kotlin.reflect.KFunction
|
||||||
|
inner (anonymous) class NoReceiverInCallableReferenceClassesKt$A_bar$1
|
||||||
|
inner (anonymous) class NoReceiverInCallableReferenceClassesKt$A_foo$1
|
||||||
|
inner (anonymous) class NoReceiverInCallableReferenceClassesKt$aBar$1
|
||||||
|
inner (anonymous) class NoReceiverInCallableReferenceClassesKt$aFoo$1
|
||||||
|
static method <clinit>(): void
|
||||||
|
public final static @org.jetbrains.annotations.NotNull method getABar(): kotlin.reflect.KProperty0
|
||||||
|
public final static @org.jetbrains.annotations.NotNull method getAFoo(): kotlin.reflect.KFunction
|
||||||
|
public final static @org.jetbrains.annotations.NotNull method getA_bar(): kotlin.reflect.KProperty1
|
||||||
|
public final static @org.jetbrains.annotations.NotNull method getA_foo(): kotlin.reflect.KFunction
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// FILE: test.kt
|
||||||
|
class A(val value: String)
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val ref = A::value
|
||||||
|
return ref(A("OK"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that non-bound callable references are generated as singletons
|
||||||
|
// 1 GETSTATIC TestKt\$box\$ref\$1.INSTANCE
|
||||||
@@ -769,6 +769,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/callableReference/boundPropertyReferenceInInline.kt");
|
runTest("compiler/testData/codegen/bytecodeText/callableReference/boundPropertyReferenceInInline.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt36975.kt")
|
||||||
|
public void testKt36975() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/callableReference/kt36975.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nameIntrinsicWithImplicitThis.kt")
|
@TestMetadata("nameIntrinsicWithImplicitThis.kt")
|
||||||
public void testNameIntrinsicWithImplicitThis() throws Exception {
|
public void testNameIntrinsicWithImplicitThis() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/callableReference/nameIntrinsicWithImplicitThis.kt");
|
runTest("compiler/testData/codegen/bytecodeText/callableReference/nameIntrinsicWithImplicitThis.kt");
|
||||||
|
|||||||
+5
@@ -769,6 +769,11 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/callableReference/boundPropertyReferenceInInline.kt");
|
runTest("compiler/testData/codegen/bytecodeText/callableReference/boundPropertyReferenceInInline.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt36975.kt")
|
||||||
|
public void testKt36975() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/callableReference/kt36975.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nameIntrinsicWithImplicitThis.kt")
|
@TestMetadata("nameIntrinsicWithImplicitThis.kt")
|
||||||
public void testNameIntrinsicWithImplicitThis() throws Exception {
|
public void testNameIntrinsicWithImplicitThis() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/callableReference/nameIntrinsicWithImplicitThis.kt");
|
runTest("compiler/testData/codegen/bytecodeText/callableReference/nameIntrinsicWithImplicitThis.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user