[K/N][codegen] Split CAdapterGenerator on two parts

This commit is contained in:
Igor Chevdar
2022-11-09 13:56:11 +02:00
committed by Space Team
parent f8b8cbb9f2
commit 87abb7ea3e
@@ -162,13 +162,9 @@ private class ExportedElementScope(val kind: ScopeKind, val name: String) {
return "$kind: $name ${elements.joinToString(", ")} ${scopes.joinToString("\n")}" return "$kind: $name ${elements.joinToString(", ")} ${scopes.joinToString("\n")}"
} }
fun generateCAdapters() { fun generateCAdapters(builder: (ExportedElement) -> Unit) {
elements.forEach { elements.forEach { builder(it) }
it.generateCAdapter() scopes.forEach { it.generateCAdapters(builder) }
}
scopes.forEach {
it.generateCAdapters()
}
} }
// collects names of inner scopes to make sure function<->scope name clashes would be detected, and functions would be mangled with "_" suffix // collects names of inner scopes to make sure function<->scope name clashes would be detected, and functions would be mangled with "_" suffix
@@ -197,7 +193,7 @@ private class ExportedElementScope(val kind: ScopeKind, val name: String) {
private class ExportedElement(val kind: ElementKind, private class ExportedElement(val kind: ElementKind,
val scope: ExportedElementScope, val scope: ExportedElementScope,
val declaration: DeclarationDescriptor, val declaration: DeclarationDescriptor,
val owner: CAdapterGenerator) : ContextUtils { val owner: CAdapterGenerator) {
init { init {
scope.elements.add(this) scope.elements.add(this)
} }
@@ -211,76 +207,6 @@ private class ExportedElement(val kind: ElementKind,
return "$kind: $name (aliased to ${if (::cname.isInitialized) cname.toString() else "<unknown>"})" return "$kind: $name (aliased to ${if (::cname.isInitialized) cname.toString() else "<unknown>"})"
} }
override val context = owner.context
fun generateCAdapter() {
when {
isFunction -> {
val function = declaration as FunctionDescriptor
val irFunction = irSymbol.owner as IrFunction
cname = "_konan_function_${owner.nextFunctionIndex()}"
val llvmCallable = owner.codegen.llvmFunction(irFunction)
// If function is virtual, we need to resolve receiver properly.
val bridge = generateFunction(owner.codegen, llvmCallable.functionType, cname) {
val callee = if (!DescriptorUtils.isTopLevelDeclaration(function) &&
irFunction.isOverridable) {
val receiver = param(0)
lookupVirtualImpl(receiver, irFunction)
} else {
// KT-45468: Alias insertion may not be handled by LLVM properly, in case callee is in the cache.
// Hence, insert not an alias but a wrapper, hoping it will be optimized out later.
llvmCallable
}
val numParams = LLVMCountParams(llvmCallable.llvmValue)
val args = (0..numParams - 1).map { index -> param(index) }
callee.attributeProvider.addFunctionAttributes(this.function)
val result = call(callee, args, exceptionHandler = ExceptionHandler.Caller, verbatim = true)
ret(result)
}
LLVMSetLinkage(bridge, LLVMLinkage.LLVMExternalLinkage)
}
isClass -> {
val irClass = irSymbol.owner as IrClass
cname = "_konan_function_${owner.nextFunctionIndex()}"
// Produce type getter.
val getTypeFunction = addLlvmFunctionWithDefaultAttributes(
context,
llvm.module,
"${cname}_type",
owner.kGetTypeFuncType
)
val builder = LLVMCreateBuilderInContext(llvm.llvmContext)!!
val bb = LLVMAppendBasicBlockInContext(llvm.llvmContext, getTypeFunction, "")!!
LLVMPositionBuilderAtEnd(builder, bb)
LLVMBuildRet(builder, irClass.typeInfoPtr.llvm)
LLVMDisposeBuilder(builder)
// Produce instance getter if needed.
if (isSingletonObject) {
generateFunction(owner.codegen, owner.kGetObjectFuncType, "${cname}_instance") {
val value = call(
owner.codegen.llvmFunction(context.getObjectClassInstanceFunction(irClass)),
emptyList(),
Lifetime.GLOBAL,
ExceptionHandler.Caller,
false,
returnSlot)
ret(value)
}
}
}
isEnumEntry -> {
// Produce entry getter.
cname = "_konan_function_${owner.nextFunctionIndex()}"
generateFunction(owner.codegen, owner.kGetObjectFuncType, cname) {
val irEnumEntry = irSymbol.owner as IrEnumEntry
val value = getEnumEntry(irEnumEntry, ExceptionHandler.Caller)
ret(value)
}
}
}
}
fun uniqueName(descriptor: DeclarationDescriptor, shortName: Boolean) = fun uniqueName(descriptor: DeclarationDescriptor, shortName: Boolean) =
scope.scopeUniqueName(descriptor, shortName) scope.scopeUniqueName(descriptor, shortName)
@@ -298,7 +224,7 @@ private class ExportedElement(val kind: ElementKind,
val isEnumEntry = declaration is ClassDescriptor && declaration.kind == ClassKind.ENUM_ENTRY val isEnumEntry = declaration is ClassDescriptor && declaration.kind == ClassKind.ENUM_ENTRY
val isSingletonObject = declaration is ClassDescriptor && DescriptorUtils.isObject(declaration) val isSingletonObject = declaration is ClassDescriptor && DescriptorUtils.isObject(declaration)
private val irSymbol = when { val irSymbol = when {
isFunction -> owner.symbolTable.referenceFunction(declaration as FunctionDescriptor) isFunction -> owner.symbolTable.referenceFunction(declaration as FunctionDescriptor)
isClass -> owner.symbolTable.referenceClass(declaration as ClassDescriptor) isClass -> owner.symbolTable.referenceClass(declaration as ClassDescriptor)
isEnumEntry -> owner.symbolTable.referenceEnumEntry(declaration as ClassDescriptor) isEnumEntry -> owner.symbolTable.referenceEnumEntry(declaration as ClassDescriptor)
@@ -534,30 +460,15 @@ private fun ModuleDescriptor.getPackageFragments(): List<PackageFragmentDescript
} }
internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVisitor<Boolean, Void?> { internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVisitor<Boolean, Void?> {
private val builtIns = context.builtIns
private val scopes = mutableListOf<ExportedElementScope>() private val scopes = mutableListOf<ExportedElementScope>()
internal val prefix = context.config.fullExportedNamePrefix.replace("-|\\.".toRegex(), "_") internal val prefix = context.config.fullExportedNamePrefix.replace("-|\\.".toRegex(), "_")
private lateinit var outputStreamWriter: PrintWriter
private val paramNamesRecorded = mutableMapOf<String, Int>() private val paramNamesRecorded = mutableMapOf<String, Int>()
private var codegenOrNull: CodeGenerator? = null
internal val codegen get() = codegenOrNull!!
private var symbolTableOrNull: SymbolTable? = null private var symbolTableOrNull: SymbolTable? = null
internal val symbolTable get() = symbolTableOrNull!! internal val symbolTable get() = symbolTableOrNull!!
// Primitive built-ins and unsigned types
private val predefinedTypes = listOf(
context.builtIns.byteType, context.builtIns.shortType,
context.builtIns.intType, context.builtIns.longType,
context.builtIns.floatType, context.builtIns.doubleType,
context.builtIns.charType, context.builtIns.booleanType,
context.builtIns.unitType
) + UnsignedType.values().map {
// Unfortunately, `context.ir` and `context.irBuiltins` are not initialized, so `context.ir.symbols.ubyte`, etc, are unreachable.
context.builtIns.builtInsModule.findClassAcrossModuleDependencies(it.classId)!!.defaultType
}
internal fun paramsToUniqueNames(params: List<ParameterDescriptor>): Map<ParameterDescriptor, String> { internal fun paramsToUniqueNames(params: List<ParameterDescriptor>): Map<ParameterDescriptor, String> {
paramNamesRecorded.clear() paramNamesRecorded.clear()
return params.associate { return params.associate {
@@ -695,15 +606,6 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
} }
} }
fun generateBindings(codegen: CodeGenerator) {
this.codegenOrNull = codegen
try {
generateBindings()
} finally {
this.codegenOrNull = null
}
}
private fun buildExports() { private fun buildExports() {
scopes.push(ExportedElementScope(ScopeKind.TOP, "kotlin")) scopes.push(ExportedElementScope(ScopeKind.TOP, "kotlin"))
moduleDescriptors += context.moduleDescriptor moduleDescriptors += context.moduleDescriptor
@@ -717,17 +619,207 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
context.moduleDescriptor.getPackage(FqName.ROOT).accept(this, null) context.moduleDescriptor.getPackage(FqName.ROOT).accept(this, null)
} }
private fun generateBindings() { private val simpleNameMapping = mapOf(
"<this>" to "thiz",
"<set-?>" to "set"
)
private val primitiveTypeMapping = KonanPrimitiveType.values().associate {
it to when (it) {
KonanPrimitiveType.BOOLEAN -> "${prefix}_KBoolean"
KonanPrimitiveType.CHAR -> "${prefix}_KChar"
KonanPrimitiveType.BYTE -> "${prefix}_KByte"
KonanPrimitiveType.SHORT -> "${prefix}_KShort"
KonanPrimitiveType.INT -> "${prefix}_KInt"
KonanPrimitiveType.LONG -> "${prefix}_KLong"
KonanPrimitiveType.FLOAT -> "${prefix}_KFloat"
KonanPrimitiveType.DOUBLE -> "${prefix}_KDouble"
KonanPrimitiveType.NON_NULL_NATIVE_PTR -> "void*"
KonanPrimitiveType.VECTOR128 -> "${prefix}_KVector128"
}
}
private val unsignedTypeMapping = UnsignedType.values().associate {
it.classId to when (it) {
UnsignedType.UBYTE -> "${prefix}_KUByte"
UnsignedType.USHORT -> "${prefix}_KUShort"
UnsignedType.UINT -> "${prefix}_KUInt"
UnsignedType.ULONG -> "${prefix}_KULong"
}
}
internal fun isMappedToString(type: KotlinType): Boolean =
isMappedToString(type.computeBinaryType())
private fun isMappedToString(binaryType: BinaryType<ClassDescriptor>): Boolean =
when (binaryType) {
is BinaryType.Primitive -> false
is BinaryType.Reference -> binaryType.types.first() == builtIns.string
}
internal fun isMappedToReference(type: KotlinType) =
!isMappedToVoid(type) && !isMappedToString(type) &&
type.binaryTypeIsReference()
internal fun isMappedToVoid(type: KotlinType): Boolean {
return type.isUnit() || type.isNothing()
}
fun translateName(name: Name): String {
val nameString = name.asString()
return when {
simpleNameMapping.contains(nameString) -> simpleNameMapping[nameString]!!
cKeywords.contains(nameString) -> "${nameString}_"
name.isSpecial -> nameString.replace("[<> ]".toRegex(), "_")
else -> nameString
}
}
private fun translateTypeFull(type: KotlinType): Pair<String, String> =
if (isMappedToVoid(type)) {
"void" to "void"
} else {
translateNonVoidTypeFull(type)
}
private fun translateNonVoidTypeFull(type: KotlinType): Pair<String, String> = type.unwrapToPrimitiveOrReference(
eachInlinedClass = { inlinedClass, _ ->
unsignedTypeMapping[inlinedClass.classId]?.let {
return it to it
}
},
ifPrimitive = { primitiveType, _ ->
primitiveTypeMapping[primitiveType]!!.let { it to it }
},
ifReference = {
val clazz = (it.computeBinaryType() as BinaryType.Reference).types.first()
if (clazz == builtIns.string) {
"const char*" to "KObjHeader*"
} else {
"${prefix}_kref_${translateTypeFqName(clazz.fqNameSafe.asString())}" to "KObjHeader*"
}
}
)
fun translateType(element: SignatureElement): String =
translateTypeFull(element.type).first
fun translateType(type: KotlinType): String
= translateTypeFull(type).first
fun translateTypeBridge(type: KotlinType): String = translateTypeFull(type).second
fun translateTypeFqName(name: String): String {
return name.replace('.', '_')
}
private var functionIndex = 0
fun nextFunctionIndex() = functionIndex++
fun generateBindings(codegen: CodeGenerator) = BindingsBuilder(codegen).build()
inner class BindingsBuilder(val codegen: CodeGenerator) : ContextUtils {
override val context = this@CAdapterGenerator.context
internal val prefix = context.config.fullExportedNamePrefix.replace("-|\\.".toRegex(), "_")
private lateinit var outputStreamWriter: PrintWriter
// Primitive built-ins and unsigned types
private val predefinedTypes = listOf(
builtIns.byteType, builtIns.shortType,
builtIns.intType, builtIns.longType,
builtIns.floatType, builtIns.doubleType,
builtIns.charType, builtIns.booleanType,
builtIns.unitType
) + UnsignedType.values().map {
// Unfortunately, `context.ir` and `context.irBuiltins` are not initialized, so `context.ir.symbols.ubyte`, etc, are unreachable.
builtIns.builtInsModule.findClassAcrossModuleDependencies(it.classId)!!.defaultType
}
fun build() {
val top = scopes.pop() val top = scopes.pop()
assert(scopes.isEmpty() && top.kind == ScopeKind.TOP) assert(scopes.isEmpty() && top.kind == ScopeKind.TOP)
// Now, let's generate C world adapters for all functions. // Now, let's generate C world adapters for all functions.
top.generateCAdapters() top.generateCAdapters(::buildCAdapter)
// Then generate data structure, describing generated adapters. // Then generate data structure, describing generated adapters.
makeGlobalStruct(top) makeGlobalStruct(top)
} }
private fun buildCAdapter(exportedElement: ExportedElement): Unit = with(exportedElement) {
when {
isFunction -> {
val function = declaration as FunctionDescriptor
val irFunction = irSymbol.owner as IrFunction
cname = "_konan_function_${owner.nextFunctionIndex()}"
val llvmCallable = codegen.llvmFunction(irFunction)
// If function is virtual, we need to resolve receiver properly.
val bridge = generateFunction(codegen, llvmCallable.functionType, cname) {
val callee = if (!DescriptorUtils.isTopLevelDeclaration(function) &&
irFunction.isOverridable) {
val receiver = param(0)
lookupVirtualImpl(receiver, irFunction)
} else {
// KT-45468: Alias insertion may not be handled by LLVM properly, in case callee is in the cache.
// Hence, insert not an alias but a wrapper, hoping it will be optimized out later.
llvmCallable
}
val numParams = LLVMCountParams(llvmCallable.llvmValue)
val args = (0 until numParams).map { index -> param(index) }
callee.attributeProvider.addFunctionAttributes(this.function)
val result = call(callee, args, exceptionHandler = ExceptionHandler.Caller, verbatim = true)
ret(result)
}
LLVMSetLinkage(bridge, LLVMLinkage.LLVMExternalLinkage)
}
isClass -> {
val irClass = irSymbol.owner as IrClass
cname = "_konan_function_${owner.nextFunctionIndex()}"
// Produce type getter.
val getTypeFunction = addLlvmFunctionWithDefaultAttributes(
context,
llvm.module,
"${cname}_type",
kGetTypeFuncType
)
val builder = LLVMCreateBuilderInContext(llvm.llvmContext)!!
val bb = LLVMAppendBasicBlockInContext(llvm.llvmContext, getTypeFunction, "")!!
LLVMPositionBuilderAtEnd(builder, bb)
LLVMBuildRet(builder, irClass.typeInfoPtr.llvm)
LLVMDisposeBuilder(builder)
// Produce instance getter if needed.
if (isSingletonObject) {
generateFunction(codegen, kGetObjectFuncType, "${cname}_instance") {
val value = call(
codegen.llvmFunction(context.getObjectClassInstanceFunction(irClass)),
emptyList(),
Lifetime.GLOBAL,
ExceptionHandler.Caller,
false,
returnSlot)
ret(value)
}
}
}
isEnumEntry -> {
// Produce entry getter.
cname = "_konan_function_${owner.nextFunctionIndex()}"
generateFunction(codegen, kGetObjectFuncType, cname) {
val irEnumEntry = irSymbol.owner as IrEnumEntry
val value = getEnumEntry(irEnumEntry, ExceptionHandler.Caller)
ret(value)
}
}
}
}
private val kGetTypeFuncType = LLVMFunctionType(codegen.kTypeInfoPtr, null, 0, 0)!!
// Abstraction leak for slot :(.
private val kGetObjectFuncType = LLVMFunctionType(codegen.kObjHeaderPtr, cValuesOf(codegen.kObjHeaderPtrPtr), 1, 0)!!
private fun output(string: String, indent: Int = 0) { private fun output(string: String, indent: Int = 0) {
if (indent != 0) outputStreamWriter.print(" " * indent) if (indent != 0) outputStreamWriter.print(" " * indent)
outputStreamWriter.println(string) outputStreamWriter.println(string)
@@ -1070,107 +1162,5 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
outputStreamWriter.close() outputStreamWriter.close()
} }
} }
private val simpleNameMapping = mapOf(
"<this>" to "thiz",
"<set-?>" to "set"
)
private val primitiveTypeMapping = KonanPrimitiveType.values().associate {
it to when (it) {
KonanPrimitiveType.BOOLEAN -> "${prefix}_KBoolean"
KonanPrimitiveType.CHAR -> "${prefix}_KChar"
KonanPrimitiveType.BYTE -> "${prefix}_KByte"
KonanPrimitiveType.SHORT -> "${prefix}_KShort"
KonanPrimitiveType.INT -> "${prefix}_KInt"
KonanPrimitiveType.LONG -> "${prefix}_KLong"
KonanPrimitiveType.FLOAT -> "${prefix}_KFloat"
KonanPrimitiveType.DOUBLE -> "${prefix}_KDouble"
KonanPrimitiveType.NON_NULL_NATIVE_PTR -> "void*"
KonanPrimitiveType.VECTOR128 -> "${prefix}_KVector128"
} }
} }
private val unsignedTypeMapping = UnsignedType.values().associate {
it.classId to when (it) {
UnsignedType.UBYTE -> "${prefix}_KUByte"
UnsignedType.USHORT -> "${prefix}_KUShort"
UnsignedType.UINT -> "${prefix}_KUInt"
UnsignedType.ULONG -> "${prefix}_KULong"
}
}
internal fun isMappedToString(type: KotlinType): Boolean =
isMappedToString(type.computeBinaryType())
private fun isMappedToString(binaryType: BinaryType<ClassDescriptor>): Boolean =
when (binaryType) {
is BinaryType.Primitive -> false
is BinaryType.Reference -> binaryType.types.first() == context.builtIns.string
}
internal fun isMappedToReference(type: KotlinType) =
!isMappedToVoid(type) && !isMappedToString(type) &&
type.binaryTypeIsReference()
internal fun isMappedToVoid(type: KotlinType): Boolean {
return type.isUnit() || type.isNothing()
}
fun translateName(name: Name): String {
val nameString = name.asString()
return when {
simpleNameMapping.contains(nameString) -> simpleNameMapping[nameString]!!
cKeywords.contains(nameString) -> "${nameString}_"
name.isSpecial -> nameString.replace("[<> ]".toRegex(), "_")
else -> nameString
}
}
private fun translateTypeFull(type: KotlinType): Pair<String, String> =
if (isMappedToVoid(type)) {
"void" to "void"
} else {
translateNonVoidTypeFull(type)
}
private fun translateNonVoidTypeFull(type: KotlinType): Pair<String, String> = type.unwrapToPrimitiveOrReference(
eachInlinedClass = { inlinedClass, _ ->
unsignedTypeMapping[inlinedClass.classId]?.let {
return it to it
}
},
ifPrimitive = { primitiveType, _ ->
primitiveTypeMapping[primitiveType]!!.let { it to it }
},
ifReference = {
val clazz = (it.computeBinaryType() as BinaryType.Reference).types.first()
if (clazz == context.builtIns.string) {
"const char*" to "KObjHeader*"
} else {
"${prefix}_kref_${translateTypeFqName(clazz.fqNameSafe.asString())}" to "KObjHeader*"
}
}
)
fun translateType(element: SignatureElement): String =
translateTypeFull(element.type).first
fun translateType(type: KotlinType): String
= translateTypeFull(type).first
fun translateTypeBridge(type: KotlinType): String = translateTypeFull(type).second
fun translateTypeFqName(name: String): String {
return name.replace('.', '_')
}
private var functionIndex = 0
fun nextFunctionIndex() = functionIndex++
internal val kGetTypeFuncType get() =
LLVMFunctionType(codegen.kTypeInfoPtr, null, 0, 0)!!
// Abstraction leak for slot :(.
internal val kGetObjectFuncType get() =
LLVMFunctionType(codegen.kObjHeaderPtr, cValuesOf(codegen.kObjHeaderPtrPtr), 1, 0)!!
}