From f670de9e806d4eaeac166f0f061e94e7342f308a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 3 Oct 2026 08:28:31 +0100 Subject: [PATCH] Define and initialize global variables in their own modules --- src/llvm_backend.cpp | 410 +++++++++--------- src/llvm_backend.hpp | 6 +- src/llvm_backend_general.cpp | 19 +- tests/issues/run.bat | 2 + tests/issues/run.sh | 2 + .../issues/test_issue_split_globals/main.odin | 47 ++ .../other/data/data.odin | 4 + .../test_issue_split_globals/other/other.odin | 36 ++ 8 files changed, 311 insertions(+), 215 deletions(-) create mode 100644 tests/issues/test_issue_split_globals/main.odin create mode 100644 tests/issues/test_issue_split_globals/other/data/data.odin create mode 100644 tests/issues/test_issue_split_globals/other/other.odin diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 031a71f3d..d39318215 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -192,7 +192,7 @@ gb_internal void lb_correct_entity_linkage(lbGenerator *gen) { LLVMValueRef other_global = nullptr; if (ec.e->kind == Entity_Variable) { other_global = LLVMGetNamedGlobal(ec.other_module->mod, ec.cname); - if (other_global && (LLVMGetInitializer(other_global) != nullptr || LLVMIsExternallyInitialized(other_global))) { + if (other_global && !LLVMIsDeclaration(other_global)) { LLVM_SET_INTERNAL_WEAK_LINKAGE(other_global); if (!ec.e->Variable.is_export && !ec.e->Variable.is_foreign) { LLVMSetVisibility(other_global, LLVMHiddenVisibility); @@ -2164,73 +2164,130 @@ gb_internal bool lb_init_global_var(lbModule *m, lbProcedure *p, Entity *e, Ast } -gb_internal void lb_create_startup_runtime_generate_body(lbModule *m, lbProcedure *p) { - lb_begin_procedure_body(p); - - if (p->objc_names) { - LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(m, p->objc_names->type), p->objc_names->value, nullptr, 0, ""); +gb_internal bool lb_global_variable_has_constant_init(Entity *e, DeclInfo *decl) { + TypeAndValue tav = type_and_value_of_expr(decl->init_expr); + if (!is_type_any(e->type) && tav.mode != Addressing_Invalid && tav.value.kind != ExactValue_Invalid) { + return true; } - Type *dummy_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin); - LLVMTypeRef raw_dummy_type = lb_type_internal_for_procedures_raw(m, dummy_type); - - for (auto &var : *p->global_variables) { - if (var.is_initialized) { - continue; - } - - lbModule *entity_module = m; - - Entity *e = var.decl->entity; - GB_ASSERT(e->kind == Entity_Variable); - e->code_gen_module = entity_module; - Ast *init_expr = var.decl->init_expr; - - if (init_expr == nullptr && var.init.value == nullptr) { - continue; - } - - if (false && type_size_of(e->type) > 8) { - String ename = lb_get_entity_name(m, e); - gbString name = gb_string_make(permanent_allocator(), ""); - name = gb_string_appendc(name, "__$startup$"); - name = gb_string_append_length(name, ename.text, ename.len); - - lbProcedure *dummy = lb_create_dummy_procedure(m, make_string_c(name), dummy_type); - dummy->is_startup = true; - LLVMSetVisibility(dummy->value, LLVMHiddenVisibility); - LLVM_SET_INTERNAL_WEAK_LINKAGE(p->value); - - lb_begin_procedure_body(dummy); - lb_init_global_var(m, dummy, e, init_expr, var); - lb_end_procedure_body(dummy); - - LLVMValueRef context_ptr = lb_find_or_generate_context_ptr(p).addr.value; - LLVMValueRef cast_ctx = LLVMBuildBitCast(p->builder, context_ptr, LLVMPointerType(LLVMInt8TypeInContext(m->ctx), 0), ""); - LLVMBuildCall2(p->builder, raw_dummy_type, dummy->value, &cast_ctx, 1, ""); - } else { - lb_init_global_var(m, p, e, init_expr, var); - } - - lbBlock *next = lb_create_block(p, "global.init", true); - lb_emit_jump(p, next); - lb_start_block(p, next); - } - CheckerInfo *info = m->gen->info; - - for (Entity *e : info->init_procedures) { - lbValue value = lb_find_procedure_value_from_entity(m, e); - lb_emit_call(p, value, {}, ProcInlining_none, ProcTailing_none); - } - - - lb_end_procedure_body(p); + return is_type_untyped_nil(tav.type); } +gb_internal void lb_create_global_variable(lbModule *m, lbGlobalVariable *var) { + DeclInfo *decl = var->decl; + Entity *e = decl->entity; -gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *objc_names, Array &global_variables) { // Startup Runtime + bool is_foreign = e->Variable.is_foreign; + bool is_export = e->Variable.is_export; + + String name = lb_get_entity_name(m, e); + + lbValue g = {}; + g.type = alloc_type_pointer(e->type); + g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name)); + + if (decl->init_expr != nullptr) { + TypeAndValue tav = type_and_value_of_expr(decl->init_expr); + if (!is_type_any(e->type)) { + if (tav.mode != Addressing_Invalid) { + if (tav.value.kind != ExactValue_Invalid) { + auto cc = LB_CONST_CONTEXT_DEFAULT; + cc.is_rodata = e->kind == Entity_Variable && e->Variable.is_rodata; + cc.allow_local = false; + cc.link_section = e->Variable.link_section; + + ExactValue v = tav.value; + lbValue init = lb_const_value(m, e->type, v, cc); + + LLVMDeleteGlobal(g.value); + g.value = nullptr; + g.value = LLVMAddGlobal(m->mod, LLVMTypeOf(init.value), alloc_cstring(permanent_allocator(), name)); + + LLVMSetInitializer(g.value, init.value); + var->is_initialized = true; + if (cc.is_rodata) { + LLVMSetGlobalConstant(g.value, true); + } + } + } + } + if (!var->is_initialized && is_type_untyped_nil(tav.type)) { + var->is_initialized = true; + if (e->kind == Entity_Variable && e->Variable.is_rodata) { + LLVMSetGlobalConstant(g.value, true); + } + } + } else if (e->kind == Entity_Variable && e->Variable.is_rodata) { + LLVMSetGlobalConstant(g.value, true); + } + + lb_apply_thread_local_model(g.value, e->Variable.thread_local_model); + + if (is_foreign) { + LLVMSetLinkage(g.value, LLVMExternalLinkage); + LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass); + LLVMSetExternallyInitialized(g.value, true); + lb_add_foreign_library_path(m, e->Variable.foreign_library); + } else if (LLVMGetInitializer(g.value) == nullptr) { + LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, e->type))); + } + if (is_export) { + LLVMSetLinkage(g.value, LLVMDLLExportLinkage); + LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass); + } else if (!is_foreign) { + LLVM_SET_INTERNAL_WEAK_LINKAGE(g.value); + } + lb_set_linkage_from_entity_flags(m, g.value, e->flags); + LLVMSetAlignment(g.value, cast(u32)type_align_of(e->type)); + + if (e->Variable.link_section.len > 0) { + LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section)); + } + if (e->flags & EntityFlag_Require) { + lb_append_to_compiler_used(m, g.value); + } + + if (m->debug_builder) { + String global_name = e->token.string; + if (global_name.len != 0 && global_name != "_") { + LLVMMetadataRef llvm_file = lb_get_file_metadata(m, e->file); + LLVMMetadataRef llvm_scope = llvm_file; + + LLVMBool local_to_unit = LLVMGetLinkage(g.value) == LLVMInternalLinkage; + + LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0); + LLVMMetadataRef llvm_decl = nullptr; + + u32 align_in_bits = cast(u32)(8*type_align_of(e->type)); + + LLVMMetadataRef global_variable_metadata = LLVMDIBuilderCreateGlobalVariableExpression( + m->debug_builder, llvm_scope, + cast(char const *)global_name.text, global_name.len, + "", 0, // linkage + llvm_file, e->token.pos.line, + lb_debug_type(m, e->type), + local_to_unit, + llvm_expr, + llvm_decl, + align_in_bits + ); + lb_set_llvm_metadata(m, g.value, global_variable_metadata); + LLVMGlobalSetMetadata(g.value, 0, global_variable_metadata); + } + } + + g.value = LLVMConstPointerCast(g.value, lb_type(m, alloc_type_pointer(e->type))); + var->var = g; + + lb_add_entity(m, e, g); + lb_add_member(m, name, g); + + GB_ASSERT(var->is_initialized == (decl->init_expr != nullptr && lb_global_variable_has_constant_init(e, decl))); +} + +gb_internal lbProcedure *lb_create_startup_procedure(lbModule *m, String const &name) { Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin); - lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type); + lbProcedure *p = lb_create_dummy_procedure(m, name, proc_type); p->is_startup = true; if (build_context.no_plt) { lb_add_attribute_to_proc(p->module, p->value, "nonlazybind"); @@ -2241,11 +2298,71 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc // Make sure shared libraries call their own runtime startup on Linux. LLVMSetVisibility(p->value, LLVMHiddenVisibility); LLVM_SET_INTERNAL_WEAK_LINKAGE(p->value); + return p; +} - p->global_variables = &global_variables; - p->objc_names = objc_names; +gb_internal void lb_global_init_procedure_generate_body(lbModule *m, lbProcedure *p) { + lb_begin_procedure_body(p); - lb_create_startup_runtime_generate_body(main_module, p); + for (lbGlobalVariable *var : p->global_variables) { + lb_init_global_var(m, p, var->decl->entity, var->decl->init_expr, *var); + + lbBlock *next = lb_create_block(p, "global.init", true); + lb_emit_jump(p, next); + lb_start_block(p, next); + } + + lb_end_procedure_body(p); +} + +// A global is initialized by the module it is defined in, so each run of the init order +// within one module gets its own procedure, and the startup calls them in that order. +gb_internal void lb_add_global_variable_to_initialize(lbModule *m, lbGlobalVariable *var) { + lbGenerator *gen = m->gen; + lbProcedure *p = nullptr; + if (gen->global_init_procedures.count != 0) { + p = gen->global_init_procedures[gen->global_init_procedures.count-1]; + } + if (p == nullptr || p->module != m) { + gbString name = gb_string_make(permanent_allocator(), LB_STARTUP_RUNTIME_PROC_NAME); + name = gb_string_append_fmt(name, "$%td", gen->global_init_procedures.count); + + p = lb_create_startup_procedure(m, make_string_c(name)); + array_init(&p->global_variables, heap_allocator()); + p->generate_body = lb_global_init_procedure_generate_body; + + string_map_set(&m->gen_procs, p->name, p); + mpsc_enqueue(&m->procedures_to_generate, p); + array_add(&gen->global_init_procedures, p); + } + array_add(&p->global_variables, var); +} + +gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *objc_names) { // Startup Runtime + lbProcedure *p = lb_create_startup_procedure(main_module, str_lit(LB_STARTUP_RUNTIME_PROC_NAME)); + p->objc_names = objc_names; + + lb_begin_procedure_body(p); + + if (p->objc_names) { + LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, p->objc_names->type), p->objc_names->value, nullptr, 0, ""); + } + + for (lbProcedure *init : main_module->gen->global_init_procedures) { + lbValue value = {init->value, init->type}; + if (init->module != main_module) { + lbProcedure *decl = lb_create_dummy_procedure(main_module, init->name, init->type); + value = {decl->value, decl->type}; + } + lb_emit_call(p, value, {}, ProcInlining_none, ProcTailing_none); + } + + for (Entity *e : main_module->gen->info->init_procedures) { + lbValue value = lb_find_procedure_value_from_entity(main_module, e); + lb_emit_call(p, value, {}, ProcInlining_none, ProcTailing_none); + } + + lb_end_procedure_body(p); return p; } @@ -2421,12 +2538,7 @@ gb_internal bool lb_is_module_empty(lbModule *m) { } for (auto g = LLVMGetFirstGlobal(m->mod); g != nullptr; g = LLVMGetNextGlobal(g)) { - LLVMLinkage linkage = LLVMGetLinkage(g); - if (linkage == LLVMExternalLinkage || - linkage == LLVMWeakAnyLinkage) { - continue; - } - if (!LLVMIsExternallyInitialized(g)) { + if (!LLVMIsDeclaration(g) && !LLVMIsExternallyInitialized(g)) { return false; } } @@ -2679,6 +2791,9 @@ gb_internal i64 lb_module_cost(lbModule *m) { gb_internal WORKER_TASK_PROC(lb_generate_procedures_worker_proc) { lbModule *m = cast(lbModule *)data; + for (lbGlobalVariable *var : m->global_variables) { + lb_create_global_variable(m, var); + } if (m == &m->gen->default_module) { lb_setup_type_info_data(m); } @@ -2692,7 +2807,7 @@ gb_internal void lb_generate_procedures(lbGenerator *gen, bool do_threading) { if (do_threading) { for (auto const &entry : gen->modules) { lbModule *m = entry.value; - m->estimated_cost = m->procedures_to_generate.count.load(std::memory_order_relaxed); + m->estimated_cost = m->procedures_to_generate.count.load(std::memory_order_relaxed) + m->global_variables.count; } gen->default_module.estimated_cost = I64_MAX; for (lbModule *m : lb_modules_by_cost(gen)) { @@ -3474,15 +3589,12 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { } - isize global_variable_max_count = 0; bool already_has_entry_point = false; for (Entity *e : info->entities) { String name = e->token.string; - if (e->kind == Entity_Variable) { - global_variable_max_count++; - } else if (e->kind == Entity_Procedure) { + if (e->kind == Entity_Procedure) { if ((e->scope->flags&ScopeFlag_Init) && name == "main") { GB_ASSERT(e == info->entry_point); } @@ -3504,8 +3616,8 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { } } - - auto global_variables = array_make(permanent_allocator(), 0, global_variable_max_count); + // `lb_setup_type_info_data` sets its initializer + Entity *type_table = scope_lookup_current(info->runtime_package->scope, string_interner_insert(str_lit("type_table"))); for (DeclInfo *d : info->variable_init_order) { Entity *e = d->entity; @@ -3524,141 +3636,19 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { } GB_ASSERT(e->kind == Entity_Variable); - - bool is_foreign = e->Variable.is_foreign; - bool is_export = e->Variable.is_export; - - lbModule *default_module = &gen->default_module; - - lbModule *m = default_module; - lbModule *e_module = lb_module_of_entity(gen, e, default_module); - - bool const split_globals_across_modules = false; - if (split_globals_across_modules) { - m = e_module; + lbModule *m = lb_module_of_entity(gen, e, default_module); + if (e == type_table) { + m = default_module; } + e->code_gen_module = m; - String name = lb_get_entity_name(m, e); + lbGlobalVariable *var = permanent_alloc_item(); + var->decl = decl; + array_add(&m->global_variables, var); - lbGlobalVariable var = {}; - var.decl = decl; - - lbValue g = {}; - g.type = alloc_type_pointer(e->type); - g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name)); - - if (decl->init_expr != nullptr) { - TypeAndValue tav = type_and_value_of_expr(decl->init_expr); - if (!is_type_any(e->type)) { - if (tav.mode != Addressing_Invalid) { - if (tav.value.kind != ExactValue_Invalid) { - auto cc = LB_CONST_CONTEXT_DEFAULT; - cc.is_rodata = e->kind == Entity_Variable && e->Variable.is_rodata; - cc.allow_local = false; - cc.link_section = e->Variable.link_section; - - ExactValue v = tav.value; - lbValue init = lb_const_value(m, e->type, v, cc); - - - LLVMDeleteGlobal(g.value); - g.value = nullptr; - g.value = LLVMAddGlobal(m->mod, LLVMTypeOf(init.value), alloc_cstring(permanent_allocator(), name)); - - LLVMSetInitializer(g.value, init.value); - var.is_initialized = true; - if (cc.is_rodata) { - LLVMSetGlobalConstant(g.value, true); - } - } - } - } - if (!var.is_initialized && is_type_untyped_nil(tav.type)) { - var.is_initialized = true; - if (e->kind == Entity_Variable && e->Variable.is_rodata) { - LLVMSetGlobalConstant(g.value, true); - } - } - } else if (e->kind == Entity_Variable && e->Variable.is_rodata) { - LLVMSetGlobalConstant(g.value, true); + if (decl->init_expr != nullptr && !lb_global_variable_has_constant_init(e, decl)) { + lb_add_global_variable_to_initialize(m, var); } - - - lb_apply_thread_local_model(g.value, e->Variable.thread_local_model); - - if (is_foreign) { - LLVMSetLinkage(g.value, LLVMExternalLinkage); - LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass); - LLVMSetExternallyInitialized(g.value, true); - lb_add_foreign_library_path(m, e->Variable.foreign_library); - } else if (LLVMGetInitializer(g.value) == nullptr) { - LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, e->type))); - } - if (is_export) { - LLVMSetLinkage(g.value, LLVMDLLExportLinkage); - LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass); - } else if (!is_foreign) { - LLVM_SET_INTERNAL_WEAK_LINKAGE(g.value); - } - lb_set_linkage_from_entity_flags(m, g.value, e->flags); - LLVMSetAlignment(g.value, cast(u32)type_align_of(e->type)); - - if (e->Variable.link_section.len > 0) { - LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section)); - } - if (e->flags & EntityFlag_Require) { - lb_append_to_compiler_used(m, g.value); - } - - if (m->debug_builder) { - String global_name = e->token.string; - if (global_name.len != 0 && global_name != "_") { - LLVMMetadataRef llvm_file = lb_get_file_metadata(m, e->file); - LLVMMetadataRef llvm_scope = llvm_file; - - LLVMBool local_to_unit = LLVMGetLinkage(g.value) == LLVMInternalLinkage; - - LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0); - LLVMMetadataRef llvm_decl = nullptr; - - u32 align_in_bits = cast(u32)(8*type_align_of(e->type)); - - LLVMMetadataRef global_variable_metadata = LLVMDIBuilderCreateGlobalVariableExpression( - m->debug_builder, llvm_scope, - cast(char const *)global_name.text, global_name.len, - "", 0, // linkage - llvm_file, e->token.pos.line, - lb_debug_type(m, e->type), - local_to_unit, - llvm_expr, - llvm_decl, - align_in_bits - ); - lb_set_llvm_metadata(m, g.value, global_variable_metadata); - LLVMGlobalSetMetadata(g.value, 0, global_variable_metadata); - } - } - - if (default_module == m) { - g.value = LLVMConstPointerCast(g.value, lb_type(m, alloc_type_pointer(e->type))); - - var.var = g; - array_add(&global_variables, var); - } else { - lbValue local_g = {}; - local_g.type = alloc_type_pointer(e->type); - local_g.value = LLVMAddGlobal(default_module->mod, lb_type(default_module, e->type), alloc_cstring(permanent_allocator(), name)); - LLVMSetLinkage(local_g.value, LLVMExternalLinkage); - - var.var = local_g; - array_add(&global_variables, var); - - lb_add_entity(default_module, e, local_g); - lb_add_member(default_module, name, local_g); - } - - lb_add_entity(m, e, g); - lb_add_member(m, name, g); } if (build_context.ODIN_DEBUG) { @@ -3687,7 +3677,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { gen->objc_names = lb_create_objc_names(default_module); TIME_SECTION("LLVM Runtime Startup Creation (Global Variables & @(init))"); - gen->startup_runtime = lb_create_startup_runtime(default_module, gen->objc_names, global_variables); + gen->startup_runtime = lb_create_startup_runtime(default_module, gen->objc_names); TIME_SECTION("LLVM Runtime Cleanup Creation & @(fini)"); gen->cleanup_runtime = lb_create_cleanup_runtime(default_module); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 02808aecc..ac2d452fe 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -24,6 +24,7 @@ #endif struct lbProcedure; +struct lbGlobalVariable; struct lbValue { LLVMValueRef value; @@ -164,6 +165,7 @@ struct lbModule { MPSCQueue procedures_to_generate; Array global_procedures_to_create; Array global_types_to_create; + Array global_variables; BlockingMutex generated_procedures_mutex; Array generated_procedures; @@ -227,6 +229,8 @@ struct lbGenerator : LinkerData { lbProcedure *cleanup_runtime; lbProcedure *objc_names; + Array global_init_procedures; + MPSCQueue entities_to_correct_linkage; MPSCQueue objc_selectors; MPSCQueue objc_classes; @@ -401,7 +405,7 @@ struct lbProcedure { Array lifetime_scopes; void (*generate_body)(lbModule *m, lbProcedure *p); - Array *global_variables; + Array global_variables; lbProcedure *objc_names; Type *internal_gen_type; // map_set, map_get, etc. diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 6b7fefcc6..417504376 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -164,6 +164,7 @@ gb_internal WORKER_TASK_PROC(lb_init_module_worker_proc) { array_init(&m->global_procedures_to_create, a, 0, 1024); array_init(&m->global_types_to_create, a, 0, 1024); + array_init(&m->global_variables, a); mpsc_init(&m->missing_procedures_to_check, a); map_init(&m->debug_values); @@ -194,18 +195,27 @@ gb_internal void lb_init_module(lbModule *m, bool do_threading) { gb_internal i64 lb_estimate_file_code_sizes(CheckerInfo *info, PtrMap *sizes) { i64 total = 0; for (Entity *e : info->entities) { - if (e->kind != Entity_Procedure || e->file == nullptr || e->min_dep_count.load(std::memory_order_relaxed) == 0) { + if (e->file == nullptr || e->min_dep_count.load(std::memory_order_relaxed) == 0) { continue; } if (e->scope == nullptr || (e->scope->flags & ScopeFlag_File) == 0) { continue; } DeclInfo *d = e->decl_info; - if (d == nullptr || d->proc_lit == nullptr || d->proc_lit->kind != Ast_ProcLit || d->proc_lit->ProcLit.body == nullptr) { + Ast *code = nullptr; + if (e->kind == Entity_Procedure) { + if (d != nullptr && d->proc_lit != nullptr && d->proc_lit->kind == Ast_ProcLit) { + code = d->proc_lit->ProcLit.body; + } + } else if (e->kind == Entity_Variable) { + if (d != nullptr) { + code = d->init_expr; + } + } + if (code == nullptr) { continue; } - Ast *body = d->proc_lit->ProcLit.body; - i64 size = gb_max(cast(i64)(ast_end_token(body).pos.offset - ast_token(body).pos.offset), 1); + i64 size = gb_max(cast(i64)(ast_end_token(code).pos.offset - ast_token(code).pos.offset), 1); i64 *found = map_get(sizes, e->file); if (found) { *found += size; @@ -426,6 +436,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { map_init(&gen->modules, gen->info->packages.count*2); map_init(&gen->modules_through_ctx, gen->info->packages.count*2); map_init(&gen->file_modules); + array_init(&gen->global_init_procedures, heap_allocator()); if (USE_SEPARATE_MODULES) { bool module_per_file = build_context.module_per_file && (build_context.optimization_level <= 0 || build_context.lto_kind != LTO_None); diff --git a/tests/issues/run.bat b/tests/issues/run.bat index a921c04b3..f7538ca21 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -93,6 +93,8 @@ clang -c ..\test_issue_sysv_abi.c -o test_issue_sysv_abi_c.o || exit /b ..\..\..\odin test ..\test_issue_procedure_of_specialized.odin %COMMON% || exit /b ..\..\..\odin test ..\test_issue_7587.odin %COMMON% || exit /b ..\..\..\odin run ..\test_issue_7596.odin %COMMON% || exit /b +..\..\..\odin test ..\test_issue_split_globals -define:ODIN_TEST_FANCY=false -vet -strict-style -ignore-unused-defineables || exit /b +..\..\..\odin test ..\test_issue_split_globals -define:ODIN_TEST_FANCY=false -vet -strict-style -ignore-unused-defineables -debug || exit /b @echo off diff --git a/tests/issues/run.sh b/tests/issues/run.sh index e7edc7d0e..523c52322 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -119,6 +119,8 @@ $ODIN test ../test_issue_7700.odin $COMMON $ODIN test ../test_issue_procedure_of_specialized.odin $COMMON $ODIN test ../test_issue_7587.odin $COMMON $ODIN run ../test_issue_7596.odin $COMMON +$ODIN test ../test_issue_split_globals -define:ODIN_TEST_FANCY=false -vet -strict-style -ignore-unused-defineables +$ODIN test ../test_issue_split_globals -define:ODIN_TEST_FANCY=false -vet -strict-style -ignore-unused-defineables -debug $ODIN test ../test_issue_7421.odin $COMMON if [[ $($ODIN check ../test_issue_7421_tagged_duplicate.odin $COMMON_CHECK 2>&1 >/dev/null | grep -c "Error: Duplicate case") -eq 1 ]]; then echo "SUCCESSFUL 1/1" diff --git a/tests/issues/test_issue_split_globals/main.odin b/tests/issues/test_issue_split_globals/main.odin new file mode 100644 index 000000000..4ac49658e --- /dev/null +++ b/tests/issues/test_issue_split_globals/main.odin @@ -0,0 +1,47 @@ +// Globals are defined and initialized in the module of their own package +package test_issue_split_globals + +import "core:testing" +import "other" +import "other/data" + +C := other.A + 10*other.B +D := other.next() + 0*C +E := data.PTR^ +F := other.callback() +G := other.P_ptr +H := &other.TABLE + +init_counter: int + +@(init) +record_counter :: proc "contextless" () { + init_counter = other.counter +} + +@(test) +test_split_globals :: proc(t: ^testing.T) { + values := [4]int{other.A, other.B, other.get_internal(), D} + seen: [5]bool + for v in values { + testing.expect(t, 1 <= v && v <= 4) + if 1 <= v && v <= 4 { + testing.expect(t, !seen[v]) + seen[v] = true + } + } + testing.expect(t, D > other.A && D > other.B) + testing.expect_value(t, C, other.A + 10*other.B) + testing.expect_value(t, other.counter, 4) + testing.expect_value(t, init_counter, 4) + + testing.expect_value(t, E, 20) + testing.expect_value(t, data.PTR, &data.VALUES[1]) + testing.expect_value(t, F, 7) + testing.expect_value(t, G, &other.P) + testing.expect_value(t, H^[0], &other.P) + testing.expect_value(t, other.boxed^, other.Point{3, 4}) + testing.expect_value(t, other.NAMES[2], "z") + testing.expect_value(t, other.RO[3], 4) + testing.expect_value(t, other.any_p.(other.Point), other.Point{1, 2}) +} diff --git a/tests/issues/test_issue_split_globals/other/data/data.odin b/tests/issues/test_issue_split_globals/other/data/data.odin new file mode 100644 index 000000000..eb00a93e8 --- /dev/null +++ b/tests/issues/test_issue_split_globals/other/data/data.odin @@ -0,0 +1,4 @@ +package data + +VALUES := [3]int{10, 20, 30} +PTR := &VALUES[1] diff --git a/tests/issues/test_issue_split_globals/other/other.odin b/tests/issues/test_issue_split_globals/other/other.odin new file mode 100644 index 000000000..79a7805f8 --- /dev/null +++ b/tests/issues/test_issue_split_globals/other/other.odin @@ -0,0 +1,36 @@ +package other + +counter: int + +next :: proc "contextless" () -> int { + counter += 1 + return counter +} + +A := next() +B := next() + +@(linkage="internal") +internal_value := next() + +get_internal :: proc "contextless" () -> int { + return internal_value +} + +Point :: struct { + x, y: int, +} + +P := Point{1, 2} +P_ptr := &P +TABLE := []^Point{&P} +boxed := &Point{3, 4} +NAMES := [3]string{"x", "y", "z"} +any_p: any = P + +@(rodata) +RO := [4]int{1, 2, 3, 4} + +callback := proc "contextless" () -> int { + return 7 +}