[K/N] Make instance a property not a field

This enables inlining machinery, and allows to avoid frame in simple
cases.
This commit is contained in:
Pavel Kunyavskiy
2022-11-16 17:25:30 +01:00
committed by Space Team
parent d3adfec2fb
commit 68f78f1e5f
5 changed files with 77 additions and 32 deletions
@@ -307,7 +307,7 @@ internal object IrTypeInlineClassesSupport : InlineClassesSupport<IrClass, IrTyp
override fun getInlinedClassUnderlyingType(clazz: IrClass): IrType = override fun getInlinedClassUnderlyingType(clazz: IrClass): IrType =
clazz.constructors.firstOrNull { it.isPrimary }?.valueParameters?.single()?.type clazz.constructors.firstOrNull { it.isPrimary }?.valueParameters?.single()?.type
?: clazz.declarations.filterIsInstance<IrProperty>().atMostOne { it.backingField != null }?.backingField?.type ?: clazz.declarations.filterIsInstance<IrProperty>().atMostOne { it.backingField?.takeUnless { it.isStatic } != null }?.backingField?.type
?: clazz.inlineClassRepresentation!!.underlyingType ?: clazz.inlineClassRepresentation!!.underlyingType
override fun getPackageFqName(clazz: IrClass) = override fun getPackageFqName(clazz: IrClass) =
@@ -502,7 +502,7 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
} }
private fun getInlineClassBackingField(irClass: IrClass): IrField = private fun getInlineClassBackingField(irClass: IrClass): IrField =
irClass.declarations.filterIsInstance<IrProperty>().mapNotNull { it.backingField }.single() irClass.declarations.filterIsInstance<IrProperty>().mapNotNull { it.backingField?.takeUnless { it.isStatic } }.single()
} }
private val Context.getLoweredInlineClassConstructor: (IrConstructor) -> IrSimpleFunction by Context.lazyMapMember { irConstructor -> private val Context.getLoweredInlineClassConstructor: (IrConstructor) -> IrSimpleFunction by Context.lazyMapMember { irConstructor ->
@@ -19,33 +19,45 @@ import org.jetbrains.kotlin.backend.konan.ir.isUnit
import org.jetbrains.kotlin.backend.konan.isObjCClass import org.jetbrains.kotlin.backend.konan.isObjCClass
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addField import org.jetbrains.kotlin.ir.builders.declarations.*
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.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.irCall import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.name.Name
internal fun Context.getObjectClassInstanceFunction(clazz: IrClass) = mapping.objectInstanceGetter.getOrPut(clazz) { internal fun Context.getObjectClassInstanceFunction(clazz: IrClass) = mapping.objectInstanceGetter.getOrPut(clazz) {
when { when {
clazz.isUnit() -> ir.symbols.theUnitInstance.owner clazz.isUnit() -> ir.symbols.theUnitInstance.owner
clazz.isCompanion -> { clazz.isCompanion -> {
require((clazz.parent as? IrClass)?.isExternalObjCClass() != true) { "External objc Classes can't be used this way"} require((clazz.parent as? IrClass)?.isExternalObjCClass() != true) { "External objc Classes can't be used this way"}
irFactory.buildFun { val property = irFactory.buildProperty {
startOffset = clazz.startOffset
endOffset = clazz.endOffset
name = "companion".synthesizedName name = "companion".synthesizedName
returnType = clazz.defaultType
}.apply { }.apply {
parent = clazz.parent parent = clazz.parent
addGetter {
startOffset = clazz.startOffset
endOffset = clazz.endOffset
returnType = clazz.defaultType
}
} }
property.getter!!
} }
else -> { else -> {
irFactory.buildFun { val property = irFactory.buildProperty {
startOffset = clazz.startOffset
endOffset = clazz.endOffset
name = "instance".synthesizedName name = "instance".synthesizedName
returnType = clazz.defaultType
}.apply { }.apply {
parent = clazz parent = clazz
addGetter {
startOffset = clazz.startOffset
endOffset = clazz.endOffset
returnType = clazz.defaultType
}
} }
property.getter!!
} }
} }
} }
@@ -60,15 +72,13 @@ internal class ObjectClassLowering(val context: Context) : FileLoweringPass {
if (declaration.isObject && !declaration.isCompanion && !declaration.isUnit()) { if (declaration.isObject && !declaration.isCompanion && !declaration.isUnit()) {
processObjectClass( processObjectClass(
declaration, declaration,
declaration, declaration
instanceFieldName
) )
} }
declaration.declarations.singleOrNull { (it as? IrClass)?.isCompanion == true }?.let { declaration.declarations.singleOrNull { (it as? IrClass)?.isCompanion == true }?.let {
processObjectClass( processObjectClass(
it as IrClass, it as IrClass,
declaration, declaration
companionFieldName
) )
} }
return declaration return declaration
@@ -94,21 +104,20 @@ internal class ObjectClassLowering(val context: Context) : FileLoweringPass {
fun processObjectClass( fun processObjectClass(
declaration: IrClass, declaration: IrClass,
classToAdd: IrClass, classToAdd: IrClass
fieldName: Name
) { ) {
val function = context.getObjectClassInstanceFunction(declaration) val function = context.getObjectClassInstanceFunction(declaration)
classToAdd.declarations.add(function) val property = function.correspondingPropertySymbol!!.owner
classToAdd.declarations.add(property)
val primaryConstructor = declaration.constructors.single { it.isPrimary } property.addBackingField {
require(primaryConstructor.valueParameters.isEmpty())
val instanceField = classToAdd.addField {
name = fieldName
isFinal = true isFinal = true
isStatic = true isStatic = true
type = declaration.defaultType type = declaration.defaultType
visibility = DescriptorVisibilities.PRIVATE visibility = DescriptorVisibilities.PRIVATE
}.also { field -> }.also { field ->
val primaryConstructor = declaration.constructors.single { it.isPrimary }
require(primaryConstructor.valueParameters.isEmpty())
val builder = context.createIrBuilder(field.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET) val builder = context.createIrBuilder(field.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET)
val initializer = if (declaration.isCompanion && classToAdd.isObjCClass()) { val initializer = if (declaration.isCompanion && classToAdd.isObjCClass()) {
builder.irGetObjCClassCompanion(declaration) builder.irGetObjCClassCompanion(declaration)
@@ -131,15 +140,14 @@ internal class ObjectClassLowering(val context: Context) : FileLoweringPass {
} }
} }
field.initializer = builder.irExprBody(initializer) field.initializer = builder.irExprBody(initializer)
if (declaration.annotations.hasAnnotation(KonanFqNames.threadLocal)) { }
field.annotations += buildSimpleAnnotation(context.irBuiltIns, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.ir.symbols.threadLocal.owner) if (declaration.annotations.hasAnnotation(KonanFqNames.threadLocal)) {
} else if (declaration.annotations.hasAnnotation(KonanFqNames.sharedImmutable) || context.memoryModel != MemoryModel.EXPERIMENTAL){ property.annotations += buildSimpleAnnotation(context.irBuiltIns, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.ir.symbols.threadLocal.owner)
field.annotations += buildSimpleAnnotation(context.irBuiltIns, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.ir.symbols.sharedImmutable.owner) } else if (declaration.annotations.hasAnnotation(KonanFqNames.sharedImmutable) || context.memoryModel != MemoryModel.EXPERIMENTAL){
} property.annotations += buildSimpleAnnotation(context.irBuiltIns, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.ir.symbols.sharedImmutable.owner)
} }
function.body = context.createIrBuilder(function.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody { function.body = context.createIrBuilder(function.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
val value = irGetField(null, instanceField) +irReturn(irGetField(null, property.backingField!!))
+irReturn(value)
} }
} }
@@ -162,11 +170,6 @@ internal class ObjectClassLowering(val context: Context) : FileLoweringPass {
return fields.all { it.isConst } && this.constructors.all { it.isAutogeneratedSimpleConstructor() } return fields.all { it.isConst } && this.constructors.all { it.isAutogeneratedSimpleConstructor() }
} }
companion object {
val companionFieldName = "companionField".synthesizedName
val instanceFieldName = "instanceField".synthesizedName
}
// This is a hack to avoid early freezing. Should be removed when freezing is removed // This is a hack to avoid early freezing. Should be removed when freezing is removed
object IrStatementOriginFieldPreInit : IrStatementOriginImpl("FIELD_PRE_INIT") object IrStatementOriginFieldPreInit : IrStatementOriginImpl("FIELD_PRE_INIT")
@@ -6262,6 +6262,11 @@ fileCheckTest("filecheck_replace_invoke_with_call") {
annotatedSource = project.file('filecheck/replace_invoke_with_call.kt') annotatedSource = project.file('filecheck/replace_invoke_with_call.kt')
} }
fileCheckTest("filecheck_no_frame_on_constant_object_access") {
annotatedSource = project.file('filecheck/no_frame_on_constant_object_access.kt')
enabled = project.globalTestArgs.contains('-opt')
}
fileCheckTest("filecheck_kt49847_simple_function_reference") { fileCheckTest("filecheck_kt49847_simple_function_reference") {
annotatedSource = project.file('filecheck/kt49847_simple_function_reference.kt') annotatedSource = project.file('filecheck/kt49847_simple_function_reference.kt')
enabled = project.globalTestArgs.contains('-opt') enabled = project.globalTestArgs.contains('-opt')
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
object A {
const val x = 5
}
class B(val z:Int) {
companion object {
const val y = 7
}
}
object C {
val x = listOf(1, 2, 3)
}
// CHECK-LABEL: define i32 @"kfun:#f(){}kotlin.Int"()
// CHECK-NOT: EnterFrame
fun f() = A.x + B.y
// CHECK: {{^}}epilogue:
// test that assumption on how EnterFrame looks like is not broken
// CHECK-LABEL: define void @"kfun:#g(){}"()
// CHECK: EnterFrame
fun g() {
val x = C.x
}
// CHECK: {{^}}epilogue:
fun main() {
f()
g()
}