IC mangling: Generalize mangling algorithm between two backends

This commit is contained in:
Ilmir Usmanov
2020-11-13 23:42:31 +01:00
parent b33774e5f2
commit bc1b6fef1f
3 changed files with 130 additions and 128 deletions
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.codegen.state
import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
@@ -23,6 +24,62 @@ const val NOT_INLINE_CLASS_PARAMETER_PLACEHOLDER = "_"
private fun FunctionDescriptor.isFunctionFromStdlib(): Boolean = fqNameSafe.startsWith(Name.identifier("kotlin")) private fun FunctionDescriptor.isFunctionFromStdlib(): Boolean = fqNameSafe.startsWith(Name.identifier("kotlin"))
class InfoForMangling(
val fqName: FqNameUnsafe,
val isInline: Boolean,
val isNullable: Boolean
)
fun collectFunctionSignatureForManglingSuffix(
useOldManglingRules: Boolean,
requiresFunctionNameManglingForParameterTypes: Boolean,
fqNamesForMangling: List<InfoForMangling?>,
returnTypeInfo: InfoForMangling?,
): String? {
fun getSignatureElementForMangling(info: InfoForMangling?): String = buildString {
if (info == null) return ""
if (useOldManglingRules || info.isInline) {
append('L')
append(info.fqName)
if (info.isNullable) append('?')
append(';')
} else {
append(NOT_INLINE_CLASS_PARAMETER_PLACEHOLDER)
}
}
fun collectSignatureForMangling(): String =
fqNamesForMangling.joinToString(separator = if (useOldManglingRules) ", " else "") {
getSignatureElementForMangling(it)
}
if (useOldManglingRules) {
if (requiresFunctionNameManglingForParameterTypes) {
return collectSignatureForMangling()
}
// If a class member function returns inline class value, mangle its name.
// NB here function can be a suspend function JVM view with return type replaced with 'Any',
// should unwrap it and take original return type instead.
if (returnTypeInfo != null) {
return ":" + getSignatureElementForMangling(returnTypeInfo)
}
} else {
// If a function accepts inline class parameters, mangle its name.
if (requiresFunctionNameManglingForParameterTypes || returnTypeInfo != null) {
// If a class member function returns inline class value, mangle its name.
// NB here function can be a suspend function JVM view with return type replaced with 'Any',
// should unwrap it and take original return type instead.
val signature = collectSignatureForMangling() +
if (returnTypeInfo != null)
":" + getSignatureElementForMangling(returnTypeInfo)
else ""
return signature
}
}
return null
}
fun getManglingSuffixBasedOnKotlinSignature( fun getManglingSuffixBasedOnKotlinSignature(
descriptor: CallableMemberDescriptor, descriptor: CallableMemberDescriptor,
shouldMangleByReturnType: Boolean, shouldMangleByReturnType: Boolean,
@@ -38,77 +95,31 @@ fun getManglingSuffixBasedOnKotlinSignature(
val unwrappedDescriptor = descriptor.unwrapInitialDescriptorForSuspendFunction() val unwrappedDescriptor = descriptor.unwrapInitialDescriptorForSuspendFunction()
if (useOldManglingRules || descriptor.isFunctionFromStdlib()) { val resultNew = collectFunctionSignatureForManglingSuffix(
if (requiresFunctionNameManglingForParameterTypes(descriptor)) { useOldManglingRules = useOldManglingRules || descriptor.isFunctionFromStdlib(),
return "-" + md5base64(collectSignatureForMangling(descriptor, true)) requiresFunctionNameManglingForParameterTypes = requiresFunctionNameManglingForParameterTypes(descriptor),
} fqNamesForMangling =
(listOfNotNull(descriptor.extensionReceiverParameter?.type) + descriptor.valueParameters.map { it.type })
.map { getInfoForMangling(it) },
returnTypeInfo =
if (shouldMangleByReturnType && requiresFunctionNameManglingForReturnType(unwrappedDescriptor))
getInfoForMangling(unwrappedDescriptor.returnType!!)
else null
)
// If a class member function returns inline class value, mangle its name. return resultNew?.let { "-" + md5base64(it) }
// NB here function can be a suspend function JVM view with return type replaced with 'Any',
// should unwrap it and take original return type instead.
if (shouldMangleByReturnType) {
if (requiresFunctionNameManglingForReturnType(unwrappedDescriptor)) {
return "-" + md5base64(
":" + getSignatureElementForMangling(
unwrappedDescriptor.returnType!!, true
)
)
}
}
} else {
// If a function accepts inline class parameters, mangle its name.
if (requiresFunctionNameManglingForParameterTypes(descriptor) ||
(shouldMangleByReturnType && requiresFunctionNameManglingForReturnType(unwrappedDescriptor))
) {
// If a class member function returns inline class value, mangle its name.
// NB here function can be a suspend function JVM view with return type replaced with 'Any',
// should unwrap it and take original return type instead.
val signature = collectSignatureForMangling(descriptor, false) +
if (shouldMangleByReturnType && requiresFunctionNameManglingForReturnType(unwrappedDescriptor))
":" + getSignatureElementForMangling(unwrappedDescriptor.returnType!!, false)
else ""
return "-" + md5base64(signature)
}
}
return null
} }
private fun collectSignatureForMangling(descriptor: CallableMemberDescriptor, useOldManglingRules: Boolean): String { private fun getInfoForMangling(type: KotlinType): InfoForMangling? {
val types = listOfNotNull(descriptor.extensionReceiverParameter?.type) + descriptor.valueParameters.map { it.type } val descriptor = type.constructor.declarationDescriptor ?: return null
return types.joinToString(separator = if (useOldManglingRules) ", " else "") { getSignatureElementForMangling(it, useOldManglingRules) } return when (descriptor) {
} is ClassDescriptor -> InfoForMangling(descriptor.fqNameUnsafe, descriptor.isInline, type.isMarkedNullable)
private fun getSignatureElementForMangling(type: KotlinType, useOldManglingRules: Boolean): String = buildString { is TypeParameterDescriptor -> {
val descriptor = type.constructor.declarationDescriptor ?: return "" getInfoForMangling(descriptor.representativeUpperBound)
if (useOldManglingRules) {
when (descriptor) {
is ClassDescriptor -> {
append('L')
append(descriptor.fqNameUnsafe)
if (type.isMarkedNullable) append('?')
append(';')
}
is TypeParameterDescriptor -> {
append(getSignatureElementForMangling(descriptor.representativeUpperBound, useOldManglingRules))
}
} }
} else {
when (descriptor) {
is ClassDescriptor -> if (descriptor.isInline) {
append('L')
append(descriptor.fqNameUnsafe)
if (type.isMarkedNullable) append('?')
append(';')
} else {
append(NOT_INLINE_CLASS_PARAMETER_PLACEHOLDER)
}
is TypeParameterDescriptor -> { else -> null
append(getSignatureElementForMangling(descriptor.representativeUpperBound, useOldManglingRules))
}
}
} }
} }
@@ -366,12 +366,13 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
private fun createInvokeMethod(receiverVar: IrValueDeclaration?): IrSimpleFunction = private fun createInvokeMethod(receiverVar: IrValueDeclaration?): IrSimpleFunction =
functionReferenceClass.addFunction { functionReferenceClass.addFunction {
setSourceRange(if (isLambda) callee else irFunctionReference) setSourceRange(if (isLambda) callee else irFunctionReference)
name = if (samSuperType == null && callee.returnType.erasedUpperBound.isInline && context.state.functionsWithInlineClassReturnTypesMangled) { name =
// For functions with inline class return type we need to mangle the invoke method. if (samSuperType == null && callee.returnType.erasedUpperBound.isInline && context.state.functionsWithInlineClassReturnTypesMangled) {
// Otherwise, bridge lowering may fail to generate bridges for inline class types erasing to Any. // For functions with inline class return type we need to mangle the invoke method.
val suffix = InlineClassAbi.hashSuffix(callee, mangleReturnTypes = true, useOldMangleRules = false) // Otherwise, bridge lowering may fail to generate bridges for inline class types erasing to Any.
Name.identifier("${superMethod.owner.name.asString()}-${suffix}") val suffix = InlineClassAbi.hashReturnSuffix(callee)
} else superMethod.owner.name Name.identifier("${superMethod.owner.name.asString()}-${suffix}")
} else superMethod.owner.name
returnType = callee.returnType returnType = callee.returnType
isSuspend = callee.isSuspend isSuspend = callee.isSuspend
}.apply { }.apply {
@@ -7,13 +7,15 @@ package org.jetbrains.kotlin.backend.jvm.lower.inlineclasses
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.codegen.state.NOT_INLINE_CLASS_PARAMETER_PLACEHOLDER import org.jetbrains.kotlin.codegen.state.InfoForMangling
import org.jetbrains.kotlin.codegen.state.collectFunctionSignatureForManglingSuffix
import org.jetbrains.kotlin.codegen.state.md5base64 import org.jetbrains.kotlin.codegen.state.md5base64
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrStatementOriginImpl import org.jetbrains.kotlin.ir.expressions.IrStatementOriginImpl
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
/** /**
@@ -74,29 +76,7 @@ object InlineClassAbi {
return Name.identifier("constructor-impl") return Name.identifier("constructor-impl")
} }
val suffix = if (useOldMangleRules || irFunction.isFunctionFromStdlib()) { val suffix = hashSuffix(irFunction, mangleReturnTypes, useOldMangleRules) ?: return irFunction.name
when {
irFunction.fullValueParameterList.any { it.type.requiresMangling } ->
hashSuffix(irFunction, mangleReturnTypes = false, useOldMangleRules = true)
mangleReturnTypes && irFunction.hasMangledReturnType ->
returnHashSuffix(irFunction)
(irFunction.parent as? IrClass)?.isInline == true &&
irFunction.origin != IrDeclarationOrigin.IR_BUILTINS_STUB ->
"impl"
else ->
return irFunction.name
}
} else {
when {
irFunction.fullValueParameterList.any { it.type.requiresMangling } || (mangleReturnTypes && irFunction.hasMangledReturnType) ->
hashSuffix(irFunction, mangleReturnTypes, false)
(irFunction.parent as? IrClass)?.isInline == true &&
irFunction.origin != IrDeclarationOrigin.IR_BUILTINS_STUB ->
"impl"
else ->
return irFunction.name
}
}
val base = when { val base = when {
irFunction.isGetter -> irFunction.isGetter ->
@@ -112,44 +92,54 @@ object InlineClassAbi {
return Name.identifier("$base-$suffix") return Name.identifier("$base-$suffix")
} }
private val IrFunction.propertyName: Name private fun hashSuffix(
get() = (this as IrSimpleFunction).correspondingPropertySymbol!!.owner.name irFunction: IrFunction,
mangleReturnTypes: Boolean,
useOldMangleRules: Boolean,
alwaysMangleReturnType: Boolean = false
): String? {
val signatureForMangling = collectFunctionSignatureForManglingSuffix(
useOldManglingRules = useOldMangleRules || irFunction.isFunctionFromStdlib(),
requiresFunctionNameManglingForParameterTypes = irFunction.fullValueParameterList.any { it.type.requiresMangling },
fqNamesForMangling = irFunction.fullValueParameterList.map {
it.type.asInfoForMangling()
} + listOfNotNull(
// The JVM backend computes mangled names after creating suspend function views, but before default argument
// stub insertion. It would be nice if this part of the continuation lowering happened earlier in the pipeline.
// TODO: Move suspend function view creation before JvmInlineClassLowering.
if (irFunction.isSuspend)
InfoForMangling(FqNameUnsafe("kotlin.coroutines.Continuation"), isInline = false, isNullable = false)
else null
),
returnTypeInfo = if (alwaysMangleReturnType || (mangleReturnTypes && irFunction.hasMangledReturnType)) {
irFunction.returnType.asInfoForMangling()
} else null
)
fun hashSuffix(irFunction: IrFunction, mangleReturnTypes: Boolean, useOldMangleRules: Boolean): String { return when {
val signatureElementsForMangling = signatureForMangling != null -> md5base64(signatureForMangling)
irFunction.fullValueParameterList.mapTo(mutableListOf()) { it.type.eraseToString(useOldMangleRules) } (irFunction.parent as? IrClass)?.isInline == true && irFunction.origin != IrDeclarationOrigin.IR_BUILTINS_STUB -> "impl"
if (irFunction.isSuspend) { else -> null
// The JVM backend computes mangled names after creating suspend function views, but before default argument
// stub insertion. It would be nice if this part of the continuation lowering happened earlier in the pipeline.
// TODO: Move suspend function view creation before JvmInlineClassLowering.
signatureElementsForMangling += if (useOldMangleRules) "Lkotlin.coroutines.Continuation;"
else NOT_INLINE_CLASS_PARAMETER_PLACEHOLDER
} }
val signatureString = signatureElementsForMangling.joinToString(separator = if (useOldMangleRules) ", " else "") +
if (mangleReturnTypes && irFunction.hasMangledReturnType && !useOldMangleRules)
":${irFunction.returnType.eraseToString(useOldMangleRules)}" else ""
return md5base64(signatureString)
} }
private fun returnHashSuffix(irFunction: IrFunction) = fun hashReturnSuffix(irFunction: IrFunction): String =
md5base64(":${irFunction.returnType.eraseToString(false)}") hashSuffix(
irFunction,
mangleReturnTypes = true,
useOldMangleRules = false,
alwaysMangleReturnType = true
)!!
private fun IrType.eraseToString(useOldMangleRules: Boolean) = private fun IrType.asInfoForMangling(): InfoForMangling =
if (useOldMangleRules) { InfoForMangling(
buildString { erasedUpperBound.fqNameWhenAvailable!!.toUnsafe(),
append('L') isInline = getClass()?.isInline == true,
append(erasedUpperBound.fqNameWhenAvailable!!) isNullable = isNullable()
if (isNullable()) append('?') )
append(';')
} private val IrFunction.propertyName: Name
} else { get() = (this as IrSimpleFunction).correspondingPropertySymbol!!.owner.name
if (getClass()?.isInline == true) buildString {
append('L')
append(erasedUpperBound.fqNameWhenAvailable!!)
if (isNullable()) append('?')
append(';')
} else NOT_INLINE_CLASS_PARAMETER_PLACEHOLDER
}
} }
internal val IrType.requiresMangling: Boolean internal val IrType.requiresMangling: Boolean