diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index 69ea5e3cad5..0ec2d1223ef 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -86,14 +86,11 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration private val resolver = defaultResolver(repositories, target, distribution) internal val immediateLibraries: List by lazy { - val result = resolver.resolveImmediateLibraries( - libraryNames, - target, + val result = resolver.resolveImmediateLibraries(libraryNames, target, currentAbiVersion, configuration.getBoolean(KonanConfigKeys.NOSTDLIB), configuration.getBoolean(KonanConfigKeys.NODEFAULTLIBS), - { msg -> configuration.report(STRONG_WARNING, msg) } - ) + { msg -> configuration.report(STRONG_WARNING, msg) }) resolver.resolveLibrariesRecursive(result, target, currentAbiVersion) result } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt index 09b27ed1e84..93d40747f8f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.isUnit import org.jetbrains.kotlin.backend.konan.getObjCMethodInfo import org.jetbrains.kotlin.backend.konan.isExternalObjCClass import org.jetbrains.kotlin.backend.konan.isValueType +import org.jetbrains.kotlin.backend.konan.library.KonanLibraryReader import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor @@ -32,7 +33,6 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.components.isVararg import org.jetbrains.kotlin.resolve.constants.StringValue import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty @@ -274,3 +274,9 @@ internal val ClassDescriptor.typeInfoHasVtableAttached: Boolean internal val ModuleDescriptor.privateFunctionsTableSymbolName get() = "private_functions_${name.asString()}" internal val ModuleDescriptor.privateClassesTableSymbolName get() = "private_classes_${name.asString()}" + +internal val String.moduleConstructorName + get() = "_Konan_init_${this}" + +internal val KonanLibraryReader.moduleConstructorName + get() = uniqueName.moduleConstructorName diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 77482f57e10..a4cb306e1f2 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -344,9 +344,35 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) { } } - val librariesToLink: List get() = context.config.immediateLibraries - .filter { (!it.isDefaultLibrary && !context.config.purgeUserLibs) || it in usedLibraries } - .withResolvedDependencies() + val librariesToLink: List by lazy { + context.config.immediateLibraries + .filter { (!it.isDefaultLibrary && !context.config.purgeUserLibs) || it in usedLibraries } + .withResolvedDependencies() + .topoSort() + } + + private fun List.topoSort(): List { + var sorted = mutableListOf() + val visited = mutableSetOf() + val tempMarks = mutableSetOf() + + fun visit(node: LibraryReaderImpl, result: MutableList) { + if (visited.contains(node)) return + if (tempMarks.contains(node)) error("Cyclic dependency in library graph.") + tempMarks.add(node) + node.resolvedDependencies.forEach { + visit(it, result) + } + visited.add(node) + result += node + } + + this.forEach next@{ + if (visited.contains(it)) return@next + visit(it, sorted) + } + return sorted + } val librariesForLibraryManifest: List get() { // Note: library manifest should contain the list of all user libraries and frontend-used default libraries. diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 5bf173910e7..32e8cb0895b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.backend.konan.ir.* import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport import org.jetbrains.kotlin.backend.konan.optimizations.* import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.IrElement @@ -41,6 +42,7 @@ import org.jetbrains.kotlin.ir.util.getArguments import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -354,13 +356,14 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map) { - if (initializers.isEmpty()) return + val ctorName = context.config.moduleId.moduleConstructorName + val ctorFunction = LLVMAddFunction(context.llvmModule, ctorName, kVoidFuncType)!! // Create constructor function. + LLVMSetLinkage(ctorFunction, LLVMLinkage.LLVMExternalLinkage) + generateFunction(codegen, ctorFunction) { + val initGuard = LLVMAddGlobal(context.llvmModule, int32Type, "Konan_init_guard") + LLVMSetInitializer(initGuard, kImmZero) + LLVMSetLinkage(initGuard, LLVMLinkage.LLVMPrivateLinkage) + val bbInited = basicBlock("inited", null) + val bbNeedInit = basicBlock("need_init", null) - val ctorList = initializers.map { it -> createGlobalCtor(it) } - val globalCtors = context.llvm.staticData.placeGlobalArray("llvm.global_ctors", kCtorType, ctorList) - LLVMSetLinkage(globalCtors.llvmGlobal, LLVMLinkage.LLVMAppendingLinkage) + val value = LLVMBuildLoad(builder, initGuard, "")!! + condBr(icmpEq(value, kImmZero), bbNeedInit, bbInited) + + appendingTo(bbInited) { + ret(null) + } + + appendingTo(bbNeedInit) { + LLVMBuildStore(builder, kImmOne, initGuard) + + if (context.config.produce.isNativeBinary) { + context.llvm.librariesToLink.forEach { + val dependencyCtorFunction = context.llvm.externalFunction( + it.moduleConstructorName, kVoidFuncType, CurrentKonanModule) + call(dependencyCtorFunction, emptyList(), Lifetime.IRRELEVANT, + exceptionHandler = ExceptionHandler.Caller, verbatim = true) + } + } + // TODO: shall we put that into the try block? + initializers.forEach { + call(it, emptyList(), Lifetime.IRRELEVANT, + exceptionHandler = ExceptionHandler.Caller, verbatim = true) + } + ret(null) + } + } + + if (context.config.produce.isNativeBinary) { + // Append initializers of global variables in "llvm.global_ctors" array + val globalCtors = context.llvm.staticData.placeGlobalArray("llvm.global_ctors", kCtorType, + listOf(createGlobalCtor(ctorFunction))) + LLVMSetLinkage(globalCtors.llvmGlobal, LLVMLinkage.LLVMAppendingLinkage) + if (context.config.produce == CompilerOutputKind.PROGRAM) { + // Provide an optional handle for calling .ctors, if standard constrcutors mechanism + // is not available on the platform (i.e. WASM, embedded). + val globalCtorFunction = LLVMAddFunction(context.llvmModule, "_Konan_constructors", kVoidFuncType)!! + LLVMSetLinkage(globalCtorFunction, LLVMLinkage.LLVMExternalLinkage) + generateFunction(codegen, globalCtorFunction) { + // Unfortunately, LLVMAddAlias() doesn't seem to work on WASM yet. + call(ctorFunction, emptyList(), Lifetime.IRRELEVANT, + exceptionHandler = ExceptionHandler.Caller, verbatim = true) + ret(null) + } + appendLlvmUsed("llvm.used", listOf(globalCtorFunction)) + } + } } //-------------------------------------------------------------------------// diff --git a/runtime/src/launcher/cpp/launcher.cpp b/runtime/src/launcher/cpp/launcher.cpp index 7d6f878a8aa..f73ed1a9933 100644 --- a/runtime/src/launcher/cpp/launcher.cpp +++ b/runtime/src/launcher/cpp/launcher.cpp @@ -45,6 +45,12 @@ extern "C" KInt Konan_run_start(int argc, const char** argv) { } extern "C" RUNTIME_USED int Konan_main(int argc, const char** argv) { + +#ifdef KONAN_NO_CTORS_SECTION + extern void _Konan_constructors(void); + _Konan_constructors(); +#endif + Kotlin_initRuntimeIfNeeded(); KInt exitStatus = Konan_run_start(argc, argv); diff --git a/runtime/src/launcher/js/launcher.js b/runtime/src/launcher/js/launcher.js index de5eb5fc322..b2e9d9432b6 100644 --- a/runtime/src/launcher/js/launcher.js +++ b/runtime/src/launcher/js/launcher.js @@ -133,14 +133,6 @@ function stackTop() { return new Uint32Array(fourBytes)[0]; } -function runGlobalInitializers(exports) { - for (var property in exports) { - if (property.startsWith("Konan_global_ctor_")) { - exports[property](); - } - } -} - var konan_dependencies = { env: { abort: function() { @@ -220,7 +212,6 @@ function invokeModule(inst, args) { var exit_status = 0; try { - runGlobalInitializers(instance.exports); if (isBrowser()) { instance.exports.Kotlin_initRuntimeIfNeeded(); } diff --git a/runtime/src/launcher/kotlin/konan/start.kt b/runtime/src/launcher/kotlin/konan/start.kt index 9bf2eab0873..521434f2e34 100644 --- a/runtime/src/launcher/kotlin/konan/start.kt +++ b/runtime/src/launcher/kotlin/konan/start.kt @@ -26,7 +26,6 @@ external fun EntryPointSelector(args: Array) @ExportForCppRuntime private fun Konan_start(args: Array): Int { try { - EntryPointSelector(args) // Successfully finished: diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt index 159607044bf..63ed57864c1 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt @@ -135,12 +135,12 @@ class ClangArgs(private val configurables: Configurables) : Configurables by con KonanTarget.WASM32 -> listOf("-DKONAN_WASM=1", "-DKONAN_NO_FFI=1", "-DKONAN_NO_THREADS=1", "-DKONAN_NO_EXCEPTIONS=1", "-DKONAN_NO_MATH=1", "-DKONAN_INTERNAL_DLMALLOC=1", "-DKONAN_INTERNAL_SNPRINTF=1", - "-DKONAN_INTERNAL_NOW=1", "-DKONAN_NO_MEMMEM") + "-DKONAN_INTERNAL_NOW=1", "-DKONAN_NO_MEMMEM", "-DKONAN_NO_CTORS_SECTION") is KonanTarget.ZEPHYR -> listOf( "-DKONAN_ZEPHYR=1", "-DKONAN_NO_FFI=1", "-DKONAN_NO_THREADS=1", "-DKONAN_NO_EXCEPTIONS=1", "-DKONAN_NO_MATH=1", "-DKONAN_INTERNAL_SNPRINTF=1", "-DKONAN_INTERNAL_NOW=1", - "-DKONAN_NO_MEMMEM=1") + "-DKONAN_NO_MEMMEM=1", "-DKONAN_NO_CTORS_SECTION") } private val host = HostManager.host