[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 =
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
override fun getPackageFqName(clazz: IrClass) =
@@ -502,7 +502,7 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT
}
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 ->
@@ -19,33 +19,45 @@ import org.jetbrains.kotlin.backend.konan.ir.isUnit
import org.jetbrains.kotlin.backend.konan.isObjCClass
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addField
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.name.Name
internal fun Context.getObjectClassInstanceFunction(clazz: IrClass) = mapping.objectInstanceGetter.getOrPut(clazz) {
when {
clazz.isUnit() -> ir.symbols.theUnitInstance.owner
clazz.isCompanion -> {
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
returnType = clazz.defaultType
}.apply {
parent = clazz.parent
addGetter {
startOffset = clazz.startOffset
endOffset = clazz.endOffset
returnType = clazz.defaultType
}
}
property.getter!!
}
else -> {
irFactory.buildFun {
val property = irFactory.buildProperty {
startOffset = clazz.startOffset
endOffset = clazz.endOffset
name = "instance".synthesizedName
returnType = clazz.defaultType
}.apply {
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()) {
processObjectClass(
declaration,
declaration,
instanceFieldName
declaration
)
}
declaration.declarations.singleOrNull { (it as? IrClass)?.isCompanion == true }?.let {
processObjectClass(
it as IrClass,
declaration,
companionFieldName
declaration
)
}
return declaration
@@ -94,21 +104,20 @@ internal class ObjectClassLowering(val context: Context) : FileLoweringPass {
fun processObjectClass(
declaration: IrClass,
classToAdd: IrClass,
fieldName: Name
classToAdd: IrClass
) {
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 }
require(primaryConstructor.valueParameters.isEmpty())
val instanceField = classToAdd.addField {
name = fieldName
property.addBackingField {
isFinal = true
isStatic = true
type = declaration.defaultType
visibility = DescriptorVisibilities.PRIVATE
}.also { field ->
val primaryConstructor = declaration.constructors.single { it.isPrimary }
require(primaryConstructor.valueParameters.isEmpty())
val builder = context.createIrBuilder(field.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET)
val initializer = if (declaration.isCompanion && classToAdd.isObjCClass()) {
builder.irGetObjCClassCompanion(declaration)
@@ -131,15 +140,14 @@ internal class ObjectClassLowering(val context: Context) : FileLoweringPass {
}
}
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)
} else if (declaration.annotations.hasAnnotation(KonanFqNames.sharedImmutable) || context.memoryModel != MemoryModel.EXPERIMENTAL){
field.annotations += buildSimpleAnnotation(context.irBuiltIns, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.ir.symbols.sharedImmutable.owner)
}
}
if (declaration.annotations.hasAnnotation(KonanFqNames.threadLocal)) {
property.annotations += buildSimpleAnnotation(context.irBuiltIns, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.ir.symbols.threadLocal.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 {
val value = irGetField(null, instanceField)
+irReturn(value)
+irReturn(irGetField(null, property.backingField!!))
}
}
@@ -162,11 +170,6 @@ internal class ObjectClassLowering(val context: Context) : FileLoweringPass {
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
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')
}
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") {
annotatedSource = project.file('filecheck/kt49847_simple_function_reference.kt')
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()
}