diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index 92ec4744ff7..ceb1f07fbb8 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -108,8 +108,7 @@ internal val buildCExportsPhase = konanUnitPhase( op = { val prefix = config.fullExportedNamePrefix.replace("-|\\.".toRegex(), "_") val cAdapterTypeTranslator = CAdapterTypeTranslator(prefix, this.builtIns) - this.cAdapterExportedElements = CAdapterGenerator(this, this.moduleDescriptor, cAdapterTypeTranslator) - .buildExports(this.symbolTable!!) + this.cAdapterExportedElements = CAdapterGenerator(this, cAdapterTypeTranslator).buildExports(this.moduleDescriptor) }, name = "BuildCExports", description = "Build C exports", diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterBindingsBuilder.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterApiExporter.kt similarity index 75% rename from kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterBindingsBuilder.kt rename to kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterApiExporter.kt index e4ee783cefa..ddd467ee51f 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterBindingsBuilder.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterApiExporter.kt @@ -5,44 +5,31 @@ package org.jetbrains.kotlin.backend.konan.cexport -import kotlinx.cinterop.cValuesOf -import llvm.* -import org.jetbrains.kotlin.backend.common.pop -import org.jetbrains.kotlin.backend.konan.llvm.* -import org.jetbrains.kotlin.backend.konan.llvm.CodeGenerator -import org.jetbrains.kotlin.backend.konan.llvm.ContextUtils -import org.jetbrains.kotlin.backend.konan.llvm.ExceptionHandler -import org.jetbrains.kotlin.backend.konan.llvm.Lifetime -import org.jetbrains.kotlin.backend.konan.llvm.addLlvmFunctionWithDefaultAttributes -import org.jetbrains.kotlin.backend.konan.llvm.generateFunction -import org.jetbrains.kotlin.backend.konan.lower.getObjectClassInstanceFunction +import org.jetbrains.kotlin.backend.konan.NativeGenerationState import org.jetbrains.kotlin.builtins.UnsignedType import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrEnumEntry -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.util.isOverridable import org.jetbrains.kotlin.konan.target.Family -import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.types.typeUtil.makeNullable import java.io.PrintWriter -internal class CAdapterBindingsBuilder( - val codegen: CodeGenerator, - val elements: CAdapterExportedElements, -) : ContextUtils { - override val generationState = codegen.generationState - +/** + * Third phase of C export: + * 1. Create a C++ file with runtime bindings + * 2. Create a header file with API + * 3. (MinGW only) create EXPORTS def file. + */ +internal class CAdapterApiExporter( + private val generationState: NativeGenerationState, + private val elements: CAdapterExportedElements, +) { private val typeTranslator = elements.typeTranslator private val builtIns = elements.typeTranslator.builtIns - private val scopes = elements.scopes - internal val prefix = context.config.fullExportedNamePrefix.replace("-|\\.".toRegex(), "_") + private val prefix = elements.typeTranslator.prefix private lateinit var outputStreamWriter: PrintWriter // Primitive built-ins and unsigned types @@ -57,92 +44,6 @@ internal class CAdapterBindingsBuilder( builtIns.builtInsModule.findClassAcrossModuleDependencies(it.classId)!!.defaultType } - fun build() { - val top = scopes.pop() - assert(scopes.isEmpty() && top.kind == ScopeKind.TOP) - - // Now, let's generate C world adapters for all functions. - top.generateCAdapters(::buildCAdapter) - - // Then generate data structure, describing generated adapters. - 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) { if (indent != 0) outputStreamWriter.print(" " * indent) outputStreamWriter.println(string) @@ -249,9 +150,11 @@ internal class CAdapterBindingsBuilder( } } - val exportedSymbols = mutableListOf() + private val exportedSymbols = mutableListOf() - private fun makeGlobalStruct(top: ExportedElementScope) { + // TODO: Pass temp and output files explicitly and untie from `NativeGenerationState`. + fun makeGlobalStruct() { + val top = elements.scopes.first() val headerFile = generationState.outputFiles.cAdapterHeader outputStreamWriter = headerFile.printWriter() @@ -285,7 +188,7 @@ internal class CAdapterBindingsBuilder( output("typedef double ${prefix}_KDouble;") val typedef_KVector128 = "typedef float __attribute__ ((__vector_size__ (16))) ${prefix}_KVector128;" - if (context.config.target.family == Family.MINGW) { + if (generationState.config.target.family == Family.MINGW) { // Separate `output` for each line to ensure Windows EOL (LFCR), otherwise generated file will have inconsistent line ending. output("#ifndef _MSC_VER") output(typedef_KVector128) @@ -476,7 +379,7 @@ internal class CAdapterBindingsBuilder( output("RUNTIME_USED ${prefix}_ExportedSymbols* $exportedSymbol(void) { return &__konan_symbols;}") outputStreamWriter.close() - if (context.config.target.family == Family.MINGW) { + if (generationState.config.target.family == Family.MINGW) { outputStreamWriter = generationState.outputFiles .cAdapterDef .printWriter() diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterCodegen.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterCodegen.kt new file mode 100644 index 00000000000..0cfa573950e --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterCodegen.kt @@ -0,0 +1,119 @@ +/* + * 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. + */ + +package org.jetbrains.kotlin.backend.konan.cexport + +import kotlinx.cinterop.cValuesOf +import llvm.* +import org.jetbrains.kotlin.backend.konan.NativeGenerationState +import org.jetbrains.kotlin.backend.konan.llvm.* +import org.jetbrains.kotlin.backend.konan.llvm.CodeGenerator +import org.jetbrains.kotlin.backend.konan.llvm.ContextUtils +import org.jetbrains.kotlin.backend.konan.llvm.ExceptionHandler +import org.jetbrains.kotlin.backend.konan.llvm.Lifetime +import org.jetbrains.kotlin.backend.konan.llvm.addLlvmFunctionWithDefaultAttributes +import org.jetbrains.kotlin.backend.konan.llvm.generateFunction +import org.jetbrains.kotlin.backend.konan.lower.getObjectClassInstanceFunction +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrEnumEntry +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.util.isOverridable +import org.jetbrains.kotlin.resolve.DescriptorUtils + +/** + * Second phase of C Export: build bitcode bridges from C wrappers to Kotlin functions. + */ +internal class CAdapterCodegen( + private val codegen: CodeGenerator, + override val generationState: NativeGenerationState, +) : ContextUtils { + + fun buildAllAdaptersRecursively(elements: CAdapterExportedElements) { + val top = elements.scopes.single() + assert(top.kind == ScopeKind.TOP) + top.generateCAdapters(this::buildCAdapter) + } + + private fun ExportedElementScope.generateCAdapters(builder: (ExportedElement) -> Unit) { + this.elements.forEach { builder(it) } + this.scopes.forEach { it.generateCAdapters(builder) } + } + + 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)!! +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterCompile.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterCompile.kt index 6f0d90f0b18..b740dd1a50d 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterCompile.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterCompile.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.konan.exec.* import org.jetbrains.kotlin.konan.target.* import org.jetbrains.kotlin.konan.file.* +// Fourth phase of C export: compile runtime bindings to bitcode. fun produceCAdapterBitcode(clang: ClangArgs, cppFileName: String, bitcodeFileName: String) { val clangCommand = clang.clangCXX("-std=c++17", cppFileName, "-emit-llvm", "-c", "-o", bitcodeFileName) Command(clangCommand).execute() diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterGenerator.kt index 5abc716439c..7207eb66ea4 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cexport/CAdapterGenerator.kt @@ -5,26 +5,24 @@ package org.jetbrains.kotlin.backend.konan.cexport -import llvm.* import org.jetbrains.kotlin.backend.common.descriptors.allParameters import org.jetbrains.kotlin.backend.common.pop import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.driver.phases.PsiToIrContext -import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor -import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.referenceFunction -import org.jetbrains.kotlin.konan.target.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.isChildOf import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.annotations.* -import org.jetbrains.kotlin.resolve.descriptorUtil.* +import org.jetbrains.kotlin.resolve.annotations.argumentValue +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi +import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.typeUtil.* +import org.jetbrains.kotlin.types.typeUtil.isUnit internal enum class ScopeKind { TOP, @@ -96,11 +94,6 @@ internal class ExportedElementScope(val kind: ScopeKind, val name: String) { return "$kind: $name ${elements.joinToString(", ")} ${scopes.joinToString("\n")}" } - fun generateCAdapters(builder: (ExportedElement) -> Unit) { - elements.forEach { builder(it) } - scopes.forEach { it.generateCAdapters(builder) } - } - // collects names of inner scopes to make sure function<->scope name clashes would be detected, and functions would be mangled with "_" suffix fun collectInnerScopeName(innerScope: ExportedElementScope) { scopeNames += innerScope.name @@ -396,17 +389,18 @@ private fun ModuleDescriptor.getPackageFragments(): List { private val scopes = mutableListOf() internal val prefix = typeTranslator.prefix private val paramNamesRecorded = mutableMapOf() - private var symbolTableOrNull: SymbolTable? = null - internal val symbolTable get() = symbolTableOrNull!! + internal val symbolTable get() = context.symbolTable!! internal fun paramsToUniqueNames(params: List): Map { paramNamesRecorded.clear() @@ -536,17 +530,7 @@ internal class CAdapterGenerator( private val moduleDescriptors = mutableSetOf() - fun buildExports(symbolTable: SymbolTable): CAdapterExportedElements { - this.symbolTableOrNull = symbolTable - try { - buildExports() - return CAdapterExportedElements(typeTranslator, scopes) - } finally { - this.symbolTableOrNull = null - } - } - - private fun buildExports() { + fun buildExports(moduleDescriptor: ModuleDescriptor): CAdapterExportedElements { scopes.push(ExportedElementScope(ScopeKind.TOP, "kotlin")) moduleDescriptors += moduleDescriptor moduleDescriptors += moduleDescriptor.getExportedDependencies(context.config) @@ -557,6 +541,7 @@ internal class CAdapterGenerator( }) moduleDescriptor.getPackage(FqName.ROOT).accept(this, null) + return CAdapterExportedElements(typeTranslator, scopes) } private val simpleNameMapping = mapOf( diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/CExport.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/CExport.kt index deac20af798..391bd732297 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/CExport.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/CExport.kt @@ -15,5 +15,5 @@ internal val BuildCExports = createSimpleNamedCompilerPhase val prefix = context.config.fullExportedNamePrefix.replace("-|\\.".toRegex(), "_") val typeTranslator = CAdapterTypeTranslator(prefix, context.builtIns) - CAdapterGenerator(context, input.moduleDescriptor, typeTranslator).buildExports(context.symbolTable!!) + CAdapterGenerator(context, typeTranslator).buildExports(input.moduleDescriptor) } \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 251588c8e2f..04ee26e2323 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -10,7 +10,8 @@ import llvm.* import org.jetbrains.kotlin.backend.common.lower.coroutines.getOrCreateFunctionWithContinuationStub import org.jetbrains.kotlin.backend.common.lower.inline.InlinerExpressionLocationHint import org.jetbrains.kotlin.backend.konan.* -import org.jetbrains.kotlin.backend.konan.cexport.CAdapterBindingsBuilder +import org.jetbrains.kotlin.backend.konan.cexport.CAdapterApiExporter +import org.jetbrains.kotlin.backend.konan.cexport.CAdapterCodegen import org.jetbrains.kotlin.backend.konan.cgen.CBridgeOrigin import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.backend.konan.ir.* @@ -327,7 +328,10 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, } } private fun appendCAdapters() { - CAdapterBindingsBuilder(codegen, context.cAdapterExportedElements).build() + val elements = context.cAdapterExportedElements + CAdapterCodegen(codegen, generationState).buildAllAdaptersRecursively(elements) + // TODO: It is not a part of IrToBitcode. Maybe move it somewhere? + CAdapterApiExporter(generationState, elements).makeGlobalStruct() } private fun FunctionGenerationContext.initThreadLocalField(irField: IrField) {