From 3ab201ce10b81942814a92f06e3ed15fa9f8bebf Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 14 Sep 2024 15:21:00 +0200 Subject: [PATCH] Use atexit to fix finalizers on Windows #1361. --- releasenotes.md | 1 + resources/examples/ls.c3 | 4 ++-- resources/msvc_stack.c3 | 6 ++++++ src/compiler/llvm_codegen_function.c | 18 +++++++++++++++++- src/compiler/llvm_codegen_internal.h | 1 + src/compiler/llvm_codegen_module.c | 4 +++- .../initialize/initialize_finalize.c3t | 9 +++++++-- 7 files changed, 37 insertions(+), 6 deletions(-) diff --git a/releasenotes.md b/releasenotes.md index 5a0126c84..01c466dcd 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -24,6 +24,7 @@ - Correct '.so' suffix on dynamic libraries on Linux. - Fix bug where inline index access to array in a struct would crash the compiler. - Asserts are now correctly included and traced in when running tests. +- Use atexit to fix finalizers on Windows #1361. ### Stdlib changes - Additional init functions for hashmap. diff --git a/resources/examples/ls.c3 b/resources/examples/ls.c3 index 7bdc03917..0a3902852 100644 --- a/resources/examples/ls.c3 +++ b/resources/examples/ls.c3 @@ -2,8 +2,8 @@ import std::io; fn void main() { - Path path = path::getcwd()!!; - foreach (i, p : path::ls(path)!!) + Path path = path::new_cwd()!!; + foreach (i, p : path::new_ls(path)!!) { io::printfn("%02d %s", i, p.str_view()); } diff --git a/resources/msvc_stack.c3 b/resources/msvc_stack.c3 index 3e564e928..18b964a6c 100644 --- a/resources/msvc_stack.c3 +++ b/resources/msvc_stack.c3 @@ -2,6 +2,12 @@ module test; import std::io; import std::collections::map; import std::os; +import libc; + +fn void bye() @finalizer +{ + libc::puts("Bye from finalizer!"); +} fn void test1() { diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 3365c79b3..2709c4f1c 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -406,10 +406,26 @@ void llvm_emit_function_body(GenContext *c, Decl *decl) DEBUG_LOG("Generating function %s.", decl->name); if (decl->func_decl.attr_dynamic) vec_add(c->dynamic_functions, decl); assert(decl->backend_ref); - if (decl->func_decl.attr_init || decl->func_decl.attr_finalizer) + if (decl->func_decl.attr_init || (decl->func_decl.attr_finalizer && compiler.platform.object_format == OBJ_FORMAT_MACHO)) { llvm_append_xxlizer(c, decl->func_decl.priority, decl->func_decl.attr_init, decl->backend_ref); } + if (decl->func_decl.attr_finalizer && compiler.platform.object_format != OBJ_FORMAT_MACHO) + { + LLVMValueRef atexit = LLVMGetNamedFunction(c->module, "atexit"); + if (!atexit) atexit = LLVMAddFunction(c->module, "atexit", c->atexit_type); + scratch_buffer_clear(); + scratch_buffer_append(".__c3_atexit_"); + scratch_buffer_set_extern_decl_name(decl, false); + LLVMValueRef func = LLVMAddFunction(c->module, scratch_buffer_to_string(), c->xtor_func_type); + + LLVMBuilderRef builder = llvm_create_function_entry(c, func, NULL); + LLVMValueRef args[1] = { decl->backend_ref }; + LLVMBuildCall2(builder, c->atexit_type, atexit, args, 1, ""); + LLVMBuildRetVoid(builder); + LLVMDisposeBuilder(builder); + llvm_append_xxlizer(c, decl->func_decl.priority, true, func); + } llvm_emit_body(c, decl->backend_ref, type_get_resolved_prototype(decl->type), diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 1f33f15bc..2c7e71fe6 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -97,6 +97,7 @@ typedef struct GenContext_ LLVMTypeRef bool_type; LLVMTypeRef byte_type; LLVMTypeRef introspect_type; + LLVMTypeRef atexit_type; LLVMTypeRef fault_type; LLVMTypeRef size_type; LLVMTypeRef typeid_type; diff --git a/src/compiler/llvm_codegen_module.c b/src/compiler/llvm_codegen_module.c index e855e101a..23c4150e9 100644 --- a/src/compiler/llvm_codegen_module.c +++ b/src/compiler/llvm_codegen_module.c @@ -131,13 +131,15 @@ void gencontext_begin_module(GenContext *c) c->ptr_type = LLVMPointerType(c->byte_type, 0); c->size_type = llvm_get_type(c, type_usz); c->typeid_type = llvm_get_type(c, type_typeid); + LLVMTypeRef void_type = LLVMVoidTypeInContext(c->context); LLVMTypeRef dtable_type[3] = { c->ptr_type, c->ptr_type, c->ptr_type }; c->dtable_type = LLVMStructTypeInContext(c->context, dtable_type, 3, false); c->chars_type = llvm_get_type(c, type_chars); LLVMTypeRef ctor_type[3] = { LLVMInt32TypeInContext(c->context), c->ptr_type, c->ptr_type }; c->xtor_entry_type = LLVMStructTypeInContext(c->context, ctor_type, 3, false); - c->xtor_func_type = LLVMFunctionType(LLVMVoidTypeInContext(c->context), NULL, 0, false); + c->xtor_func_type = LLVMFunctionType(void_type, NULL, 0, false); c->introspect_type = create_introspection_type(c); + c->atexit_type = LLVMFunctionType(void_type, &c->ptr_type, 1, false); c->fault_type = create_fault_type(c); c->memcmp_function = NULL; LLVMTypeRef memcmp_types[3] = {c->ptr_type, c->ptr_type, c->size_type }; diff --git a/test/test_suite/initialize/initialize_finalize.c3t b/test/test_suite/initialize/initialize_finalize.c3t index 9898ecf88..22b3334d6 100644 --- a/test/test_suite/initialize/initialize_finalize.c3t +++ b/test/test_suite/initialize/initialize_finalize.c3t @@ -31,8 +31,7 @@ fn void shutdown() @finalizer /* #expect: test.ll -@llvm.global_ctors = appending global [4 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 300, ptr @test.startup2, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @test.start_main, ptr null }, { i32, ptr, ptr } { i32 200, ptr @test.startup1, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @test.empty_startup, ptr null }] -@llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @test.shutdown, ptr null }] +@llvm.global_ctors = appending global [5 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 300, ptr @test.startup2, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @test.start_main, ptr null }, { i32, ptr, ptr } { i32 200, ptr @test.startup1, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @test.empty_startup, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @.__c3_atexit_test.shutdown, ptr null }] define void @test.startup2() #0 { entry: @@ -60,4 +59,10 @@ define void @test.shutdown() #0 { entry: call void @puts(ptr @.str.4) ret void +} + +define void @.__c3_atexit_test.shutdown() { +entry: + call void @atexit(ptr @test.shutdown) + ret void } \ No newline at end of file