[K/N] Optimize code generation for function references
In before, each function reference created 5 functions needed only for equals/hashcode properties. It contributed arround 3.5% of code size on several sample projects. This is now replaced by passing constant object to base class constructor.
This commit is contained in:
committed by
Space Team
parent
7d8a1e9c1c
commit
00c900cb49
+1
@@ -377,6 +377,7 @@ internal abstract class KonanSymbols(
|
||||
private fun reflectionClass(name: String) = irBuiltIns.findClass(Name.identifier(name), StandardNames.KOTLIN_REFLECT_FQ_NAME)!!
|
||||
|
||||
val kFunctionImpl = internalClass("KFunctionImpl")
|
||||
val kFunctionDescription = internalClass("KFunctionDescription")
|
||||
val kSuspendFunctionImpl = internalClass("KSuspendFunctionImpl")
|
||||
|
||||
val kMutableProperty0 = reflectionClass("KMutableProperty0")
|
||||
|
||||
+31
-15
@@ -1895,27 +1895,43 @@ internal class CodeGeneratorVisitor(
|
||||
intrinsicGenerator.evaluateConstantConstructorFields(value, value.valueArguments.map { evaluateConstantValue(it) })
|
||||
} else {
|
||||
val fields = context.getLayoutBuilder(constructedClass).getFields(llvm)
|
||||
val valueParameters = value.constructor.owner.valueParameters.associateBy { it.name.toString() }
|
||||
val constructor = value.constructor.owner
|
||||
val valueParameters = constructor.valueParameters.associateBy { it.name.toString() }
|
||||
// support of initilaization of object in following case:
|
||||
// open class Base(val field: ...)
|
||||
// Child(val otherField: ...) : Base(constantValue)
|
||||
//
|
||||
// Child(constantValue) could be initialized constantly. This is required for function references.
|
||||
val delegatedCallConstants = constructor.body?.statements
|
||||
?.filterIsInstance<IrDelegatingConstructorCall>()
|
||||
?.singleOrNull()
|
||||
?.getArgumentsWithIr()
|
||||
?.filter { it.second is IrConstantValue }
|
||||
?.associate { it.first.name.toString() to it.second }
|
||||
.orEmpty()
|
||||
fields.map { field ->
|
||||
if (field.isConst) {
|
||||
val init = field.irField!!.initializer?.expression
|
||||
require(field.name !in valueParameters) {
|
||||
"Constant field ${field.name} of class ${constructedClass.name} shouldn't be a constructor parameter"
|
||||
}
|
||||
when (init) {
|
||||
is IrConst<*> -> evaluateConst(init)
|
||||
is IrConstantValue -> evaluateConstantValue(init)
|
||||
null -> error("Constant field ${field.name} of class ${constructedClass.name} should have initializer")
|
||||
else -> error("Unexpected constant initializer type: ${init::class}")
|
||||
val init = if (field.isConst) {
|
||||
field.irField!!.initializer?.expression.also {
|
||||
require(field.name !in valueParameters) {
|
||||
"Constant field ${field.name} of class ${constructedClass.name} shouldn't be a constructor parameter"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val index = valueParameters[field.name]?.index
|
||||
?: error("Bad statically initialized object: field ${field.name} value not set in ${constructedClass.name}")
|
||||
evaluateConstantValue(value.valueArguments[index])
|
||||
if (index != null)
|
||||
value.valueArguments[index]
|
||||
else
|
||||
delegatedCallConstants[field.name]
|
||||
}
|
||||
when (init) {
|
||||
is IrConst<*> -> evaluateConst(init)
|
||||
is IrConstantValue -> evaluateConstantValue(init)
|
||||
null -> error("Bad statically initialized object: field ${field.name} value not set in ${constructedClass.name}")
|
||||
else -> error("Unexpected constant initializer type: ${init::class}")
|
||||
}
|
||||
}.also {
|
||||
require(it.size == value.valueArguments.size + fields.count { it.isConst }) {
|
||||
"Bad statically initialized object of class ${constructedClass.name}: too many fields"
|
||||
require(it.size == value.valueArguments.size + fields.count { it.isConst } + delegatedCallConstants.size) {
|
||||
"Bad statically initialized object of class ${constructedClass.name}: not all arguments are used"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-11
@@ -310,6 +310,7 @@ internal class FunctionReferenceLowering(val generationState: NativeGenerationSt
|
||||
}
|
||||
|
||||
private val kFunctionImplSymbol = symbols.kFunctionImpl
|
||||
private val kFunctionDescriptionSymbol = symbols.kFunctionDescription
|
||||
private val kFunctionImplConstructorSymbol = kFunctionImplSymbol.constructors.single()
|
||||
private val kSuspendFunctionImplSymbol = symbols.kSuspendFunctionImpl
|
||||
private val kSuspendFunctionImplConstructorSymbol = kSuspendFunctionImplSymbol.constructors.single()
|
||||
@@ -384,15 +385,6 @@ internal class FunctionReferenceLowering(val generationState: NativeGenerationSt
|
||||
addOverrideInner(name) { _ -> value() }
|
||||
}
|
||||
|
||||
val kTypeGenerator = KTypeGenerator(context, irFile, functionReference)
|
||||
addOverride("computeReturnType") { with(kTypeGenerator) { irKType(referencedFunction.returnType) } }
|
||||
addOverride("computeArity") { irInt(unboundFunctionParameters.size + if (functionReferenceTarget.isSuspend) 1 else 0) }
|
||||
addOverride("computeFlags") { irInt(getFlags()) }
|
||||
val name = ((functionReferenceTarget as? IrSimpleFunction)?.attributeOwnerId as? IrSimpleFunction)?.name
|
||||
?: functionReferenceTarget.name
|
||||
addOverride("computeName") { irString(name.asString()) }
|
||||
addOverride("computeFqName") { irString(getFqName()) }
|
||||
|
||||
|
||||
listOfNotNull(
|
||||
functionReference.symbol.owner.dispatchReceiverParameter,
|
||||
@@ -435,11 +427,15 @@ internal class FunctionReferenceLowering(val generationState: NativeGenerationSt
|
||||
|
||||
body = context.createIrBuilder(symbol, startOffset, endOffset).irBlockBody {
|
||||
val superConstructor = when {
|
||||
this@FunctionReferenceBuilder.isSuspend -> kSuspendFunctionImplConstructorSymbol.owner
|
||||
isLambda -> irBuiltIns.anyClass.owner.constructors.single()
|
||||
this@FunctionReferenceBuilder.isSuspend -> kSuspendFunctionImplConstructorSymbol.owner
|
||||
else -> kFunctionImplConstructorSymbol.owner
|
||||
}
|
||||
+irDelegatingConstructorCall(superConstructor)
|
||||
+irDelegatingConstructorCall(superConstructor).apply {
|
||||
if (!isLambda) {
|
||||
putValueArgument(0, getDescription())
|
||||
}
|
||||
}
|
||||
+IrInstanceInitializerCallImpl(startOffset, endOffset, functionReferenceClass.symbol, irBuiltIns.unitType)
|
||||
// Save all arguments to fields.
|
||||
boundFunctionParameters.forEachIndexed { index, parameter ->
|
||||
@@ -465,6 +461,21 @@ internal class FunctionReferenceLowering(val generationState: NativeGenerationSt
|
||||
return BuiltFunctionReference(clazz, expression)
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.getDescription() : IrConstantValue {
|
||||
val kTypeGenerator = KTypeGenerator(this@FunctionReferenceBuilder.context, irFile, functionReference)
|
||||
|
||||
return irConstantObject(
|
||||
kFunctionDescriptionSymbol.owner,
|
||||
mapOf(
|
||||
"flags" to irConstantPrimitive(irInt(getFlags())),
|
||||
"arity" to irConstantPrimitive(irInt(getArity())),
|
||||
"fqName" to irConstantPrimitive(irString(getFqName())),
|
||||
"name" to irConstantPrimitive(irString(getName().asString())),
|
||||
"returnType" to with(kTypeGenerator) { irKType(referencedFunction.returnType) }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// this value is used only for hashCode and equals, to distinguish different wrappers on same functions
|
||||
private fun getFlags() =
|
||||
listOfNotNull(
|
||||
@@ -481,6 +492,12 @@ internal class FunctionReferenceLowering(val generationState: NativeGenerationSt
|
||||
else
|
||||
functionReferenceTarget.computeFullName()
|
||||
|
||||
private fun getName() =
|
||||
((functionReferenceTarget as? IrSimpleFunction)?.attributeOwnerId as? IrSimpleFunction)?.name
|
||||
?: functionReferenceTarget.name
|
||||
|
||||
private fun getArity() = unboundFunctionParameters.size + if (functionReferenceTarget.isSuspend) 1 else 0
|
||||
|
||||
private fun isFunInterfaceConstructorAdapter() =
|
||||
referencedFunction.origin == IrDeclarationOrigin.ADAPTER_FOR_FUN_INTERFACE_CONSTRUCTOR
|
||||
|
||||
|
||||
@@ -8,20 +8,23 @@ package kotlin.native.internal
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KType
|
||||
|
||||
@FixmeReflection
|
||||
internal abstract class KFunctionImpl<out R>: KFunction<R> {
|
||||
final override val returnType get() = computeReturnType()
|
||||
val flags get() = computeFlags()
|
||||
val arity get() = computeArity()
|
||||
val fqName get() = computeFqName()
|
||||
val receiver get() = computeReceiver()
|
||||
final override val name get() = computeName()
|
||||
internal class KFunctionDescription(
|
||||
val flags: Int,
|
||||
val arity: Int,
|
||||
val fqName: String,
|
||||
val name: String,
|
||||
val returnType: KType
|
||||
)
|
||||
|
||||
@FixmeReflection
|
||||
internal abstract class KFunctionImpl<out R>(val description: KFunctionDescription): KFunction<R> {
|
||||
final override val returnType get() = description.returnType
|
||||
val flags get() = description.flags
|
||||
val arity get() = description.arity
|
||||
val fqName get() = description.fqName
|
||||
val receiver get() = computeReceiver()
|
||||
final override val name get() = description.name
|
||||
|
||||
abstract fun computeReturnType() : KType
|
||||
abstract fun computeFlags() : Int
|
||||
abstract fun computeArity() : Int
|
||||
abstract fun computeFqName() : String
|
||||
abstract fun computeName() : String
|
||||
open fun computeReceiver(): Any? = null
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
|
||||
+1
-1
@@ -10,6 +10,6 @@ import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@FixmeReflection
|
||||
internal abstract class KSuspendFunctionImpl<out R>: KFunctionImpl<R>() {
|
||||
internal abstract class KSuspendFunctionImpl<out R>(description: KFunctionDescription): KFunctionImpl<R>(description) {
|
||||
override fun toString() = "suspend function $name"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user