[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)!!
|
private fun reflectionClass(name: String) = irBuiltIns.findClass(Name.identifier(name), StandardNames.KOTLIN_REFLECT_FQ_NAME)!!
|
||||||
|
|
||||||
val kFunctionImpl = internalClass("KFunctionImpl")
|
val kFunctionImpl = internalClass("KFunctionImpl")
|
||||||
|
val kFunctionDescription = internalClass("KFunctionDescription")
|
||||||
val kSuspendFunctionImpl = internalClass("KSuspendFunctionImpl")
|
val kSuspendFunctionImpl = internalClass("KSuspendFunctionImpl")
|
||||||
|
|
||||||
val kMutableProperty0 = reflectionClass("KMutableProperty0")
|
val kMutableProperty0 = reflectionClass("KMutableProperty0")
|
||||||
|
|||||||
+31
-15
@@ -1895,27 +1895,43 @@ internal class CodeGeneratorVisitor(
|
|||||||
intrinsicGenerator.evaluateConstantConstructorFields(value, value.valueArguments.map { evaluateConstantValue(it) })
|
intrinsicGenerator.evaluateConstantConstructorFields(value, value.valueArguments.map { evaluateConstantValue(it) })
|
||||||
} else {
|
} else {
|
||||||
val fields = context.getLayoutBuilder(constructedClass).getFields(llvm)
|
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 ->
|
fields.map { field ->
|
||||||
if (field.isConst) {
|
val init = if (field.isConst) {
|
||||||
val init = field.irField!!.initializer?.expression
|
field.irField!!.initializer?.expression.also {
|
||||||
require(field.name !in valueParameters) {
|
require(field.name !in valueParameters) {
|
||||||
"Constant field ${field.name} of class ${constructedClass.name} shouldn't be a constructor parameter"
|
"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}")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val index = valueParameters[field.name]?.index
|
val index = valueParameters[field.name]?.index
|
||||||
?: error("Bad statically initialized object: field ${field.name} value not set in ${constructedClass.name}")
|
if (index != null)
|
||||||
evaluateConstantValue(value.valueArguments[index])
|
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 {
|
}.also {
|
||||||
require(it.size == value.valueArguments.size + fields.count { it.isConst }) {
|
require(it.size == value.valueArguments.size + fields.count { it.isConst } + delegatedCallConstants.size) {
|
||||||
"Bad statically initialized object of class ${constructedClass.name}: too many fields"
|
"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 kFunctionImplSymbol = symbols.kFunctionImpl
|
||||||
|
private val kFunctionDescriptionSymbol = symbols.kFunctionDescription
|
||||||
private val kFunctionImplConstructorSymbol = kFunctionImplSymbol.constructors.single()
|
private val kFunctionImplConstructorSymbol = kFunctionImplSymbol.constructors.single()
|
||||||
private val kSuspendFunctionImplSymbol = symbols.kSuspendFunctionImpl
|
private val kSuspendFunctionImplSymbol = symbols.kSuspendFunctionImpl
|
||||||
private val kSuspendFunctionImplConstructorSymbol = kSuspendFunctionImplSymbol.constructors.single()
|
private val kSuspendFunctionImplConstructorSymbol = kSuspendFunctionImplSymbol.constructors.single()
|
||||||
@@ -384,15 +385,6 @@ internal class FunctionReferenceLowering(val generationState: NativeGenerationSt
|
|||||||
addOverrideInner(name) { _ -> value() }
|
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(
|
listOfNotNull(
|
||||||
functionReference.symbol.owner.dispatchReceiverParameter,
|
functionReference.symbol.owner.dispatchReceiverParameter,
|
||||||
@@ -435,11 +427,15 @@ internal class FunctionReferenceLowering(val generationState: NativeGenerationSt
|
|||||||
|
|
||||||
body = context.createIrBuilder(symbol, startOffset, endOffset).irBlockBody {
|
body = context.createIrBuilder(symbol, startOffset, endOffset).irBlockBody {
|
||||||
val superConstructor = when {
|
val superConstructor = when {
|
||||||
this@FunctionReferenceBuilder.isSuspend -> kSuspendFunctionImplConstructorSymbol.owner
|
|
||||||
isLambda -> irBuiltIns.anyClass.owner.constructors.single()
|
isLambda -> irBuiltIns.anyClass.owner.constructors.single()
|
||||||
|
this@FunctionReferenceBuilder.isSuspend -> kSuspendFunctionImplConstructorSymbol.owner
|
||||||
else -> kFunctionImplConstructorSymbol.owner
|
else -> kFunctionImplConstructorSymbol.owner
|
||||||
}
|
}
|
||||||
+irDelegatingConstructorCall(superConstructor)
|
+irDelegatingConstructorCall(superConstructor).apply {
|
||||||
|
if (!isLambda) {
|
||||||
|
putValueArgument(0, getDescription())
|
||||||
|
}
|
||||||
|
}
|
||||||
+IrInstanceInitializerCallImpl(startOffset, endOffset, functionReferenceClass.symbol, irBuiltIns.unitType)
|
+IrInstanceInitializerCallImpl(startOffset, endOffset, functionReferenceClass.symbol, irBuiltIns.unitType)
|
||||||
// Save all arguments to fields.
|
// Save all arguments to fields.
|
||||||
boundFunctionParameters.forEachIndexed { index, parameter ->
|
boundFunctionParameters.forEachIndexed { index, parameter ->
|
||||||
@@ -465,6 +461,21 @@ internal class FunctionReferenceLowering(val generationState: NativeGenerationSt
|
|||||||
return BuiltFunctionReference(clazz, expression)
|
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
|
// this value is used only for hashCode and equals, to distinguish different wrappers on same functions
|
||||||
private fun getFlags() =
|
private fun getFlags() =
|
||||||
listOfNotNull(
|
listOfNotNull(
|
||||||
@@ -481,6 +492,12 @@ internal class FunctionReferenceLowering(val generationState: NativeGenerationSt
|
|||||||
else
|
else
|
||||||
functionReferenceTarget.computeFullName()
|
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() =
|
private fun isFunInterfaceConstructorAdapter() =
|
||||||
referencedFunction.origin == IrDeclarationOrigin.ADAPTER_FOR_FUN_INTERFACE_CONSTRUCTOR
|
referencedFunction.origin == IrDeclarationOrigin.ADAPTER_FOR_FUN_INTERFACE_CONSTRUCTOR
|
||||||
|
|
||||||
|
|||||||
@@ -8,20 +8,23 @@ package kotlin.native.internal
|
|||||||
import kotlin.reflect.KFunction
|
import kotlin.reflect.KFunction
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
|
|
||||||
@FixmeReflection
|
internal class KFunctionDescription(
|
||||||
internal abstract class KFunctionImpl<out R>: KFunction<R> {
|
val flags: Int,
|
||||||
final override val returnType get() = computeReturnType()
|
val arity: Int,
|
||||||
val flags get() = computeFlags()
|
val fqName: String,
|
||||||
val arity get() = computeArity()
|
val name: String,
|
||||||
val fqName get() = computeFqName()
|
val returnType: KType
|
||||||
val receiver get() = computeReceiver()
|
)
|
||||||
final override val name get() = computeName()
|
|
||||||
|
@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
|
open fun computeReceiver(): Any? = null
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
|
|||||||
+1
-1
@@ -10,6 +10,6 @@ import kotlin.reflect.KFunction
|
|||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@FixmeReflection
|
@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"
|
override fun toString() = "suspend function $name"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user