From 629777b988da50ab347b6309ac07821acfd1ee59 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 12:42:36 +0100 Subject: Multithread `check_update_dependency_tree_for_procedures` --- src/checker.cpp | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'src/checker.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index e5dda2aa7..c43175230 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -7025,6 +7025,7 @@ gb_internal void add_type_info_for_type_definitions(Checker *c) { } } +#if 0 gb_internal void check_walk_all_dependencies(DeclInfo *decl) { if (decl == nullptr) { return; @@ -7046,6 +7047,44 @@ gb_internal void check_update_dependency_tree_for_procedures(Checker *c) { check_walk_all_dependencies(decl); } } +#else +gb_internal void check_walk_all_dependencies(DeclInfo *decl); + +gb_internal WORKER_TASK_PROC(check_walk_all_dependencies_worker_proc) { + if (data == nullptr) { + return 0; + } + DeclInfo *decl = cast(DeclInfo *)data; + + for (DeclInfo *child = decl->next_child; child != nullptr; child = child->next_sibling) { + thread_pool_add_task(check_walk_all_dependencies_worker_proc, child); + check_walk_all_dependencies(child); + } + + add_deps_from_child_to_parent(decl); + return 0; +} + +gb_internal void check_walk_all_dependencies(DeclInfo *decl) { + if (decl != nullptr) { + thread_pool_add_task(check_walk_all_dependencies_worker_proc, decl); + } +} + +gb_internal void check_update_dependency_tree_for_procedures(Checker *c) { + mutex_lock(&c->nested_proc_lits_mutex); + for (DeclInfo *decl : c->nested_proc_lits) { + check_walk_all_dependencies(decl); + } + mutex_unlock(&c->nested_proc_lits_mutex); + for (Entity *e : c->info.entities) { + DeclInfo *decl = e->decl_info; + check_walk_all_dependencies(decl); + } + + thread_pool_wait(); +} +#endif gb_internal void check_parsed_files(Checker *c) { -- cgit v1.2.3 From 70d396c8ad7b79e3e3676cfbb732565653f0d627 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 13:26:07 +0100 Subject: Split type and inline cycles into separate loops --- src/checker.cpp | 41 ++++++++++++++++++++++++++--------------- src/checker.hpp | 3 +++ 2 files changed, 29 insertions(+), 15 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index c43175230..441c7fa6a 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2552,14 +2552,12 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { } } - gb_internal void add_dependency_to_set(Checker *c, Entity *entity) { if (entity == nullptr) { return; } CheckerInfo *info = &c->info; - auto *set = &info->minimum_dependency_set; if (entity->type != nullptr && is_type_polymorphic(entity->type)) { @@ -2569,8 +2567,11 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) { } } - if (ptr_set_update(set, entity)) { - return; + { + MUTEX_GUARD(&info->minimum_dependency_type_info_mutex); + if (ptr_set_update(&info->minimum_dependency_set, entity)) { + return; + } } DeclInfo *decl = decl_info_of_entity(entity); @@ -7173,25 +7174,35 @@ gb_internal void check_parsed_files(Checker *c) { } check_merge_queues_into_arrays(c); - TIME_SECTION("check for type cycles and inline cycles"); + TIME_SECTION("check for type cycles"); // NOTE(bill): Check for illegal cyclic type declarations for_array(i, c->info.definitions) { Entity *e = c->info.definitions[i]; - if (e->kind == Entity_TypeName && e->type != nullptr && is_type_typed(e->type)) { + if (e->kind != Entity_TypeName) { + continue; + } + if (e->type != nullptr && is_type_typed(e->type)) { if (e->TypeName.is_type_alias) { // Ignore for the time being } else { (void)type_align_of(e->type); } - } else if (e->kind == Entity_Procedure) { - DeclInfo *decl = e->decl_info; - ast_node(pl, ProcLit, decl->proc_lit); - if (pl->inlining == ProcInlining_inline) { - for (Entity *dep : decl->deps) { - if (dep == e) { - error(e->token, "Cannot inline recursive procedure '%.*s'", LIT(e->token.string)); - break; - } + } + } + + TIME_SECTION("check for inline cycles"); + for_array(i, c->info.definitions) { + Entity *e = c->info.definitions[i]; + if (e->kind != Entity_Procedure) { + continue; + } + DeclInfo *decl = e->decl_info; + ast_node(pl, ProcLit, decl->proc_lit); + if (pl->inlining == ProcInlining_inline) { + for (Entity *dep : decl->deps) { + if (dep == e) { + error(e->token, "Cannot inline recursive procedure '%.*s'", LIT(e->token.string)); + break; } } } diff --git a/src/checker.hpp b/src/checker.hpp index 58ac8beb5..1da46b74a 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -448,7 +448,10 @@ struct CheckerInfo { AstPackage * init_package; Scope * init_scope; Entity * entry_point; + + BlockingMutex minimum_dependency_set_mutex; PtrSet minimum_dependency_set; + BlockingMutex minimum_dependency_type_info_mutex; PtrMap min_dep_type_info_index_map; TypeSet min_dep_type_info_set; -- cgit v1.2.3 From 60684ff028fe4b0df1925d23d4ff05192f45faab Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 13:39:06 +0100 Subject: Multithread some of the min dep system --- src/checker.cpp | 111 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 96 insertions(+), 15 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 441c7fa6a..26430359c 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2377,8 +2377,11 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { return; } - if (type_set_update(&c->info.min_dep_type_info_set, t)) { - return; + { + MUTEX_GUARD(&c->info.minimum_dependency_type_info_mutex); + if (type_set_update(&c->info.min_dep_type_info_set, t)) { + return; + } } // Add nested types @@ -2604,6 +2607,82 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) { } } +struct AddDependecyToSetWorkerData { + Checker *c; + Entity *entity; +}; + +gb_internal void add_dependency_to_set_threaded(Checker *c, Entity *entity); + +gb_internal WORKER_TASK_PROC(add_dependency_to_set_worker) { + AddDependecyToSetWorkerData *wd = cast(AddDependecyToSetWorkerData *)data; + Checker *c = wd->c; + Entity *entity = wd->entity; + if (entity == nullptr) { + return 0; + } + + CheckerInfo *info = &c->info; + + if (entity->type != nullptr && + is_type_polymorphic(entity->type)) { + DeclInfo *decl = decl_info_of_entity(entity); + if (decl != nullptr && decl->gen_proc_type == nullptr) { + return 0; + } + } + + { + MUTEX_GUARD(&info->minimum_dependency_type_info_mutex); + if (ptr_set_update(&info->minimum_dependency_set, entity)) { + return 0; + } + } + + DeclInfo *decl = decl_info_of_entity(entity); + if (decl == nullptr) { + return 0; + } + for (TypeInfoPair const tt : decl->type_info_deps) { + add_min_dep_type_info(c, tt.type); + } + + for (Entity *e : decl->deps) { + add_dependency_to_set(c, e); + if (e->kind == Entity_Procedure && e->Procedure.is_foreign) { + Entity *fl = e->Procedure.foreign_library; + if (fl != nullptr) { + GB_ASSERT_MSG(fl->kind == Entity_LibraryName && + (fl->flags&EntityFlag_Used), + "%.*s", LIT(entity->token.string)); + add_dependency_to_set_threaded(c, fl); + } + } else if (e->kind == Entity_Variable && e->Variable.is_foreign) { + Entity *fl = e->Variable.foreign_library; + if (fl != nullptr) { + GB_ASSERT_MSG(fl->kind == Entity_LibraryName && + (fl->flags&EntityFlag_Used), + "%.*s", LIT(entity->token.string)); + add_dependency_to_set_threaded(c, fl); + } + } + } + return 0; +} + + +gb_internal void add_dependency_to_set_threaded(Checker *c, Entity *entity) { + if (entity == nullptr) { + return; + } + + AddDependecyToSetWorkerData *wd = gb_alloc_item(temporary_allocator(), AddDependecyToSetWorkerData); + wd->c = c; + wd->entity = entity; + thread_pool_add_task(add_dependency_to_set_worker, wd); +} + + gb_internal void force_add_dependency_entity(Checker *c, Scope *scope, String const &name) { Entity *e = scope_lookup(scope, name); if (e == nullptr) { @@ -2657,23 +2736,23 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st Entity *e = c->info.definitions[i]; if (e->scope == builtin_pkg->scope) { if (e->type == nullptr) { - add_dependency_to_set(c, e); + add_dependency_to_set_threaded(c, e); } } else if (e->kind == Entity_Procedure && e->Procedure.is_export) { - add_dependency_to_set(c, e); + add_dependency_to_set_threaded(c, e); } else if (e->kind == Entity_Variable && e->Variable.is_export) { - add_dependency_to_set(c, e); + add_dependency_to_set_threaded(c, e); } } for (Entity *e; mpsc_dequeue(&c->info.required_foreign_imports_through_force_queue, &e); /**/) { array_add(&c->info.required_foreign_imports_through_force, e); - add_dependency_to_set(c, e); + add_dependency_to_set_threaded(c, e); } for (Entity *e; mpsc_dequeue(&c->info.required_global_variable_queue, &e); /**/) { e->flags |= EntityFlag_Used; - add_dependency_to_set(c, e); + add_dependency_to_set_threaded(c, e); } for_array(i, c->info.entities) { @@ -2681,16 +2760,16 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st switch (e->kind) { case Entity_Variable: if (e->Variable.is_export) { - add_dependency_to_set(c, e); + add_dependency_to_set_threaded(c, e); } else if (e->flags & EntityFlag_Require) { - add_dependency_to_set(c, e); + add_dependency_to_set_threaded(c, e); } break; case Entity_Procedure: if (e->Procedure.is_export) { - add_dependency_to_set(c, e); + add_dependency_to_set_threaded(c, e); } else if (e->flags & EntityFlag_Require) { - add_dependency_to_set(c, e); + add_dependency_to_set_threaded(c, e); } if (e->flags & EntityFlag_Init) { Type *t = base_type(e->type); @@ -2730,7 +2809,7 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st if (is_init) { - add_dependency_to_set(c, e); + add_dependency_to_set_threaded(c, e); array_add(&c->info.init_procedures, e); } } else if (e->flags & EntityFlag_Fini) { @@ -2765,7 +2844,7 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st } if (is_fini) { - add_dependency_to_set(c, e); + add_dependency_to_set_threaded(c, e); array_add(&c->info.fini_procedures, e); } } @@ -2782,7 +2861,7 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st Entity *e = entry.value; if (e != nullptr) { e->flags |= EntityFlag_Used; - add_dependency_to_set(c, e); + add_dependency_to_set_threaded(c, e); } } @@ -2797,7 +2876,7 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st } } else if (start != nullptr) { start->flags |= EntityFlag_Used; - add_dependency_to_set(c, start); + add_dependency_to_set_threaded(c, start); } } @@ -2905,6 +2984,8 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) { generate_minimum_dependency_set_internal(c, start); + thread_pool_wait(); + #undef FORCE_ADD_RUNTIME_ENTITIES } -- cgit v1.2.3 From 1e0902677f905e752b42e2f48dcda53141b78eee Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 17:29:11 +0100 Subject: Multithread min dep set by removing the set itself --- src/checker.cpp | 193 ++++++++++++++++++++++-------------------- src/checker.hpp | 7 +- src/entity.cpp | 1 + src/error.cpp | 16 ++-- src/llvm_backend.cpp | 17 ++-- src/llvm_backend_proc.cpp | 3 +- src/llvm_backend_stmt.cpp | 6 +- src/main.cpp | 3 +- src/name_canonicalization.cpp | 61 ++++++++++++- src/name_canonicalization.hpp | 2 + src/ptr_set.cpp | 38 +++++++++ src/threading.cpp | 38 +++++++++ src/types.cpp | 1 + 13 files changed, 263 insertions(+), 123 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 26430359c..b3a702cbd 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1712,7 +1712,7 @@ gb_internal void check_remove_expr_info(CheckerContext *c, Ast *e) { } gb_internal isize type_info_index(CheckerInfo *info, TypeInfoPair pair, bool error_on_failure) { - mutex_lock(&info->minimum_dependency_type_info_mutex); + rw_mutex_shared_lock(&info->minimum_dependency_type_info_mutex); isize entry_index = -1; u64 hash = pair.hash; @@ -1720,7 +1720,7 @@ gb_internal isize type_info_index(CheckerInfo *info, TypeInfoPair pair, bool err if (found_entry_index) { entry_index = *found_entry_index; } - mutex_unlock(&info->minimum_dependency_type_info_mutex); + rw_mutex_shared_unlock(&info->minimum_dependency_type_info_mutex); if (error_on_failure && entry_index < 0) { compiler_error("Type_Info for '%s' could not be found", type_to_string(pair.type)); @@ -2377,11 +2377,8 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { return; } - { - MUTEX_GUARD(&c->info.minimum_dependency_type_info_mutex); - if (type_set_update(&c->info.min_dep_type_info_set, t)) { - return; - } + if (type_set_update_with_mutex(&c->info.min_dep_type_info_set, t, &c->info.min_dep_type_info_set_mutex)) { + return; } // Add nested types @@ -2555,13 +2552,15 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { } } +gb_internal void add_dependency_to_set_threaded(Checker *c, Entity *entity); + +gb_global std::atomic global_checker_ptr; + gb_internal void add_dependency_to_set(Checker *c, Entity *entity) { if (entity == nullptr) { return; } - CheckerInfo *info = &c->info; - if (entity->type != nullptr && is_type_polymorphic(entity->type)) { DeclInfo *decl = decl_info_of_entity(entity); @@ -2570,11 +2569,8 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) { } } - { - MUTEX_GUARD(&info->minimum_dependency_type_info_mutex); - if (ptr_set_update(&info->minimum_dependency_set, entity)) { - return; - } + if (entity->min_dep_count.fetch_add(1, std::memory_order_relaxed) > 0) { + return; } DeclInfo *decl = decl_info_of_entity(entity); @@ -2584,46 +2580,45 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) { for (TypeInfoPair const tt : decl->type_info_deps) { add_min_dep_type_info(c, tt.type); } - for (Entity *e : decl->deps) { - add_dependency_to_set(c, e); - if (e->kind == Entity_Procedure && e->Procedure.is_foreign) { - Entity *fl = e->Procedure.foreign_library; - if (fl != nullptr) { - GB_ASSERT_MSG(fl->kind == Entity_LibraryName && - (fl->flags&EntityFlag_Used), - "%.*s", LIT(entity->token.string)); - add_dependency_to_set(c, fl); + switch (e->kind) { + case Entity_Procedure: + if (e->Procedure.is_foreign) { + Entity *fl = e->Procedure.foreign_library; + if (fl != nullptr) { + GB_ASSERT_MSG(fl->kind == Entity_LibraryName && + (fl->flags&EntityFlag_Used), + "%.*s", LIT(entity->token.string)); + add_dependency_to_set(c, fl); + } } - } else if (e->kind == Entity_Variable && e->Variable.is_foreign) { - Entity *fl = e->Variable.foreign_library; - if (fl != nullptr) { - GB_ASSERT_MSG(fl->kind == Entity_LibraryName && - (fl->flags&EntityFlag_Used), - "%.*s", LIT(entity->token.string)); - add_dependency_to_set(c, fl); + break; + case Entity_Variable: + if (e->Variable.is_foreign) { + Entity *fl = e->Variable.foreign_library; + if (fl != nullptr) { + GB_ASSERT_MSG(fl->kind == Entity_LibraryName && + (fl->flags&EntityFlag_Used), + "%.*s", LIT(entity->token.string)); + add_dependency_to_set(c, fl); + } } + break; } } -} -struct AddDependecyToSetWorkerData { - Checker *c; - Entity *entity; -}; - -gb_internal void add_dependency_to_set_threaded(Checker *c, Entity *entity); + for (Entity *e : decl->deps) { + add_dependency_to_set(c, e); + } +} gb_internal WORKER_TASK_PROC(add_dependency_to_set_worker) { - AddDependecyToSetWorkerData *wd = cast(AddDependecyToSetWorkerData *)data; - Checker *c = wd->c; - Entity *entity = wd->entity; + Checker *c = global_checker_ptr.load(std::memory_order_relaxed); + Entity *entity = cast(Entity *)data; if (entity == nullptr) { return 0; } - CheckerInfo *info = &c->info; - if (entity->type != nullptr && is_type_polymorphic(entity->type)) { DeclInfo *decl = decl_info_of_entity(entity); @@ -2632,11 +2627,8 @@ gb_internal WORKER_TASK_PROC(add_dependency_to_set_worker) { } } - { - MUTEX_GUARD(&info->minimum_dependency_type_info_mutex); - if (ptr_set_update(&info->minimum_dependency_set, entity)) { - return 0; - } + if (entity->min_dep_count.fetch_add(1, std::memory_order_relaxed) > 0) { + return 0; } DeclInfo *decl = decl_info_of_entity(entity); @@ -2648,25 +2640,36 @@ gb_internal WORKER_TASK_PROC(add_dependency_to_set_worker) { } for (Entity *e : decl->deps) { - add_dependency_to_set(c, e); - if (e->kind == Entity_Procedure && e->Procedure.is_foreign) { - Entity *fl = e->Procedure.foreign_library; - if (fl != nullptr) { - GB_ASSERT_MSG(fl->kind == Entity_LibraryName && - (fl->flags&EntityFlag_Used), - "%.*s", LIT(entity->token.string)); - add_dependency_to_set_threaded(c, fl); + switch (e->kind) { + case Entity_Procedure: + if (e->Procedure.is_foreign) { + Entity *fl = e->Procedure.foreign_library; + if (fl != nullptr) { + GB_ASSERT_MSG(fl->kind == Entity_LibraryName && + (fl->flags&EntityFlag_Used), + "%.*s", LIT(entity->token.string)); + add_dependency_to_set_threaded(c, fl); + } } - } else if (e->kind == Entity_Variable && e->Variable.is_foreign) { - Entity *fl = e->Variable.foreign_library; - if (fl != nullptr) { - GB_ASSERT_MSG(fl->kind == Entity_LibraryName && - (fl->flags&EntityFlag_Used), - "%.*s", LIT(entity->token.string)); - add_dependency_to_set_threaded(c, fl); + break; + case Entity_Variable: + if (e->Variable.is_foreign) { + Entity *fl = e->Variable.foreign_library; + if (fl != nullptr) { + GB_ASSERT_MSG(fl->kind == Entity_LibraryName && + (fl->flags&EntityFlag_Used), + "%.*s", LIT(entity->token.string)); + add_dependency_to_set_threaded(c, fl); + } } + break; } } + + for (Entity *e : decl->deps) { + add_dependency_to_set_threaded(c, e); + } + return 0; } @@ -2675,11 +2678,7 @@ gb_internal void add_dependency_to_set_threaded(Checker *c, Entity *entity) { if (entity == nullptr) { return; } - - AddDependecyToSetWorkerData *wd = gb_alloc_item(temporary_allocator(), AddDependecyToSetWorkerData); - wd->c = c; - wd->entity = entity; - thread_pool_add_task(add_dependency_to_set_worker, wd); + thread_pool_add_task(add_dependency_to_set_worker, entity); } @@ -2732,27 +2731,35 @@ gb_internal void collect_testing_procedures_of_package(Checker *c, AstPackage *p } gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *start) { + // auto const &add_to_set = add_dependency_to_set; + auto const &add_to_set = add_dependency_to_set_threaded; + + Scope *builtin_scope = builtin_pkg->scope; for_array(i, c->info.definitions) { Entity *e = c->info.definitions[i]; - if (e->scope == builtin_pkg->scope) { + if (e->scope == builtin_scope) { if (e->type == nullptr) { - add_dependency_to_set_threaded(c, e); + add_to_set(c, e); + } + } else if (e->kind == Entity_Procedure) { + if (e->Procedure.is_export) { + add_to_set(c, e); + } + } else if (e->kind == Entity_Variable) { + if (e->Variable.is_export) { + add_to_set(c, e); } - } else if (e->kind == Entity_Procedure && e->Procedure.is_export) { - add_dependency_to_set_threaded(c, e); - } else if (e->kind == Entity_Variable && e->Variable.is_export) { - add_dependency_to_set_threaded(c, e); } } for (Entity *e; mpsc_dequeue(&c->info.required_foreign_imports_through_force_queue, &e); /**/) { array_add(&c->info.required_foreign_imports_through_force, e); - add_dependency_to_set_threaded(c, e); + add_to_set(c, e); } for (Entity *e; mpsc_dequeue(&c->info.required_global_variable_queue, &e); /**/) { e->flags |= EntityFlag_Used; - add_dependency_to_set_threaded(c, e); + add_to_set(c, e); } for_array(i, c->info.entities) { @@ -2760,16 +2767,16 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st switch (e->kind) { case Entity_Variable: if (e->Variable.is_export) { - add_dependency_to_set_threaded(c, e); + add_to_set(c, e); } else if (e->flags & EntityFlag_Require) { - add_dependency_to_set_threaded(c, e); + add_to_set(c, e); } break; case Entity_Procedure: if (e->Procedure.is_export) { - add_dependency_to_set_threaded(c, e); + add_to_set(c, e); } else if (e->flags & EntityFlag_Require) { - add_dependency_to_set_threaded(c, e); + add_to_set(c, e); } if (e->flags & EntityFlag_Init) { Type *t = base_type(e->type); @@ -2809,7 +2816,7 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st if (is_init) { - add_dependency_to_set_threaded(c, e); + add_to_set(c, e); array_add(&c->info.init_procedures, e); } } else if (e->flags & EntityFlag_Fini) { @@ -2844,7 +2851,7 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st } if (is_fini) { - add_dependency_to_set_threaded(c, e); + add_to_set(c, e); array_add(&c->info.fini_procedures, e); } } @@ -2861,7 +2868,7 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st Entity *e = entry.value; if (e != nullptr) { e->flags |= EntityFlag_Used; - add_dependency_to_set_threaded(c, e); + add_to_set(c, e); } } @@ -2876,16 +2883,11 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st } } else if (start != nullptr) { start->flags |= EntityFlag_Used; - add_dependency_to_set_threaded(c, start); + add_to_set(c, start); } } gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) { - isize entity_count = c->info.entities.count; - isize min_dep_set_cap = next_pow2_isize(entity_count*4); // empirically determined factor - - ptr_set_init(&c->info.minimum_dependency_set, min_dep_set_cap); - #define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \ if (condition) { \ String entities[] = {__VA_ARGS__}; \ @@ -6267,8 +6269,10 @@ gb_internal void check_unchecked_bodies(Checker *c) { // use the `procs_to_check` array global_procedure_body_in_worker_queue = false; - for (Entity *e : c->info.minimum_dependency_set) { - check_procedure_later_from_entity(c, e, "check_unchecked_bodies"); + for (Entity *e : c->info.entities) { + if (e->min_dep_count.load(std::memory_order_relaxed) > 0) { + check_procedure_later_from_entity(c, e, "check_unchecked_bodies"); + } } if (!global_procedure_body_in_worker_queue) { @@ -7042,6 +7046,7 @@ gb_internal void check_merge_queues_into_arrays(Checker *c) { } check_add_entities_from_queues(c); check_add_definitions_from_queues(c); + thread_pool_wait(); } gb_internal GB_COMPARE_PROC(init_procedures_cmp) { @@ -7100,7 +7105,7 @@ gb_internal void add_type_info_for_type_definitions(Checker *c) { Entity *e = c->info.definitions[i]; if (e->kind == Entity_TypeName && e->type != nullptr && is_type_typed(e->type)) { i64 align = type_align_of(e->type); - if (align > 0 && ptr_set_exists(&c->info.minimum_dependency_set, e)) { + if (align > 0 && e->min_dep_count.load(std::memory_order_relaxed) > 0) { add_type_info_type(&c->builtin_ctx, e->type); } } @@ -7170,6 +7175,8 @@ gb_internal void check_update_dependency_tree_for_procedures(Checker *c) { gb_internal void check_parsed_files(Checker *c) { + global_checker_ptr.store(c, std::memory_order_relaxed); + TIME_SECTION("map full filepaths to scope"); add_type_info_type(&c->builtin_ctx, t_invalid); @@ -7312,11 +7319,9 @@ gb_internal void check_parsed_files(Checker *c) { check_unchecked_bodies(c); TIME_SECTION("check #soa types"); - check_merge_queues_into_arrays(c); - thread_pool_wait(); - TIME_SECTION("update minimum dependency set"); + TIME_SECTION("update minimum dependency set again"); generate_minimum_dependency_set_internal(c, c->info.entry_point); // NOTE(laytan): has to be ran after generate_minimum_dependency_set, diff --git a/src/checker.hpp b/src/checker.hpp index 1da46b74a..8b4d61ee2 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -449,11 +449,10 @@ struct CheckerInfo { Scope * init_scope; Entity * entry_point; - BlockingMutex minimum_dependency_set_mutex; - PtrSet minimum_dependency_set; - - BlockingMutex minimum_dependency_type_info_mutex; + RwMutex minimum_dependency_type_info_mutex; PtrMap min_dep_type_info_index_map; + + RWSpinLock min_dep_type_info_set_mutex; TypeSet min_dep_type_info_set; Array type_info_types_hash_map; // 2 * type_info_types.count diff --git a/src/entity.cpp b/src/entity.cpp index 6c0aa6ace..5ca3fa916 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -164,6 +164,7 @@ struct Entity { u64 id; std::atomic flags; std::atomic state; + std::atomic min_dep_count; Token token; Scope * scope; Type * type; diff --git a/src/error.cpp b/src/error.cpp index 006d5ae8d..10bf1caf5 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -86,7 +86,7 @@ gb_internal char *token_pos_to_string(TokenPos const &pos); gb_internal bool set_file_path_string(i32 index, String const &path) { bool ok = false; GB_ASSERT(index >= 0); - mutex_lock(&global_error_collector.path_mutex); + // mutex_lock(&global_error_collector.path_mutex); mutex_lock(&global_files_mutex); if (index >= global_file_path_strings.count) { @@ -99,14 +99,14 @@ gb_internal bool set_file_path_string(i32 index, String const &path) { } mutex_unlock(&global_files_mutex); - mutex_unlock(&global_error_collector.path_mutex); + // mutex_unlock(&global_error_collector.path_mutex); return ok; } gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) { bool ok = false; GB_ASSERT(index >= 0); - mutex_lock(&global_error_collector.path_mutex); + // mutex_lock(&global_error_collector.path_mutex); mutex_lock(&global_files_mutex); if (index >= global_files.count) { @@ -118,13 +118,13 @@ gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) { ok = true; } mutex_unlock(&global_files_mutex); - mutex_unlock(&global_error_collector.path_mutex); + // mutex_unlock(&global_error_collector.path_mutex); return ok; } gb_internal String get_file_path_string(i32 index) { GB_ASSERT(index >= 0); - mutex_lock(&global_error_collector.path_mutex); + // mutex_lock(&global_error_collector.path_mutex); mutex_lock(&global_files_mutex); String path = {}; @@ -133,13 +133,13 @@ gb_internal String get_file_path_string(i32 index) { } mutex_unlock(&global_files_mutex); - mutex_unlock(&global_error_collector.path_mutex); + // mutex_unlock(&global_error_collector.path_mutex); return path; } gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) { GB_ASSERT(index >= 0); - mutex_lock(&global_error_collector.path_mutex); + // mutex_lock(&global_error_collector.path_mutex); mutex_lock(&global_files_mutex); AstFile *file = nullptr; @@ -148,7 +148,7 @@ gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) { } mutex_unlock(&global_files_mutex); - mutex_unlock(&global_error_collector.path_mutex); + // mutex_unlock(&global_error_collector.path_mutex); return file; } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index ff17e9c10..11b979774 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2097,8 +2097,6 @@ gb_internal GB_COMPARE_PROC(llvm_global_entity_cmp) { } gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, CheckerInfo *info, bool do_threading) { - auto *min_dep_set = &info->minimum_dependency_set; - for (Entity *e : info->entities) { String name = e->token.string; Scope * scope = e->scope; @@ -2135,11 +2133,16 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker } } - if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) { + if (!polymorphic_struct && e->min_dep_count.load(std::memory_order_relaxed) == 0) { // NOTE(bill): Nothing depends upon it so doesn't need to be built continue; } + // if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) { + // // NOTE(bill): Nothing depends upon it so doesn't need to be built + // continue; + // } + lbModule *m = &gen->default_module; if (USE_SEPARATE_MODULES) { m = lb_module_of_entity(gen, e); @@ -2845,8 +2848,6 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { lbModule *default_module = &gen->default_module; CheckerInfo *info = gen->info; - auto *min_dep_set = &info->minimum_dependency_set; - switch (build_context.metrics.arch) { case TargetArch_amd64: case TargetArch_i386: @@ -3184,10 +3185,14 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { continue; } - if (!ptr_set_exists(min_dep_set, e)) { + if (e->min_dep_count.load(std::memory_order_relaxed) == 0) { continue; } + // if (!ptr_set_exists(min_dep_set, e)) { + // continue; + // } + DeclInfo *decl = decl_info_of_entity(e); if (decl == nullptr) { continue; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index f2e6662c8..06829efab 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -798,9 +798,8 @@ gb_internal void lb_end_procedure_body(lbProcedure *p) { gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e) { GB_ASSERT(pd->body != nullptr); lbModule *m = p->module; - auto *min_dep_set = &m->info->minimum_dependency_set; - if (ptr_set_exists(min_dep_set, e) == false) { + if (e->min_dep_count.load(std::memory_order_relaxed) == 0) { // NOTE(bill): Nothing depends upon it so doesn't need to be built return; } diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 5481ca447..590920b59 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -3,8 +3,6 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) return; } - auto *min_dep_set = &p->module->info->minimum_dependency_set; - for (Ast *ident : vd->names) { GB_ASSERT(ident->kind == Ast_Ident); Entity *e = entity_of_node(ident); @@ -21,7 +19,7 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) } } - if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) { + if (!polymorphic_struct && e->min_dep_count.load(std::memory_order_relaxed) == 0) { continue; } @@ -56,7 +54,7 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) if (gpd) { rw_mutex_shared_lock(&gpd->mutex); for (Entity *e : gpd->procs) { - if (!ptr_set_exists(min_dep_set, e)) { + if (e->min_dep_count.load(std::memory_order_relaxed) == 0) { continue; } DeclInfo *d = decl_info_of_entity(e); diff --git a/src/main.cpp b/src/main.cpp index db4dee080..184b1eaac 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3050,7 +3050,8 @@ gb_internal void print_show_unused(Checker *c) { if (e->token.string == "_") { continue; } - if (ptr_set_exists(&info->minimum_dependency_set, e)) { + + if (e->min_dep_count.load(std::memory_order_relaxed) > 0) { continue; } array_add(&unused, e); diff --git a/src/name_canonicalization.cpp b/src/name_canonicalization.cpp index 6a4538e26..8bacfabc6 100644 --- a/src/name_canonicalization.cpp +++ b/src/name_canonicalization.cpp @@ -57,10 +57,13 @@ gb_internal isize type_set__find(TypeSet *s, TypeInfoPair pair) { usize mask = s->capacity-1; usize hash_index = cast(usize)hash & mask; for (usize i = 0; i < s->capacity; i++) { - Type *key = s->keys[hash_index].type; - if (are_types_identical_unique_tuples(key, pair.type)) { + auto *e = &s->keys[hash_index]; + u64 hash = e->hash; + Type *key = e->type; + if (hash == pair.hash && + are_types_identical_unique_tuples(key, pair.type)) { return hash_index; - } else if (key == 0) { + } else if (key == nullptr) { return -1; } hash_index = (hash_index+1)&mask; @@ -164,6 +167,48 @@ gb_internal bool type_set_update(TypeSet *s, Type *ptr) { // returns true if it return type_set_update(s, pair); } +gb_internal bool type_set_update_with_mutex(TypeSet *s, TypeInfoPair pair, RWSpinLock *m) { // returns true if it previously existsed + rwlock_acquire_upgrade(m); + if (type_set_exists(s, pair)) { + rwlock_release_upgrade(m); + return true; + } + + rwlock_release_upgrade_and_acquire_write(m); + defer (rwlock_release_write(m)); + + if (s->keys == nullptr) { + type_set_init(s); + } else if (type_set__full(s)) { + type_set_grow(s); + } + GB_ASSERT(s->count < s->capacity); + GB_ASSERT(s->capacity >= 0); + + usize mask = s->capacity-1; + usize hash = cast(usize)pair.hash; + usize hash_index = (cast(usize)hash) & mask; + GB_ASSERT(hash_index < s->capacity); + for (usize i = 0; i < s->capacity; i++) { + TypeInfoPair *key = &s->keys[hash_index]; + GB_ASSERT(!are_types_identical_unique_tuples(key->type, pair.type)); + if (key->hash == TYPE_SET_TOMBSTONE || key->hash == 0) { + *key = pair; + s->count++; + return false; + } + hash_index = (hash_index+1)&mask; + } + + GB_PANIC("ptr set out of memory"); + return false; +} + +gb_internal bool type_set_update_with_mutex(TypeSet *s, Type *ptr, RWSpinLock *m) { // returns true if it previously existsed + TypeInfoPair pair = {ptr, type_hash_canonical_type(ptr)}; + return type_set_update_with_mutex(s, pair, m); +} + gb_internal Type *type_set_add(TypeSet *s, Type *ptr) { type_set_update(s, ptr); @@ -328,12 +373,20 @@ gb_internal u64 type_hash_canonical_type(Type *type) { if (type == nullptr) { return 0; } + u64 prev_hash = type->canonical_hash.load(std::memory_order_relaxed); + if (prev_hash != 0) { + return prev_hash; + } + u64 hash = fnv64a(nullptr, 0); TypeWriter w = {}; type_writer_make_hasher(&w, &hash); write_type_to_canonical_string(&w, type); + hash = hash ? hash : 1; + + type->canonical_hash.store(hash, std::memory_order_relaxed); - return hash ? hash : 1; + return hash; } gb_internal String type_to_canonical_string(gbAllocator allocator, Type *type) { diff --git a/src/name_canonicalization.hpp b/src/name_canonicalization.hpp index 304aff42e..00b450fbe 100644 --- a/src/name_canonicalization.hpp +++ b/src/name_canonicalization.hpp @@ -102,6 +102,8 @@ gb_internal Type *type_set_add (TypeSet *s, Type *ptr); gb_internal Type *type_set_add (TypeSet *s, TypeInfoPair pair); gb_internal bool type_set_update (TypeSet *s, Type *ptr); // returns true if it previously existed gb_internal bool type_set_update (TypeSet *s, TypeInfoPair pair); // returns true if it previously existed +gb_internal bool type_set_update_with_mutex(TypeSet *s, TypeInfoPair pair, RWSpinLock *m); +gb_internal bool type_set_update_with_mutex(TypeSet *s, Type *ptr, RWSpinLock *m); gb_internal bool type_set_exists (TypeSet *s, Type *ptr); gb_internal void type_set_remove (TypeSet *s, Type *ptr); gb_internal void type_set_clear (TypeSet *s); diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp index 5097e2bb6..06c1e4a58 100644 --- a/src/ptr_set.cpp +++ b/src/ptr_set.cpp @@ -134,6 +134,44 @@ gb_internal bool ptr_set_update(PtrSet *s, T ptr) { // returns true if it pre return false; } +template +gb_internal bool ptr_set_update_with_mutex(PtrSet *s, T ptr, RWSpinLock *m) { // returns true if it previously existsed + rwlock_acquire_upgrade(m); + if (ptr_set_exists(s, ptr)) { + rwlock_release_upgrade(m); + return true; + } + + rwlock_release_upgrade_and_acquire_write(m); + defer (rwlock_release_write(m)); + + if (s->keys == nullptr) { + ptr_set_init(s); + } else if (ptr_set__full(s)) { + ptr_set_grow(s); + } + GB_ASSERT(s->count < s->capacity); + GB_ASSERT(s->capacity >= 0); + + usize mask = s->capacity-1; + u32 hash = ptr_map_hash_key(ptr); + usize hash_index = (cast(usize)hash) & mask; + GB_ASSERT(hash_index < s->capacity); + for (usize i = 0; i < s->capacity; i++) { + T *key = &s->keys[hash_index]; + GB_ASSERT(*key != ptr); + if (*key == (T)PtrSet::TOMBSTONE || *key == 0) { + *key = ptr; + s->count++; + return false; + } + hash_index = (hash_index+1)&mask; + } + + GB_PANIC("ptr set out of memory"); + return false; +} + template gb_internal T ptr_set_add(PtrSet *s, T ptr) { ptr_set_update(s, ptr); diff --git a/src/threading.cpp b/src/threading.cpp index a0d1c4049..f1d9264e3 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -448,6 +448,44 @@ gb_internal void semaphore_wait(Semaphore *s) { } #endif +static const int RWLOCK_WRITER = 1; +static const int RWLOCK_UPGRADED = 2; +static const int RWLOCK_READER = 4; +struct RWSpinLock { + Futex bits; +}; + +void rwlock_release_write(RWSpinLock *l) { + l->bits.fetch_and(~(RWLOCK_WRITER | RWLOCK_UPGRADED), std::memory_order_release); + futex_signal(&l->bits); +} + +bool rwlock_try_acquire_upgrade(RWSpinLock *l) { + int value = l->bits.fetch_or(RWLOCK_UPGRADED, std::memory_order_acquire); + return (value & (RWLOCK_UPGRADED | RWLOCK_WRITER)) == 0; +} + +void rwlock_acquire_upgrade(RWSpinLock *l) { + while (!rwlock_try_acquire_upgrade(l)) { + futex_wait(&l->bits, RWLOCK_UPGRADED); + } +} +void rwlock_release_upgrade(RWSpinLock *l) { + l->bits.fetch_add(-RWLOCK_UPGRADED, std::memory_order_acq_rel); +} + +bool rwlock_try_release_upgrade_and_acquire_write(RWSpinLock *l) { + int expect = RWLOCK_UPGRADED; + return l->bits.compare_exchange_strong(expect, RWLOCK_WRITER, std::memory_order_acq_rel); +} + +void rwlock_release_upgrade_and_acquire_write(RWSpinLock *l) { + while (!rwlock_try_release_upgrade_and_acquire_write(l)) { + futex_wait(&l->bits, RWLOCK_WRITER); + } +} + + struct Parker { Futex state; }; diff --git a/src/types.cpp b/src/types.cpp index 6b94b1690..44f9394c7 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -334,6 +334,7 @@ struct Type { // NOTE(bill): These need to be at the end to not affect the unionized data std::atomic cached_size; std::atomic cached_align; + std::atomic canonical_hash; std::atomic flags; // TypeFlag bool failure; }; -- cgit v1.2.3 From af37ba76c1b50c6f4fbe7045446ca645ed604d3e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 18:02:02 +0100 Subject: Use arena in `calculate_global_init_order` --- src/checker.cpp | 132 ++++++++++++++++++++++++++++++-------------------- src/common_memory.cpp | 7 +++ 2 files changed, 86 insertions(+), 53 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index b3a702cbd..0b54a3bbb 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3006,22 +3006,37 @@ gb_internal bool is_entity_a_dependency(Entity *e) { return false; } -gb_internal Array generate_entity_dependency_graph(CheckerInfo *info, gbAllocator allocator) { - PtrMap M = {}; - map_init(&M, info->entities.count); - defer (map_destroy(&M)); +gb_internal Array generate_entity_dependency_graph(CheckerInfo *info, Arena *arena) { + PtrMap M_procs = {}; + PtrMap M_vars = {}; + PtrMap M_other = {}; + + map_init(&M_procs, info->entities.count); + defer (map_destroy(&M_procs)); + + map_init(&M_vars, info->entities.count); + defer (map_destroy(&M_vars)); + + map_init(&M_other, info->entities.count); + defer (map_destroy(&M_other)); + for_array(i, info->entities) { Entity *e = info->entities[i]; - if (is_entity_a_dependency(e)) { - EntityGraphNode *n = gb_alloc_item(allocator, EntityGraphNode); - n->entity = e; - map_set(&M, e, n); + if (!is_entity_a_dependency(e)) { + continue; + } + EntityGraphNode *n = arena_alloc_item(arena); + n->entity = e; + switch (e->kind) { + case Entity_Procedure: map_set(&M_procs, e, n); break; + case Entity_Variable: map_set(&M_vars, e, n); break; + default: map_set(&M_other, e, n); break; } } TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 1"); // Calculate edges for graph M - for (auto const &entry : M) { + for (auto const &entry : M_procs) { EntityGraphNode *n = entry.value; Entity *e = n->entity; @@ -3033,56 +3048,70 @@ gb_internal Array generate_entity_dependency_graph(CheckerInf continue; } GB_ASSERT(dep != nullptr); - if (is_entity_a_dependency(dep)) { - EntityGraphNode *m = map_must_get(&M, dep); - entity_graph_node_set_add(&n->succ, m); - entity_graph_node_set_add(&m->pred, n); + if (!is_entity_a_dependency(dep)) { + continue; } + EntityGraphNode *m = nullptr; + + switch (dep->kind) { + case Entity_Procedure: m = map_must_get(&M_procs, dep); break; + case Entity_Variable: m = map_must_get(&M_vars, dep); break; + default: m = map_must_get(&M_other, dep); break; + } + entity_graph_node_set_add(&n->succ, m); + entity_graph_node_set_add(&m->pred, n); } } - TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2"); - auto G = array_make(allocator, 0, M.count); + TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2a (init)"); + + auto G = array_make(arena_allocator(arena), 0, M_procs.count + M_vars.count + M_other.count); - for (auto const &m_entry : M) { - auto *e = m_entry.key; + TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2b (procs)"); + + for (auto const &m_entry : M_procs) { EntityGraphNode *n = m_entry.value; - if (e->kind == Entity_Procedure) { - // Connect each pred 'p' of 'n' with each succ 's' and from - // the procedure node - for (EntityGraphNode *p : n->pred) { + // Connect each pred 'p' of 'n' with each succ 's' and from + // the procedure node + for (EntityGraphNode *p : n->pred) { + // Ignore self-cycles + if (p == n) { + continue; + } + // Each succ 's' of 'n' becomes a succ of 'p', and + // each pred 'p' of 'n' becomes a pred of 's' + for (EntityGraphNode *s : n->succ) { // Ignore self-cycles - if (p != n) { - // Each succ 's' of 'n' becomes a succ of 'p', and - // each pred 'p' of 'n' becomes a pred of 's' - for (EntityGraphNode *s : n->succ) { - // Ignore self-cycles - if (s != n) { - if (p->entity->kind == Entity_Procedure && - s->entity->kind == Entity_Procedure) { - // NOTE(bill, 2020-11-15): Only care about variable initialization ordering - // TODO(bill): This is probably wrong!!!! - continue; - } - // IMPORTANT NOTE/TODO(bill, 2020-11-15): These three calls take the majority of the - // the time to process - entity_graph_node_set_add(&p->succ, s); - entity_graph_node_set_add(&s->pred, p); - // Remove edge to 'n' - entity_graph_node_set_remove(&s->pred, n); - } - } - - // Remove edge to 'n' - entity_graph_node_set_remove(&p->succ, n); + if (s == n) { + continue; + } + if (p->entity->kind == Entity_Procedure && + s->entity->kind == Entity_Procedure) { + // NOTE(bill, 2020-11-15): Only care about variable initialization ordering + // TODO(bill): This is probably wrong!!!! + continue; } + // IMPORTANT NOTE/TODO(bill, 2020-11-15): These three calls take the majority of the + // the time to process + entity_graph_node_set_add(&p->succ, s); + entity_graph_node_set_add(&s->pred, p); + // Remove edge to 'n' + entity_graph_node_set_remove(&s->pred, n); } - } else if (e->kind == Entity_Variable) { - array_add(&G, n); + + // Remove edge to 'n' + entity_graph_node_set_remove(&p->succ, n); } } + TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2c (vars)"); + + for (auto const &m_entry : M_vars) { + EntityGraphNode *n = m_entry.value; + array_add(&G, n); + } + TIME_SECTION("generate_entity_dependency_graph: Dependency Count Checker"); for_array(i, G) { EntityGraphNode *n = G[i]; @@ -6012,13 +6041,10 @@ gb_internal void calculate_global_init_order(Checker *c) { CheckerInfo *info = &c->info; TIME_SECTION("calculate_global_init_order: generate entity dependency graph"); - Array dep_graph = generate_entity_dependency_graph(info, heap_allocator()); - defer ({ - for_array(i, dep_graph) { - entity_graph_node_destroy(dep_graph[i], heap_allocator()); - } - array_free(&dep_graph); - }); + Arena *arena = get_arena(ThreadArena_Temporary); + ArenaTempGuard arena_guard(arena); + + Array dep_graph = generate_entity_dependency_graph(info, arena); TIME_SECTION("calculate_global_init_order: priority queue create"); // NOTE(bill): Priority queue diff --git a/src/common_memory.cpp b/src/common_memory.cpp index 47b2796a9..9e0222bc4 100644 --- a/src/common_memory.cpp +++ b/src/common_memory.cpp @@ -113,6 +113,13 @@ gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment) { return ptr; } + +template +gb_internal T *arena_alloc_item(Arena *arena) { + return cast(T *)arena_alloc(arena, gb_size_of(T), gb_align_of(T)); +} + + gb_internal void arena_free_all(Arena *arena) { while (arena->curr_block != nullptr) { MemoryBlock *free_block = arena->curr_block; -- cgit v1.2.3 From 21b1173076cec12f97c5779556509ef1b908c644 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 18:20:20 +0100 Subject: Minor clean up of permanent/temporary arena usage --- src/build_settings.cpp | 10 +++++----- src/bundle_command.cpp | 2 +- src/check_builtin.cpp | 26 +++++++++++++------------- src/check_expr.cpp | 15 +++++++-------- src/check_stmt.cpp | 2 +- src/check_type.cpp | 24 ++++++++++++------------ src/checker.cpp | 6 +++--- src/common_memory.cpp | 44 +++++++++++++++++++++++++++++++++++++++++++- src/gb/gb.h | 24 +++++++++++++----------- 9 files changed, 98 insertions(+), 55 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 4bee0ad4e..0081fabee 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1126,7 +1126,7 @@ gb_internal String internal_odin_root_dir(void) { mutex_lock(&string_buffer_mutex); defer (mutex_unlock(&string_buffer_mutex)); - text = gb_alloc_array(permanent_allocator(), wchar_t, len+1); + text = permanent_alloc_array(len+1); GetModuleFileNameW(nullptr, text, cast(int)len); path = string16_to_string(heap_allocator(), make_string16(cast(u16 *)text, len)); @@ -1181,7 +1181,7 @@ gb_internal String internal_odin_root_dir(void) { mutex_lock(&string_buffer_mutex); defer (mutex_unlock(&string_buffer_mutex)); - text = gb_alloc_array(permanent_allocator(), u8, len + 1); + text = permanent_alloc_array(len + 1); gb_memmove(text, &path_buf[0], len); path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr); @@ -1233,7 +1233,7 @@ gb_internal String internal_odin_root_dir(void) { mutex_lock(&string_buffer_mutex); defer (mutex_unlock(&string_buffer_mutex)); - text = gb_alloc_array(permanent_allocator(), u8, len + 1); + text = permanent_alloc_array(len + 1); gb_memmove(text, &path_buf[0], len); path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr); @@ -1394,7 +1394,7 @@ gb_internal String internal_odin_root_dir(void) { mutex_lock(&string_buffer_mutex); defer (mutex_unlock(&string_buffer_mutex)); - text = gb_alloc_array(permanent_allocator(), u8, len + 1); + text = permanent_alloc_array(len + 1); gb_memmove(text, &path_buf[0], len); @@ -1429,7 +1429,7 @@ gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_) { len = GetFullPathNameW(cast(wchar_t *)&string16[0], 0, nullptr, nullptr); if (len != 0) { - wchar_t *text = gb_alloc_array(permanent_allocator(), wchar_t, len+1); + wchar_t *text = permanent_alloc_array(len+1); GetFullPathNameW(cast(wchar_t *)&string16[0], len, text, nullptr); mutex_unlock(&fullpath_mutex); diff --git a/src/bundle_command.cpp b/src/bundle_command.cpp index cd0cd589f..7abd48104 100644 --- a/src/bundle_command.cpp +++ b/src/bundle_command.cpp @@ -83,7 +83,7 @@ i32 bundle_android(String original_init_directory) { return 1; } - int *dir_numbers = gb_alloc_array(temporary_allocator(), int, possible_valid_dirs.count); + int *dir_numbers = temporary_alloc_array(possible_valid_dirs.count); char buf[1024] = {}; for_array(i, possible_valid_dirs) { diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index a08382c9a..7e1567750 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -354,7 +354,7 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan } isize const arg_offset = 1; - auto param_types = slice_make(permanent_allocator(), ce->args.count-arg_offset); + auto param_types = permanent_slice_make(ce->args.count-arg_offset); param_types[0] = t_objc_id; param_types[1] = sel_type; @@ -462,7 +462,7 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan { // NOTE(harold): The last argument specified in the call is the handler proc, // any other arguments before it are capture by-copy arguments. - auto param_operands = slice_make(permanent_allocator(), ce->args.count); + auto param_operands = permanent_slice_make(ce->args.count); isize capture_arg_count = ce->args.count - 1; @@ -3554,7 +3554,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } case BuiltinProc_compress_values: { - Operand *ops = gb_alloc_array(temporary_allocator(), Operand, ce->args.count); + Operand *ops = temporary_alloc_array(ce->args.count); isize value_count = 0; @@ -3685,9 +3685,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As operand->mode = Addressing_Value; } else { Type *st = alloc_type_struct_complete(); - st->Struct.fields = slice_make(permanent_allocator(), value_count); - st->Struct.tags = gb_alloc_array(permanent_allocator(), String, value_count); - st->Struct.offsets = gb_alloc_array(permanent_allocator(), i64, value_count); + st->Struct.fields = permanent_slice_make(value_count); + st->Struct.tags = permanent_alloc_array(value_count); + st->Struct.offsets = permanent_alloc_array(value_count); Scope *scope = create_scope(c->info, nullptr); @@ -4387,7 +4387,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As elem = alloc_type_struct(); elem->Struct.scope = s; elem->Struct.fields = slice_from_array(fields); - elem->Struct.tags = gb_alloc_array(permanent_allocator(), String, fields.count); + elem->Struct.tags = permanent_alloc_array(fields.count); elem->Struct.node = dummy_node_struct; type_set_offsets(elem); wait_signal_set(&elem->Struct.fields_wait_signal); @@ -4419,7 +4419,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As gb_string_free(s); return false; } - auto types = slice_make(permanent_allocator(), t->Struct.fields.count-1); + auto types = permanent_slice_make(t->Struct.fields.count-1); for_array(i, types) { Entity *f = t->Struct.fields[i]; GB_ASSERT(f->type->kind == Type_MultiPointer); @@ -4755,8 +4755,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (is_type_array(elem)) { Type *old_array = base_type(elem); soa_struct = alloc_type_struct(); - soa_struct->Struct.fields = slice_make(heap_allocator(), cast(isize)old_array->Array.count); - soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, cast(isize)old_array->Array.count); + soa_struct->Struct.fields = permanent_slice_make(cast(isize)old_array->Array.count); + soa_struct->Struct.tags = permanent_alloc_array(cast(isize)old_array->Array.count); soa_struct->Struct.node = operand->expr; soa_struct->Struct.soa_kind = StructSoa_Fixed; soa_struct->Struct.soa_elem = elem; @@ -4788,8 +4788,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As Type *old_struct = base_type(elem); soa_struct = alloc_type_struct(); - soa_struct->Struct.fields = slice_make(heap_allocator(), old_struct->Struct.fields.count); - soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, old_struct->Struct.fields.count); + soa_struct->Struct.fields = permanent_slice_make(old_struct->Struct.fields.count); + soa_struct->Struct.tags = permanent_alloc_array(old_struct->Struct.fields.count); soa_struct->Struct.node = operand->expr; soa_struct->Struct.soa_kind = StructSoa_Fixed; soa_struct->Struct.soa_elem = elem; @@ -6181,7 +6181,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } Type *new_type = alloc_type_union(); - auto variants = slice_make(permanent_allocator(), bt->Union.variants.count); + auto variants = permanent_slice_make(bt->Union.variants.count); for_array(i, bt->Union.variants) { variants[i] = alloc_type_pointer(bt->Union.variants[i]); } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e005b0bd0..84f1c6f0a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4639,7 +4639,6 @@ gb_internal ExactValue convert_exact_value_for_type(ExactValue v, Type *type) { } gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { - // GB_ASSERT_NOT_NULL(target_type); if (target_type == nullptr || operand->mode == Addressing_Invalid || operand->mode == Addressing_Type || is_type_typed(operand->type) || @@ -4810,7 +4809,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar TEMPORARY_ALLOCATOR_GUARD(); isize count = t->Union.variants.count; - ValidIndexAndScore *valids = gb_alloc_array(temporary_allocator(), ValidIndexAndScore, count); + ValidIndexAndScore *valids = temporary_alloc_array(count); isize valid_count = 0; isize first_success_index = -1; for_array(i, t->Union.variants) { @@ -6257,7 +6256,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } GB_ASSERT(ce->split_args); - auto visited = slice_make(temporary_allocator(), pt->param_count); + auto visited = temporary_slice_make(pt->param_count); auto ordered_operands = array_make(temporary_allocator(), pt->param_count); defer ({ for (Operand const &o : ordered_operands) { @@ -7254,7 +7253,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, } // Try to reduce the list further for `$T: typeid` like parameters - bool *possibly_ignore = gb_alloc_array(temporary_allocator(), bool, procs.count); + bool *possibly_ignore = temporary_alloc_array(procs.count); isize possibly_ignore_set = 0; if (true) { @@ -7342,7 +7341,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, } isize max_spaces = gb_max(max_name_length, max_type_length); - char *spaces = gb_alloc_array(temporary_allocator(), char, max_spaces+1); + char *spaces = temporary_alloc_array(max_spaces+1); for (isize i = 0; i < max_spaces; i++) { spaces[i] = ' '; } @@ -7706,7 +7705,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O } else { TEMPORARY_ALLOCATOR_GUARD(); - bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count); + bool *visited = temporary_alloc_array(param_count); // LEAK(bill) ordered_operands = array_make(permanent_allocator(), param_count); @@ -8881,7 +8880,7 @@ gb_internal void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Op isize count = multi_map_count(seen, key); if (count) { TEMPORARY_ALLOCATOR_GUARD(); - TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count); + TypeAndToken *taps = temporary_alloc_array(count); multi_map_get_all(seen, key, taps); for (isize i = 0; i < count; i++) { @@ -10927,7 +10926,7 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast } } - auto modified_args = slice_make(heap_allocator(), ce->args.count+1); + auto modified_args = permanent_slice_make(ce->args.count+1); modified_args[0] = first_arg; slice_copy(&modified_args, ce->args, 1); ce->args = modified_args; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index ae88ff333..e03d4a7ae 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1963,7 +1963,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } auto rhs = slice_from_array(vals); - auto lhs = slice_make(temporary_allocator(), rhs.count); + auto lhs = temporary_slice_make(rhs.count); slice_copy(&lhs, rs->vals); isize addressable_index = cast(isize)is_map; diff --git a/src/check_type.cpp b/src/check_type.cpp index a104d6fc0..4c995588f 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1106,7 +1106,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, GB_ASSERT(fields.count <= bf->fields.count); - auto bit_offsets = slice_make(permanent_allocator(), fields.count); + auto bit_offsets = permanent_slice_make(fields.count); i64 curr_offset = 0; for_array(i, bit_sizes) { bit_offsets[i] = curr_offset; @@ -2707,7 +2707,7 @@ gb_internal Type *get_map_cell_type(Type *type) { // Padding exists Type *s = alloc_type_struct(); Scope *scope = create_scope(nullptr, nullptr); - s->Struct.fields = slice_make(permanent_allocator(), 2); + s->Struct.fields = permanent_slice_make(2); s->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("v"), alloc_type_array(type, len), false, 0, EntityState_Resolved); s->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("_"), alloc_type_array(t_u8, padding), false, 1, EntityState_Resolved); s->Struct.scope = scope; @@ -2732,7 +2732,7 @@ gb_internal void init_map_internal_debug_types(Type *type) { Type *metadata_type = alloc_type_struct(); Scope *metadata_scope = create_scope(nullptr, nullptr); - metadata_type->Struct.fields = slice_make(permanent_allocator(), 5); + metadata_type->Struct.fields = permanent_slice_make(5); metadata_type->Struct.fields[0] = alloc_entity_field(metadata_scope, make_token_ident("key"), key, false, 0, EntityState_Resolved); metadata_type->Struct.fields[1] = alloc_entity_field(metadata_scope, make_token_ident("value"), value, false, 1, EntityState_Resolved); metadata_type->Struct.fields[2] = alloc_entity_field(metadata_scope, make_token_ident("hash"), t_uintptr, false, 2, EntityState_Resolved); @@ -2750,7 +2750,7 @@ gb_internal void init_map_internal_debug_types(Type *type) { Scope *scope = create_scope(nullptr, nullptr); Type *debug_type = alloc_type_struct(); - debug_type->Struct.fields = slice_make(permanent_allocator(), 3); + debug_type->Struct.fields = permanent_slice_make(3); debug_type->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("data"), metadata_type, false, 0, EntityState_Resolved); debug_type->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("len"), t_int, false, 1, EntityState_Resolved); debug_type->Struct.fields[2] = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, 2, EntityState_Resolved); @@ -2983,13 +2983,13 @@ gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finis if (wait_to_finish) { wait_signal_until_available(&old_struct->Struct.fields_wait_signal); } else { - GB_ASSERT(old_struct->Struct.fields_wait_signal.futex.load()); + GB_ASSERT(old_struct->Struct.fields_wait_signal.futex.load() != 0); } field_count = old_struct->Struct.fields.count; - t->Struct.fields = slice_make(permanent_allocator(), field_count+extra_field_count); - t->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); + t->Struct.fields = permanent_slice_make(field_count+extra_field_count); + t->Struct.tags = permanent_alloc_array(field_count+extra_field_count); auto const &add_entity = [](Scope *scope, Entity *entity) { @@ -3107,7 +3107,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e if (is_polymorphic) { field_count = 0; - soa_struct->Struct.fields = slice_make(permanent_allocator(), field_count+extra_field_count); + soa_struct->Struct.fields = permanent_slice_make(field_count+extra_field_count); soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); soa_struct->Struct.soa_count = 0; @@ -3117,7 +3117,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e Type *old_array = base_type(elem); field_count = cast(isize)old_array->Array.count; - soa_struct->Struct.fields = slice_make(permanent_allocator(), field_count+extra_field_count); + soa_struct->Struct.fields = permanent_slice_make(field_count+extra_field_count); soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); string_map_init(&scope->elements, 8); @@ -3159,8 +3159,8 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e if (old_struct->Struct.fields_wait_signal.futex.load()) { field_count = old_struct->Struct.fields.count; - soa_struct->Struct.fields = slice_make(permanent_allocator(), field_count+extra_field_count); - soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); + soa_struct->Struct.fields = permanent_slice_make(field_count+extra_field_count); + soa_struct->Struct.tags = permanent_alloc_array(field_count+extra_field_count); for_array(i, old_struct->Struct.fields) { Entity *old_field = old_struct->Struct.fields[i]; @@ -3355,7 +3355,7 @@ gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **t } } gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) { - GB_ASSERT_NOT_NULL(type); + GB_ASSERT(type != nullptr); if (e == nullptr) { *type = t_invalid; return true; diff --git a/src/checker.cpp b/src/checker.cpp index 0b54a3bbb..04e46d0e6 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -6177,7 +6177,7 @@ gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *u switch (pi->decl->proc_checked_state.load()) { case ProcCheckedState_InProgress: if (e) { - GB_ASSERT(global_procedure_body_in_worker_queue.load()); + GB_ASSERT(global_procedure_body_in_worker_queue.load() != 0); } return false; case ProcCheckedState_Checked: @@ -6431,7 +6431,7 @@ gb_internal WORKER_TASK_PROC(check_proc_info_worker_proc) { gb_internal void check_init_worker_data(Checker *c) { u32 thread_count = cast(u32)global_thread_pool.threads.count; - check_procedure_bodies_worker_data = gb_alloc_array(permanent_allocator(), CheckProcedureBodyWorkerData, thread_count); + check_procedure_bodies_worker_data = permanent_alloc_array(thread_count); for (isize i = 0; i < thread_count; i++) { check_procedure_bodies_worker_data[i].c = c; @@ -6493,7 +6493,7 @@ gb_internal Type *tuple_to_pointers(Type *ot) { Type *t = alloc_type_tuple(); - t->Tuple.variables = slice_make(heap_allocator(), ot->Tuple.variables.count); + t->Tuple.variables = permanent_slice_make(ot->Tuple.variables.count); Scope *scope = nullptr; for_array(i, t->Tuple.variables) { diff --git a/src/common_memory.cpp b/src/common_memory.cpp index 9e0222bc4..0b95e1242 100644 --- a/src/common_memory.cpp +++ b/src/common_memory.cpp @@ -353,7 +353,7 @@ gb_internal gbAllocator arena_allocator(Arena *arena) { gb_internal GB_ALLOCATOR_PROC(arena_allocator_proc) { void *ptr = nullptr; Arena *arena = cast(Arena *)allocator_data; - GB_ASSERT_NOT_NULL(arena); + GB_ASSERT(arena != nullptr); switch (type) { case gbAllocation_Alloc: @@ -401,6 +401,48 @@ gb_internal Arena *get_arena(ThreadArenaKind kind) { } +template +gb_internal T *permanent_alloc_item() { + Arena *arena = get_arena(ThreadArena_Permanent); + return arena_alloc_item(arena); +} + +template +gb_internal T *permanent_alloc_array(isize count) { + Arena *arena = get_arena(ThreadArena_Permanent); + return cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T)); +} + +template +gb_internal Slice permanent_slice_make(isize count) { + Arena *arena = get_arena(ThreadArena_Permanent); + T *data = cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T)); + return {data, count}; +} + +template +gb_internal T *temporary_alloc_item() { + Arena *arena = get_arena(ThreadArena_Temporary); + return arena_alloc_item(arena); +} + +template +gb_internal T *temporary_alloc_array(isize count) { + Arena *arena = get_arena(ThreadArena_Temporary); + return cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T)); +} + +template +gb_internal Slice temporary_slice_make(isize count) { + Arena *arena = get_arena(ThreadArena_Temporary); + T *data = cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T)); + return {data, count}; +} + + + + + gb_internal GB_ALLOCATOR_PROC(thread_arena_allocator_proc) { void *ptr = nullptr; diff --git a/src/gb/gb.h b/src/gb/gb.h index ffc40b8ca..c52f63cec 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -714,13 +714,15 @@ extern "C++" { } while (0) #endif + +#if defined(DISABLE_ASSERT) +#define GB_ASSERT(cond) gb_unused(cond) +#endif + #ifndef GB_ASSERT #define GB_ASSERT(cond) GB_ASSERT_MSG(cond, NULL) #endif -#ifndef GB_ASSERT_NOT_NULL -#define GB_ASSERT_NOT_NULL(ptr) GB_ASSERT_MSG((ptr) != NULL, #ptr " must not be NULL") -#endif // NOTE(bill): Things that shouldn't happen with a message! #ifndef GB_PANIC @@ -3719,7 +3721,7 @@ gb_inline i32 gb_strcmp(char const *s1, char const *s2) { } gb_inline char *gb_strcpy(char *dest, char const *source) { - GB_ASSERT_NOT_NULL(dest); + GB_ASSERT(dest != NULL); if (source) { char *str = dest; while (*source) *str++ = *source++; @@ -3729,7 +3731,7 @@ gb_inline char *gb_strcpy(char *dest, char const *source) { gb_inline char *gb_strncpy(char *dest, char const *source, isize len) { - GB_ASSERT_NOT_NULL(dest); + GB_ASSERT(dest != NULL); if (source) { char *str = dest; while (len > 0 && *source) { @@ -3746,7 +3748,7 @@ gb_inline char *gb_strncpy(char *dest, char const *source, isize len) { gb_inline isize gb_strlcpy(char *dest, char const *source, isize len) { isize result = 0; - GB_ASSERT_NOT_NULL(dest); + GB_ASSERT(dest != NULL); if (source) { char const *source_start = source; char *str = dest; @@ -5636,7 +5638,7 @@ gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char con void gb_file_free_contents(gbFileContents *fc) { if (fc == NULL || fc->size == 0) return; - GB_ASSERT_NOT_NULL(fc->data); + GB_ASSERT(fc->data != NULL); gb_free(fc->allocator, fc->data); fc->data = NULL; fc->size = 0; @@ -5648,7 +5650,7 @@ void gb_file_free_contents(gbFileContents *fc) { gb_inline b32 gb_path_is_absolute(char const *path) { b32 result = false; - GB_ASSERT_NOT_NULL(path); + GB_ASSERT(path != NULL); #if defined(GB_SYSTEM_WINDOWS) result == (gb_strlen(path) > 2) && gb_char_is_alpha(path[0]) && @@ -5663,7 +5665,7 @@ gb_inline b32 gb_path_is_relative(char const *path) { return !gb_path_is_absolut gb_inline b32 gb_path_is_root(char const *path) { b32 result = false; - GB_ASSERT_NOT_NULL(path); + GB_ASSERT(path != NULL); #if defined(GB_SYSTEM_WINDOWS) result = gb_path_is_absolute(path) && (gb_strlen(path) == 3); #else @@ -5674,14 +5676,14 @@ gb_inline b32 gb_path_is_root(char const *path) { gb_inline char const *gb_path_base_name(char const *path) { char const *ls; - GB_ASSERT_NOT_NULL(path); + GB_ASSERT(path != NULL); ls = gb_char_last_occurence(path, '/'); return (ls == NULL) ? path : ls+1; } gb_inline char const *gb_path_extension(char const *path) { char const *ld; - GB_ASSERT_NOT_NULL(path); + GB_ASSERT(path != NULL); ld = gb_char_last_occurence(path, '.'); return (ld == NULL) ? NULL : ld+1; } -- cgit v1.2.3 From a36a8722dc823c6fe143f7935e79467c6569bc00 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 19:30:32 +0100 Subject: Minimize more thread contention --- src/build_settings.cpp | 2 +- src/check_decl.cpp | 4 ++-- src/check_expr.cpp | 16 ++++++-------- src/check_stmt.cpp | 5 +++-- src/check_type.cpp | 9 ++++---- src/checker.cpp | 58 +++++++++++++++++++++++++++----------------------- src/checker.hpp | 15 +++++++------ src/entity.cpp | 2 +- src/error.cpp | 11 ++++++++++ src/threading.cpp | 10 ++++----- 10 files changed, 74 insertions(+), 58 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 0081fabee..0c88f3d13 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1163,7 +1163,7 @@ gb_internal String internal_odin_root_dir(void) { return global_module_path; } - auto path_buf = array_make(heap_allocator(), 300); + auto path_buf = array_make(temporary_allocator(), 300); defer (array_free(&path_buf)); len = 0; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 7dd9db105..49731ad60 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1981,9 +1981,9 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de ast_node(bs, BlockStmt, body); + TEMPORARY_ALLOCATOR_GUARD(); Array using_entities = {}; - using_entities.allocator = heap_allocator(); - defer (array_free(&using_entities)); + using_entities.allocator = temporary_allocator(); { if (type->Proc.param_count > 0) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 84f1c6f0a..bdbccb4f8 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7516,11 +7516,10 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op return check_call_arguments_proc_group(c, operand, call); } - auto positional_operands = array_make(heap_allocator(), 0, positional_args.count); - auto named_operands = array_make(heap_allocator(), 0, 0); + TEMPORARY_ALLOCATOR_GUARD(); - defer (array_free(&positional_operands)); - defer (array_free(&named_operands)); + auto positional_operands = array_make(temporary_allocator(), 0, positional_args.count); + auto named_operands = array_make(temporary_allocator(), 0, 0); if (positional_args.count > 0) { Entity **lhs = nullptr; @@ -7623,11 +7622,10 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O { // NOTE(bill, 2019-10-26): Allow a cycle in the parameters but not in the fields themselves auto prev_type_path = c->type_path; - c->type_path = new_checker_type_path(); - defer ({ - destroy_checker_type_path(c->type_path); - c->type_path = prev_type_path; - }); + TEMPORARY_ALLOCATOR_GUARD(); + + c->type_path = new_checker_type_path(temporary_allocator()); + defer (c->type_path = prev_type_path); if (is_call_expr_field_value(ce)) { named_fields = true; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index e03d4a7ae..ba9c08fed 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2567,8 +2567,9 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { result_count = proc_type->Proc.results->Tuple.variables.count; } - auto operands = array_make(heap_allocator(), 0, 2*rs->results.count); - defer (array_free(&operands)); + TEMPORARY_ALLOCATOR_GUARD(); + + auto operands = array_make(temporary_allocator(), 0, 2*rs->results.count); check_unpack_arguments(ctx, result_entities, result_count, &operands, rs->results, UnpackFlag_AllowOk); diff --git a/src/check_type.cpp b/src/check_type.cpp index 4c995588f..e99909d6b 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -3512,8 +3512,9 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T case_ast_node(pt, PointerType, e); CheckerContext c = *ctx; - c.type_path = new_checker_type_path(); - defer (destroy_checker_type_path(c.type_path)); + + TEMPORARY_ALLOCATOR_GUARD(); + c.type_path = new_checker_type_path(temporary_allocator()); Type *elem = t_invalid; Operand o = {}; @@ -3747,8 +3748,8 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T gb_internal Type *check_type(CheckerContext *ctx, Ast *e) { CheckerContext c = *ctx; - c.type_path = new_checker_type_path(); - defer (destroy_checker_type_path(c.type_path)); + TEMPORARY_ALLOCATOR_GUARD(); + c.type_path = new_checker_type_path(temporary_allocator()); return check_type_expr(&c, e, nullptr); } diff --git a/src/checker.cpp b/src/checker.cpp index 04e46d0e6..c1d6302ad 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1441,7 +1441,8 @@ gb_internal void init_checker_info(CheckerInfo *i) { map_init(&i->objc_method_implementations); string_map_init(&i->load_file_cache); - array_init(&i->all_procedures, heap_allocator()); + array_init(&i->all_procedures, a); + mpsc_init(&i->all_procedures_queue, a); mpsc_init(&i->entity_queue, a); // 1<<20); mpsc_init(&i->definition_queue, a); //); // 1<<20); @@ -1475,6 +1476,10 @@ gb_internal void destroy_checker_info(CheckerInfo *i) { array_free(&i->required_foreign_imports_through_force); array_free(&i->defineables); + array_free(&i->all_procedures); + + mpsc_destroy(&i->all_procedures_queue); + mpsc_destroy(&i->entity_queue); mpsc_destroy(&i->definition_queue); mpsc_destroy(&i->required_global_variable_queue); @@ -1502,12 +1507,12 @@ gb_internal CheckerContext make_checker_context(Checker *c) { ctx.scope = builtin_pkg->scope; ctx.pkg = builtin_pkg; - ctx.type_path = new_checker_type_path(); + ctx.type_path = new_checker_type_path(heap_allocator()); ctx.type_level = 0; return ctx; } gb_internal void destroy_checker_context(CheckerContext *ctx) { - destroy_checker_type_path(ctx->type_path); + destroy_checker_type_path(ctx->type_path, heap_allocator()); } gb_internal bool add_curr_ast_file(CheckerContext *ctx, AstFile *file) { @@ -1758,7 +1763,7 @@ gb_internal void add_untyped(CheckerContext *c, Ast *expr, AddressingMode mode, check_set_expr_info(c, expr, mode, type, value); } -gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMode mode, Type *type, ExactValue const &value) { +gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMode mode, Type *type, ExactValue const &value, bool use_mutex) { if (expr == nullptr) { return; } @@ -1776,7 +1781,7 @@ gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMo mutex = &ctx->pkg->type_and_value_mutex; } - mutex_lock(mutex); + if (use_mutex) mutex_lock(mutex); Ast *prev_expr = nullptr; while (prev_expr != expr) { prev_expr = expr; @@ -1801,7 +1806,7 @@ gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMo break; }; } - mutex_unlock(mutex); + if (use_mutex) mutex_unlock(mutex); } gb_internal void add_entity_definition(CheckerInfo *i, Ast *identifier, Entity *entity) { @@ -2345,11 +2350,9 @@ gb_internal void check_procedure_later(Checker *c, ProcInfo *info) { } if (DEBUG_CHECK_ALL_PROCEDURES) { - MUTEX_GUARD_BLOCK(&c->info.all_procedures_mutex) { - GB_ASSERT(info != nullptr); - GB_ASSERT(info->decl != nullptr); - array_add(&c->info.all_procedures, info); - } + GB_ASSERT(info != nullptr); + GB_ASSERT(info->decl != nullptr); + mpsc_enqueue(&c->info.all_procedures_queue, info); } } @@ -3195,19 +3198,17 @@ gb_internal Type *find_type_in_pkg(CheckerInfo *info, String const &pkg, String return e->type; } -gb_internal CheckerTypePath *new_checker_type_path() { - gbAllocator a = heap_allocator(); - auto *tp = gb_alloc_item(a, CheckerTypePath); - array_init(tp, a, 0, 16); +gb_internal CheckerTypePath *new_checker_type_path(gbAllocator allocator) { + auto *tp = gb_alloc_item(allocator, CheckerTypePath); + array_init(tp, allocator, 0, 16); return tp; } -gb_internal void destroy_checker_type_path(CheckerTypePath *tp) { +gb_internal void destroy_checker_type_path(CheckerTypePath *tp, gbAllocator allocator) { array_free(tp); - gb_free(heap_allocator(), tp); + gb_free(allocator, tp); } - gb_internal void check_type_path_push(CheckerContext *c, Entity *e) { GB_ASSERT(c->type_path != nullptr); GB_ASSERT(e != nullptr); @@ -5283,9 +5284,10 @@ gb_internal void check_add_import_decl(CheckerContext *ctx, Ast *decl) { GB_ASSERT(scope->flags&ScopeFlag_Pkg); - if (ptr_set_update(&parent_scope->imported, scope)) { - // error(token, "Multiple import of the same file within this scope"); - } + ptr_set_add(&parent_scope->imported, scope); + // if (ptr_set_update(&parent_scope->imported, scope)) { + // // error(token, "Multiple import of the same file within this scope"); + // } String import_name = path_to_entity_name(id->import_name.string, id->fullpath, false); if (is_blank_ident(import_name)) { @@ -6041,10 +6043,10 @@ gb_internal void calculate_global_init_order(Checker *c) { CheckerInfo *info = &c->info; TIME_SECTION("calculate_global_init_order: generate entity dependency graph"); - Arena *arena = get_arena(ThreadArena_Temporary); - ArenaTempGuard arena_guard(arena); + Arena *temporary_arena = get_arena(ThreadArena_Temporary); + ArenaTempGuard temporary_arena_guard(temporary_arena); - Array dep_graph = generate_entity_dependency_graph(info, arena); + Array dep_graph = generate_entity_dependency_graph(info, temporary_arena); TIME_SECTION("calculate_global_init_order: priority queue create"); // NOTE(bill): Priority queue @@ -6321,8 +6323,9 @@ gb_internal void check_safety_all_procedures_for_unchecked(Checker *c) { defer (map_destroy(&untyped)); - for_array(i, c->info.all_procedures) { - ProcInfo *pi = c->info.all_procedures[i]; + array_reserve(&c->info.all_procedures, c->info.all_procedures_queue.count.load()); + + for (ProcInfo *pi = nullptr; mpsc_dequeue(&c->info.all_procedures_queue, &pi); /**/) { GB_ASSERT(pi != nullptr); GB_ASSERT(pi->decl != nullptr); Entity *e = pi->decl->entity; @@ -6337,6 +6340,8 @@ gb_internal void check_safety_all_procedures_for_unchecked(Checker *c) { consume_proc_info(c, pi, &untyped); } } + + array_add(&c->info.all_procedures, pi); } } @@ -7412,7 +7417,6 @@ gb_internal void check_parsed_files(Checker *c) { TIME_SECTION("add untyped expression values"); - // Add untyped expression values for (UntypedExprInfo u = {}; mpsc_dequeue(&c->global_untyped_queue, &u); /**/) { GB_ASSERT(u.expr != nullptr && u.info != nullptr); if (is_type_typed(u.info->type)) { diff --git a/src/checker.hpp b/src/checker.hpp index 8b4d61ee2..5a40b10a0 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -309,11 +309,12 @@ struct EntityGraphNode; typedef PtrSet EntityGraphNodeSet; struct EntityGraphNode { - Entity * entity; // Procedure, Variable, Constant + Entity *entity; // Procedure, Variable, Constant + EntityGraphNodeSet pred; EntityGraphNodeSet succ; - isize index; // Index in array/queue - isize dep_count; + isize index; // Index in array/queue + isize dep_count; }; @@ -516,7 +517,7 @@ struct CheckerInfo { BlockingMutex load_file_mutex; StringMap load_file_cache; - BlockingMutex all_procedures_mutex; + MPSCQueue all_procedures_queue; Array all_procedures; BlockingMutex instrumentation_mutex; @@ -629,7 +630,7 @@ gb_internal void scope_lookup_parent (Scope *s, String const &name, Scope **s gb_internal Entity *scope_insert (Scope *s, Entity *entity); -gb_internal void add_type_and_value (CheckerContext *c, Ast *expression, AddressingMode mode, Type *type, ExactValue const &value); +gb_internal void add_type_and_value (CheckerContext *c, Ast *expression, AddressingMode mode, Type *type, ExactValue const &value, bool use_mutex=true); gb_internal ExprInfo *check_get_expr_info (CheckerContext *c, Ast *expr); gb_internal void add_untyped (CheckerContext *c, Ast *expression, AddressingMode mode, Type *basic_type, ExactValue const &value); gb_internal void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity); @@ -650,8 +651,8 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice const &n gb_internal void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws); gb_internal void check_delayed_file_import_entity(CheckerContext *c, Ast *decl); -gb_internal CheckerTypePath *new_checker_type_path(); -gb_internal void destroy_checker_type_path(CheckerTypePath *tp); +gb_internal CheckerTypePath *new_checker_type_path(gbAllocator allocator); +gb_internal void destroy_checker_type_path(CheckerTypePath *tp, gbAllocator allocator); gb_internal void check_type_path_push(CheckerContext *c, Entity *e); gb_internal Entity *check_type_path_pop (CheckerContext *c); diff --git a/src/entity.cpp b/src/entity.cpp index 5ca3fa916..e07e882f3 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -348,7 +348,7 @@ gb_internal Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Typ entity->type = type; entity->id = 1 + global_entity_id.fetch_add(1); if (token.pos.file_id) { - entity->file = thread_safe_get_ast_file_from_id(token.pos.file_id); + entity->file = thread_unsafe_get_ast_file_from_id(token.pos.file_id); } return entity; } diff --git a/src/error.cpp b/src/error.cpp index 10bf1caf5..53bc01654 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -153,6 +153,17 @@ gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) { } +// use AFTER PARSER +gb_internal AstFile *thread_unsafe_get_ast_file_from_id(i32 index) { + GB_ASSERT(index >= 0); + AstFile *file = nullptr; + if (index < global_files.count) { + file = global_files[index]; + } + return file; +} + + // NOTE: defined in build_settings.cpp gb_internal bool global_warnings_as_errors(void); diff --git a/src/threading.cpp b/src/threading.cpp index f1d9264e3..a35176ce6 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -448,9 +448,9 @@ gb_internal void semaphore_wait(Semaphore *s) { } #endif -static const int RWLOCK_WRITER = 1; -static const int RWLOCK_UPGRADED = 2; -static const int RWLOCK_READER = 4; +static const int RWLOCK_WRITER = 1<<0; +static const int RWLOCK_UPGRADED = 1<<1; +static const int RWLOCK_READER = 1<<2; struct RWSpinLock { Futex bits; }; @@ -467,7 +467,7 @@ bool rwlock_try_acquire_upgrade(RWSpinLock *l) { void rwlock_acquire_upgrade(RWSpinLock *l) { while (!rwlock_try_acquire_upgrade(l)) { - futex_wait(&l->bits, RWLOCK_UPGRADED); + futex_wait(&l->bits, RWLOCK_UPGRADED | RWLOCK_WRITER); } } void rwlock_release_upgrade(RWSpinLock *l) { @@ -481,7 +481,7 @@ bool rwlock_try_release_upgrade_and_acquire_write(RWSpinLock *l) { void rwlock_release_upgrade_and_acquire_write(RWSpinLock *l) { while (!rwlock_try_release_upgrade_and_acquire_write(l)) { - futex_wait(&l->bits, RWLOCK_WRITER); + futex_wait(&l->bits, RWLOCK_UPGRADED); } } -- cgit v1.2.3 From 01258d4817bb8d04ab287d847c93d36952f11f22 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 19:38:30 +0100 Subject: Multithread "check all scope usages" --- src/build_settings.cpp | 2 +- src/checker.cpp | 43 +++++++++++++++++++++++++++++++++---------- 2 files changed, 34 insertions(+), 11 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 0c88f3d13..077660f10 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1163,8 +1163,8 @@ gb_internal String internal_odin_root_dir(void) { return global_module_path; } + TEMPORARY_ALLOCATOR_GUARD(); auto path_buf = array_make(temporary_allocator(), 300); - defer (array_free(&path_buf)); len = 0; for (;;) { diff --git a/src/checker.cpp b/src/checker.cpp index c1d6302ad..1fca3dfe5 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -7204,6 +7204,36 @@ gb_internal void check_update_dependency_tree_for_procedures(Checker *c) { } #endif +gb_internal WORKER_TASK_PROC(check_scope_usage_file_worker) { + Checker *c = global_checker_ptr.load(std::memory_order_relaxed); + AstFile *f = cast(AstFile *)data; + u64 vet_flags = ast_file_vet_flags(f); + check_scope_usage(c, f->scope, vet_flags); + return 0; +} + +gb_internal WORKER_TASK_PROC(check_scope_usage_pkg_worker) { + Checker *c = global_checker_ptr.load(std::memory_order_relaxed); + AstPackage *pkg = cast(AstPackage *)data; + check_scope_usage_internal(c, pkg->scope, 0, true); + return 0; +} + + + +gb_internal void check_all_scope_usages(Checker *c) { + for (auto const &entry : c->info.files) { + AstFile *f = entry.value; + thread_pool_add_task(check_scope_usage_file_worker, f); + } + for (auto const &entry : c->info.packages) { + AstPackage *pkg = entry.value; + thread_pool_add_task(check_scope_usage_pkg_worker, pkg); + } + + thread_pool_wait(); +} + gb_internal void check_parsed_files(Checker *c) { global_checker_ptr.store(c, std::memory_order_relaxed); @@ -7271,16 +7301,9 @@ gb_internal void check_parsed_files(Checker *c) { TIME_SECTION("add entities from procedure bodies"); check_merge_queues_into_arrays(c); - TIME_SECTION("check scope usage"); - for (auto const &entry : c->info.files) { - AstFile *f = entry.value; - u64 vet_flags = ast_file_vet_flags(f); - check_scope_usage(c, f->scope, vet_flags); - } - for (auto const &entry : c->info.packages) { - AstPackage *pkg = entry.value; - check_scope_usage_internal(c, pkg->scope, 0, true); - } + TIME_SECTION("check all scope usages"); + check_all_scope_usages(c); + TIME_SECTION("add basic type information"); // Add "Basic" type information -- cgit v1.2.3 From 1c648126c73d01e6bce368bd8a55dbb89f0f2369 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 19:47:29 +0100 Subject: Move more from `heap_allocator()` to `temporary_allocator()` --- src/check_expr.cpp | 25 ++++++++++------------ src/checker.cpp | 61 ++++++++++++++++++++++++++---------------------------- 2 files changed, 40 insertions(+), 46 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index bdbccb4f8..dad9e8348 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6986,10 +6986,10 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, isize lhs_count = -1; i32 variadic_index = -1; - auto positional_operands = array_make(heap_allocator(), 0, 0); - auto named_operands = array_make(heap_allocator(), 0, 0); - defer (array_free(&positional_operands)); - defer (array_free(&named_operands)); + TEMPORARY_ALLOCATOR_GUARD(); + + auto positional_operands = array_make(temporary_allocator(), 0, 0); + auto named_operands = array_make(temporary_allocator(), 0, 0); if (procs.count == 1) { Entity *e = procs[0]; @@ -7030,7 +7030,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, if (proc_arg_count >= 0) { lhs_count = proc_arg_count; if (lhs_count > 0) { - lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count); + lhs = gb_alloc_array(temporary_allocator(), Entity *, lhs_count); for (isize param_index = 0; param_index < lhs_count; param_index++) { Entity *e = nullptr; for (Entity *p : procs) { @@ -7116,13 +7116,9 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, array_add(&named_operands, o); } - gb_free(heap_allocator(), lhs); - - auto valids = array_make(heap_allocator(), 0, procs.count); - defer (array_free(&valids)); + auto valids = array_make(temporary_allocator(), 0, procs.count); - auto proc_entities = array_make(heap_allocator(), 0, procs.count*2 + 1); - defer (array_free(&proc_entities)); + auto proc_entities = array_make(temporary_allocator(), 0, procs.count*2 + 1); for (Entity *proc : procs) { array_add(&proc_entities, proc); } @@ -7606,6 +7602,8 @@ gb_internal isize lookup_polymorphic_record_parameter(Type *t, String parameter_ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *operand, Ast *call) { + TEMPORARY_ALLOCATOR_GUARD(); + ast_node(ce, CallExpr, call); Type *original_type = operand->type; @@ -7614,7 +7612,6 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O bool show_error = true; Array operands = {}; - defer (array_free(&operands)); CallArgumentError err = CallArgumentError_None; @@ -7629,7 +7626,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O if (is_call_expr_field_value(ce)) { named_fields = true; - operands = array_make(heap_allocator(), ce->args.count); + operands = array_make(temporary_allocator(), ce->args.count); for_array(i, ce->args) { Ast *arg = ce->args[i]; ast_node(fv, FieldValue, arg); @@ -7661,7 +7658,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O } } else { - operands = array_make(heap_allocator(), 0, 2*ce->args.count); + operands = array_make(temporary_allocator(), 0, 2*ce->args.count); Entity **lhs = nullptr; isize lhs_count = -1; diff --git a/src/checker.cpp b/src/checker.cpp index 1fca3dfe5..87dd775ca 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -722,9 +722,10 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) { gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_flags, bool per_entity) { u64 original_vet_flags = vet_flags; + + TEMPORARY_ALLOCATOR_GUARD(); Array vetted_entities = {}; - array_init(&vetted_entities, heap_allocator()); - defer (array_free(&vetted_entities)); + array_init(&vetted_entities, temporary_allocator()); rw_mutex_shared_lock(&scope->mutex); for (auto const &entry : scope->elements) { @@ -5123,14 +5124,14 @@ gb_internal void add_import_dependency_node(Checker *c, Ast *decl, PtrMap generate_import_dependency_graph(Checker *c) { +gb_internal Array generate_import_dependency_graph(Checker *c, gbAllocator allocator) { PtrMap M = {}; map_init(&M, 2*c->parser->packages.count); defer (map_destroy(&M)); for_array(i, c->parser->packages) { AstPackage *pkg = c->parser->packages[i]; - ImportGraphNode *n = import_graph_node_create(heap_allocator(), pkg); + ImportGraphNode *n = import_graph_node_create(allocator, pkg); map_set(&M, pkg, n); } @@ -5147,7 +5148,7 @@ gb_internal Array generate_import_dependency_graph(Checker *c } Array G = {}; - array_init(&G, heap_allocator(), 0, M.count); + array_init(&G, allocator, 0, M.count); isize i = 0; for (auto const &entry : M) { @@ -5166,7 +5167,7 @@ struct ImportPathItem { Ast * decl; }; -gb_internal Array find_import_path(Checker *c, AstPackage *start, AstPackage *end, PtrSet *visited) { +gb_internal Array find_import_path(Checker *c, AstPackage *start, AstPackage *end, PtrSet *visited, gbAllocator allocator) { Array empty_path = {}; if (ptr_set_update(visited, start)) { @@ -5200,11 +5201,11 @@ gb_internal Array find_import_path(Checker *c, AstPackage *start ImportPathItem item = {pkg, decl}; if (pkg == end) { - auto path = array_make(heap_allocator()); + auto path = array_make(allocator); array_add(&path, item); return path; } - auto next_path = find_import_path(c, pkg, end, visited); + auto next_path = find_import_path(c, pkg, end, visited, allocator); if (next_path.count > 0) { array_add(&next_path, item); return next_path; @@ -5809,14 +5810,9 @@ gb_internal void check_export_entities(Checker *c) { } gb_internal void check_import_entities(Checker *c) { - Array dep_graph = generate_import_dependency_graph(c); - defer ({ - for_array(i, dep_graph) { - import_graph_node_destroy(dep_graph[i], heap_allocator()); - } - array_free(&dep_graph); - }); + TEMPORARY_ALLOCATOR_GUARD(); + Array dep_graph = generate_import_dependency_graph(c, temporary_allocator()); TIME_SECTION("check_import_entities - sort packages"); // NOTE(bill): Priority queue @@ -5826,8 +5822,7 @@ gb_internal void check_import_entities(Checker *c) { defer (ptr_set_destroy(&emitted)); Array package_order = {}; - array_init(&package_order, heap_allocator(), 0, c->parser->packages.count); - defer (array_free(&package_order)); + array_init(&package_order, temporary_allocator(), 0, c->parser->packages.count); while (pq.queue.count > 0) { ImportGraphNode *n = priority_queue_pop(&pq); @@ -5835,11 +5830,12 @@ gb_internal void check_import_entities(Checker *c) { AstPackage *pkg = n->pkg; if (n->dep_count > 0) { + TEMPORARY_ALLOCATOR_GUARD(); + PtrSet visited = {}; defer (ptr_set_destroy(&visited)); - auto path = find_import_path(c, pkg, pkg, &visited); - defer (array_free(&path)); + auto path = find_import_path(c, pkg, pkg, &visited, temporary_allocator()); if (path.count > 1) { ImportPathItem item = path[path.count-1]; @@ -5957,9 +5953,9 @@ gb_internal void check_import_entities(Checker *c) { } -gb_internal Array find_entity_path(Entity *start, Entity *end, PtrSet *visited = nullptr); +gb_internal Array find_entity_path(Entity *start, Entity *end, gbAllocator allocator, PtrSet *visited = nullptr); -gb_internal bool find_entity_path_tuple(Type *tuple, Entity *end, PtrSet *visited, Array *path_) { +gb_internal bool find_entity_path_tuple(Type *tuple, Entity *end, gbAllocator allocator, PtrSet *visited, Array *path_) { GB_ASSERT(path_ != nullptr); if (tuple == nullptr) { return false; @@ -5973,12 +5969,12 @@ gb_internal bool find_entity_path_tuple(Type *tuple, Entity *end, PtrSetdeps) { if (dep == end) { - auto path = array_make(heap_allocator()); + auto path = array_make(allocator); array_add(&path, dep); *path_ = path; return true; } - auto next_path = find_entity_path(dep, end, visited); + auto next_path = find_entity_path(dep, end, allocator, visited); if (next_path.count > 0) { array_add(&next_path, dep); *path_ = next_path; @@ -5990,7 +5986,7 @@ gb_internal bool find_entity_path_tuple(Type *tuple, Entity *end, PtrSet find_entity_path(Entity *start, Entity *end, PtrSet *visited) { +gb_internal Array find_entity_path(Entity *start, Entity *end, gbAllocator allocator, PtrSet *visited) { PtrSet visited_ = {}; bool made_visited = false; if (visited == nullptr) { @@ -6014,20 +6010,20 @@ gb_internal Array find_entity_path(Entity *start, Entity *end, PtrSet< GB_ASSERT(t->kind == Type_Proc); Array path = {}; - if (find_entity_path_tuple(t->Proc.params, end, visited, &path)) { + if (find_entity_path_tuple(t->Proc.params, end, allocator, visited, &path)) { return path; } - if (find_entity_path_tuple(t->Proc.results, end, visited, &path)) { + if (find_entity_path_tuple(t->Proc.results, end, allocator, visited, &path)) { return path; } } else { for (Entity *dep : decl->deps) { if (dep == end) { - auto path = array_make(heap_allocator()); + auto path = array_make(allocator); array_add(&path, dep); return path; } - auto next_path = find_entity_path(dep, end, visited); + auto next_path = find_entity_path(dep, end, allocator, visited); if (next_path.count > 0) { array_add(&next_path, dep); return next_path; @@ -6061,8 +6057,8 @@ gb_internal void calculate_global_init_order(Checker *c) { Entity *e = n->entity; if (n->dep_count > 0) { - auto path = find_entity_path(e, e); - defer (array_free(&path)); + TEMPORARY_ALLOCATOR_GUARD(); + auto path = find_entity_path(e, e, temporary_allocator()); if (path.count > 0) { Entity *e = path[0]; @@ -7450,9 +7446,10 @@ gb_internal void check_parsed_files(Checker *c) { TIME_SECTION("initialize and check for collisions in type info array"); { + TEMPORARY_ALLOCATOR_GUARD(); + Array type_info_types; // sorted after filled - array_init(&type_info_types, heap_allocator()); - defer (array_free(&type_info_types)); + array_init(&type_info_types, temporary_allocator()); for (auto const &tt : c->info.min_dep_type_info_set) { array_add(&type_info_types, tt); -- cgit v1.2.3 From 228ddd69034d7711a6c21b129eaac1b05d71b4ff Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 20:02:39 +0100 Subject: Inline some ptr set iterators --- src/checker.cpp | 17 +++++++++++++---- src/ptr_set.cpp | 5 ++++- 2 files changed, 17 insertions(+), 5 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 87dd775ca..dd4c0085a 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2996,7 +2996,7 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) { #undef FORCE_ADD_RUNTIME_ENTITIES } -gb_internal bool is_entity_a_dependency(Entity *e) { +gb_internal gb_inline bool is_entity_a_dependency(Entity *e) { if (e == nullptr) return false; switch (e->kind) { case Entity_Procedure: @@ -3047,7 +3047,9 @@ gb_internal Array generate_entity_dependency_graph(CheckerInf DeclInfo *decl = decl_info_of_entity(e); GB_ASSERT(decl != nullptr); - for (Entity *dep : decl->deps) { + FOR_PTR_SET(i_dep, decl->deps) { + Entity *dep = decl->deps.keys[i_dep]; + if (dep->flags & EntityFlag_Field) { continue; } @@ -3078,14 +3080,21 @@ gb_internal Array generate_entity_dependency_graph(CheckerInf // Connect each pred 'p' of 'n' with each succ 's' and from // the procedure node - for (EntityGraphNode *p : n->pred) { + FOR_PTR_SET(i_p, n->pred) { + EntityGraphNode *p = n->pred.keys[i_p]; + // Ignore self-cycles if (p == n) { continue; } // Each succ 's' of 'n' becomes a succ of 'p', and // each pred 'p' of 'n' becomes a pred of 's' - for (EntityGraphNode *s : n->succ) { + FOR_PTR_SET(i_s, n->succ) { + EntityGraphNode *s = n->succ.keys[i_s]; + if (s == nullptr || s == cast(EntityGraphNode *)PtrSet::TOMBSTONE) { + // NOTE(bill): This is inlined to improve development build performance + continue; + } // Ignore self-cycles if (s == n) { continue; diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp index 06c1e4a58..512a157d0 100644 --- a/src/ptr_set.cpp +++ b/src/ptr_set.cpp @@ -239,4 +239,7 @@ gb_internal PtrSetIterator begin(PtrSet &set) noexcept { template gb_internal PtrSetIterator end(PtrSet &set) noexcept { return PtrSetIterator{&set, set.capacity}; -} \ No newline at end of file +} + + +#define FOR_PTR_SET(index_, set_) for (usize index_ = 0; index_ < (set_).capacity; index_++) if ((set_).keys[index_] != nullptr && (set_).keys[index_] != cast(void *)~(uintptr)(0ull)) \ No newline at end of file -- cgit v1.2.3 From bc36ea41706f9773b031e161cbb6ece6da3d4250 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 20:11:36 +0100 Subject: Use macro instead of a C++ iterator - for speed C++ iterators are bad. --- src/check_decl.cpp | 2 +- src/check_expr.cpp | 2 +- src/checker.cpp | 36 ++++++++++++++---------------------- src/ptr_set.cpp | 9 +++++---- 4 files changed, 21 insertions(+), 28 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 49731ad60..ff888b74e 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1915,7 +1915,7 @@ gb_internal void add_deps_from_child_to_parent(DeclInfo *decl) { rw_mutex_shared_lock(&decl->deps_mutex); rw_mutex_lock(&decl->parent->deps_mutex); - for (Entity *e : decl->deps) { + FOR_PTR_SET(e, decl->deps) { ptr_set_add(&decl->parent->deps, e); } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index dad9e8348..012c50270 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6028,7 +6028,7 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize } rw_mutex_shared_lock(&decl->deps_mutex); rw_mutex_lock(&c->decl->deps_mutex); - for (Entity *dep : decl->deps) { + FOR_PTR_SET(dep, decl->deps) { ptr_set_add(&c->decl->deps, dep); } rw_mutex_unlock(&c->decl->deps_mutex); diff --git a/src/checker.cpp b/src/checker.cpp index dd4c0085a..934e6282b 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2584,7 +2584,7 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) { for (TypeInfoPair const tt : decl->type_info_deps) { add_min_dep_type_info(c, tt.type); } - for (Entity *e : decl->deps) { + FOR_PTR_SET(e, decl->deps) { switch (e->kind) { case Entity_Procedure: if (e->Procedure.is_foreign) { @@ -2611,7 +2611,7 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) { } } - for (Entity *e : decl->deps) { + FOR_PTR_SET(e, decl->deps) { add_dependency_to_set(c, e); } @@ -2643,7 +2643,7 @@ gb_internal WORKER_TASK_PROC(add_dependency_to_set_worker) { add_min_dep_type_info(c, tt.type); } - for (Entity *e : decl->deps) { + FOR_PTR_SET(e, decl->deps) { switch (e->kind) { case Entity_Procedure: if (e->Procedure.is_foreign) { @@ -2670,7 +2670,7 @@ gb_internal WORKER_TASK_PROC(add_dependency_to_set_worker) { } } - for (Entity *e : decl->deps) { + FOR_PTR_SET(e, decl->deps) { add_dependency_to_set_threaded(c, e); } @@ -3047,9 +3047,7 @@ gb_internal Array generate_entity_dependency_graph(CheckerInf DeclInfo *decl = decl_info_of_entity(e); GB_ASSERT(decl != nullptr); - FOR_PTR_SET(i_dep, decl->deps) { - Entity *dep = decl->deps.keys[i_dep]; - + FOR_PTR_SET(dep, decl->deps) { if (dep->flags & EntityFlag_Field) { continue; } @@ -3080,21 +3078,14 @@ gb_internal Array generate_entity_dependency_graph(CheckerInf // Connect each pred 'p' of 'n' with each succ 's' and from // the procedure node - FOR_PTR_SET(i_p, n->pred) { - EntityGraphNode *p = n->pred.keys[i_p]; - + FOR_PTR_SET(p, n->pred) { // Ignore self-cycles if (p == n) { continue; } // Each succ 's' of 'n' becomes a succ of 'p', and // each pred 'p' of 'n' becomes a pred of 's' - FOR_PTR_SET(i_s, n->succ) { - EntityGraphNode *s = n->succ.keys[i_s]; - if (s == nullptr || s == cast(EntityGraphNode *)PtrSet::TOMBSTONE) { - // NOTE(bill): This is inlined to improve development build performance - continue; - } + FOR_PTR_SET(s, n->succ) { // Ignore self-cycles if (s == n) { continue; @@ -5859,7 +5850,7 @@ gb_internal void check_import_entities(Checker *c) { } } - for (ImportGraphNode *p : n->pred) { + FOR_PTR_SET(p, n->pred) { p->dep_count = gb_max(p->dep_count-1, 0); priority_queue_fix(&pq, p->index); } @@ -5976,7 +5967,8 @@ gb_internal bool find_entity_path_tuple(Type *tuple, Entity *end, gbAllocator al if (var_decl == nullptr) { continue; } - for (Entity *dep : var_decl->deps) { + + FOR_PTR_SET(dep, var_decl->deps) { if (dep == end) { auto path = array_make(allocator); array_add(&path, dep); @@ -6026,7 +6018,7 @@ gb_internal Array find_entity_path(Entity *start, Entity *end, gbAlloc return path; } } else { - for (Entity *dep : decl->deps) { + FOR_PTR_SET(dep, decl->deps) { if (dep == end) { auto path = array_make(allocator); array_add(&path, dep); @@ -6080,7 +6072,7 @@ gb_internal void calculate_global_init_order(Checker *c) { } } - for (EntityGraphNode *p : n->pred) { + FOR_PTR_SET(p, n->pred) { p->dep_count -= 1; p->dep_count = gb_max(p->dep_count, 0); priority_queue_fix(&pq, p->index); @@ -6273,7 +6265,7 @@ gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *u add_untyped_expressions(&c->info, ctx.untyped); rw_mutex_shared_lock(&ctx.decl->deps_mutex); - for (Entity *dep : ctx.decl->deps) { + FOR_PTR_SET(dep, ctx.decl->deps) { if (dep && dep->kind == Entity_Procedure && (dep->flags & EntityFlag_ProcBodyChecked) == 0) { check_procedure_later_from_entity(c, dep, NULL); @@ -7346,7 +7338,7 @@ gb_internal void check_parsed_files(Checker *c) { DeclInfo *decl = e->decl_info; ast_node(pl, ProcLit, decl->proc_lit); if (pl->inlining == ProcInlining_inline) { - for (Entity *dep : decl->deps) { + FOR_PTR_SET(dep, decl->deps) { if (dep == e) { error(e->token, "Cannot inline recursive procedure '%.*s'", LIT(e->token.string)); break; diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp index 512a157d0..5b1d2cc19 100644 --- a/src/ptr_set.cpp +++ b/src/ptr_set.cpp @@ -16,6 +16,8 @@ template gb_internal bool ptr_set_exists (PtrSet *s, T ptr); template gb_internal void ptr_set_remove (PtrSet *s, T ptr); template gb_internal void ptr_set_clear (PtrSet *s); +#define FOR_PTR_SET(element, set_) for (auto *it = &(set_).keys[0], element = it ? *it : nullptr; (set_).keys != nullptr && it < &(set_).keys[(set_).capacity]; it++) if (element = *it, (*it != nullptr && *it != cast(void *)~(uintptr)(0ull))) + gb_internal gbAllocator ptr_set_allocator(void) { return heap_allocator(); } @@ -83,7 +85,7 @@ gb_internal gb_inline void ptr_set_grow(PtrSet *old_set) { PtrSet new_set = {}; ptr_set_init(&new_set, gb_max(old_set->capacity<<1, 16)); - for (T ptr : *old_set) { + FOR_PTR_SET(ptr, *old_set) { bool was_new = ptr_set_update(&new_set, ptr); GB_ASSERT(!was_new); } @@ -195,7 +197,7 @@ gb_internal gb_inline void ptr_set_clear(PtrSet *s) { gb_zero_size(s->keys, s->capacity*gb_size_of(T)); } -template +/*template struct PtrSetIterator { PtrSet *set; usize index; @@ -239,7 +241,6 @@ gb_internal PtrSetIterator begin(PtrSet &set) noexcept { template gb_internal PtrSetIterator end(PtrSet &set) noexcept { return PtrSetIterator{&set, set.capacity}; -} +}*/ -#define FOR_PTR_SET(index_, set_) for (usize index_ = 0; index_ < (set_).capacity; index_++) if ((set_).keys[index_] != nullptr && (set_).keys[index_] != cast(void *)~(uintptr)(0ull)) \ No newline at end of file -- cgit v1.2.3 From d3602ca634061bda4561a05c37f4fe3a828ad1e6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 20:37:39 +0100 Subject: Removal of some old checks --- src/checker.cpp | 83 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 35 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 934e6282b..e451a38e1 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2997,7 +2997,6 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) { } gb_internal gb_inline bool is_entity_a_dependency(Entity *e) { - if (e == nullptr) return false; switch (e->kind) { case Entity_Procedure: return true; @@ -3026,7 +3025,7 @@ gb_internal Array generate_entity_dependency_graph(CheckerInf for_array(i, info->entities) { Entity *e = info->entities[i]; - if (!is_entity_a_dependency(e)) { + if (e == nullptr || !is_entity_a_dependency(e)) { continue; } EntityGraphNode *n = arena_alloc_item(arena); @@ -3048,10 +3047,10 @@ gb_internal Array generate_entity_dependency_graph(CheckerInf GB_ASSERT(decl != nullptr); FOR_PTR_SET(dep, decl->deps) { + GB_ASSERT(dep != nullptr); if (dep->flags & EntityFlag_Field) { continue; } - GB_ASSERT(dep != nullptr); if (!is_entity_a_dependency(dep)) { continue; } @@ -7129,13 +7128,18 @@ gb_internal void check_sort_init_and_fini_procedures(Checker *c) { } gb_internal void add_type_info_for_type_definitions(Checker *c) { - for_array(i, c->info.definitions) { - Entity *e = c->info.definitions[i]; + for (Entity *e : c->info.definitions) { if (e->kind == Entity_TypeName && e->type != nullptr && is_type_typed(e->type)) { + #if 0 i64 align = type_align_of(e->type); if (align > 0 && e->min_dep_count.load(std::memory_order_relaxed) > 0) { add_type_info_type(&c->builtin_ctx, e->type); } + #else + if (e->min_dep_count.load(std::memory_order_relaxed) > 0) { + add_type_info_type(&c->builtin_ctx, e->type); + } + #endif } } } @@ -7232,6 +7236,43 @@ gb_internal void check_all_scope_usages(Checker *c) { } +gb_internal void check_for_type_cycles(Checker *c) { + // NOTE(bill): Check for illegal cyclic type declarations + for_array(i, c->info.definitions) { + Entity *e = c->info.definitions[i]; + if (e->kind != Entity_TypeName) { + continue; + } + if (e->type != nullptr && is_type_typed(e->type)) { + if (e->TypeName.is_type_alias) { + // Ignore for the time being + } else { + (void)type_align_of(e->type); + } + } + } +} + +gb_internal void check_for_inline_cycles(Checker *c) { + for_array(i, c->info.definitions) { + Entity *e = c->info.definitions[i]; + if (e->kind != Entity_Procedure) { + continue; + } + DeclInfo *decl = e->decl_info; + ast_node(pl, ProcLit, decl->proc_lit); + if (pl->inlining == ProcInlining_inline) { + FOR_PTR_SET(dep, decl->deps) { + if (dep == e) { + error(e->token, "Cannot inline recursive procedure '%.*s'", LIT(e->token.string)); + break; + } + } + } + } +} + + gb_internal void check_parsed_files(Checker *c) { global_checker_ptr.store(c, std::memory_order_relaxed); @@ -7314,38 +7355,10 @@ gb_internal void check_parsed_files(Checker *c) { check_merge_queues_into_arrays(c); TIME_SECTION("check for type cycles"); - // NOTE(bill): Check for illegal cyclic type declarations - for_array(i, c->info.definitions) { - Entity *e = c->info.definitions[i]; - if (e->kind != Entity_TypeName) { - continue; - } - if (e->type != nullptr && is_type_typed(e->type)) { - if (e->TypeName.is_type_alias) { - // Ignore for the time being - } else { - (void)type_align_of(e->type); - } - } - } + check_for_type_cycles(c); TIME_SECTION("check for inline cycles"); - for_array(i, c->info.definitions) { - Entity *e = c->info.definitions[i]; - if (e->kind != Entity_Procedure) { - continue; - } - DeclInfo *decl = e->decl_info; - ast_node(pl, ProcLit, decl->proc_lit); - if (pl->inlining == ProcInlining_inline) { - FOR_PTR_SET(dep, decl->deps) { - if (dep == e) { - error(e->token, "Cannot inline recursive procedure '%.*s'", LIT(e->token.string)); - break; - } - } - } - } + check_for_inline_cycles(c); TIME_SECTION("check deferred procedures"); check_deferred_procedures(c); -- cgit v1.2.3 From 0476d33a6c3b0c730b0e0defc10b571b1037c14c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 20:45:26 +0100 Subject: Remove global `PtrMap` and store on the `TypeNamed` directly --- src/check_type.cpp | 18 +++++++++--------- src/checker.cpp | 5 ----- src/checker.hpp | 2 -- src/types.cpp | 15 ++++++++++----- 4 files changed, 19 insertions(+), 21 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index e99909d6b..aec416921 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -267,19 +267,19 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_, gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type) { - mutex_lock(&ctx->info->gen_types_mutex); // @@global - GenTypesData *found_gen_types = nullptr; - auto *found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type); - if (found_gen_types_ptr == nullptr) { + + GB_ASSERT(original_type->kind == Type_Named); + mutex_lock(&original_type->Named.gen_types_data_mutex); + if (original_type->Named.gen_types_data == nullptr) { GenTypesData *gen_types = gb_alloc_item(permanent_allocator(), GenTypesData); gen_types->types = array_make(heap_allocator()); - map_set(&ctx->info->gen_types, original_type, gen_types); - found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type); + original_type->Named.gen_types_data = gen_types; } - found_gen_types = *found_gen_types_ptr; - GB_ASSERT(found_gen_types != nullptr); - mutex_unlock(&ctx->info->gen_types_mutex); // @@global + found_gen_types = original_type->Named.gen_types_data; + + mutex_unlock(&original_type->Named.gen_types_data_mutex); + return found_gen_types; } diff --git a/src/checker.cpp b/src/checker.cpp index e451a38e1..eb334e147 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1420,13 +1420,10 @@ gb_internal void init_checker_info(CheckerInfo *i) { array_init(&i->entities, a); map_init(&i->global_untyped); string_map_init(&i->foreigns); - // map_init(&i->gen_procs); - map_init(&i->gen_types); type_set_init(&i->min_dep_type_info_set); map_init(&i->min_dep_type_info_index_map); - // map_init(&i->type_info_map); string_map_init(&i->files); string_map_init(&i->packages); array_init(&i->variable_init_order, a); @@ -1465,8 +1462,6 @@ gb_internal void destroy_checker_info(CheckerInfo *i) { array_free(&i->entities); map_destroy(&i->global_untyped); string_map_destroy(&i->foreigns); - // map_destroy(&i->gen_procs); - map_destroy(&i->gen_types); type_set_destroy(&i->min_dep_type_info_set); map_destroy(&i->min_dep_type_info_index_map); diff --git a/src/checker.hpp b/src/checker.hpp index 5a40b10a0..e50fa40f4 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -480,8 +480,6 @@ struct CheckerInfo { RecursiveMutex lazy_mutex; // Mutex required for lazy type checking of specific files - BlockingMutex gen_types_mutex; - PtrMap gen_types; // BlockingMutex type_info_mutex; // NOT recursive // Array type_info_types; diff --git a/src/types.cpp b/src/types.cpp index 44f9394c7..3ccc74996 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -206,13 +206,18 @@ struct TypeProc { bool optional_ok; }; +struct TypeNamed { + String name; + Type * base; + Entity *type_name; /* Entity_TypeName */ + + BlockingMutex gen_types_data_mutex; + GenTypesData *gen_types_data; +}; + #define TYPE_KINDS \ TYPE_KIND(Basic, BasicType) \ - TYPE_KIND(Named, struct { \ - String name; \ - Type * base; \ - Entity *type_name; /* Entity_TypeName */ \ - }) \ + TYPE_KIND(Named, TypeNamed) \ TYPE_KIND(Generic, struct { \ i64 id; \ String name; \ -- cgit v1.2.3 From 5ea2e1fe60872c5d2b20e180a1514a082b6513e4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Sep 2025 21:41:52 +0100 Subject: Minimize mutex usage when in single threaded mode. --- src/check_decl.cpp | 2 +- src/check_expr.cpp | 10 +++++----- src/check_stmt.cpp | 12 +++++++----- src/checker.cpp | 39 +++++++++++++++++++++++++-------------- src/checker.hpp | 4 ++-- src/parser.cpp | 1 + src/parser.hpp | 1 + 7 files changed, 42 insertions(+), 27 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 58e4f0120..8fbcb5e40 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -948,7 +948,7 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e) error(ident, "foreign library names must be an identifier"); } else { String name = ident->Ident.token.string; - Entity *found = scope_lookup(ctx->scope, name); + Entity *found = scope_lookup(ctx->scope, name, ident->Ident.hash); if (found == nullptr) { if (is_blank_ident(name)) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index abfd11485..d2505c047 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1738,7 +1738,7 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam o->expr = n; String name = n->Ident.token.string; - Entity *e = scope_lookup(c->scope, name); + Entity *e = scope_lookup(c->scope, name, n->Ident.hash); if (e == nullptr) { if (is_blank_ident(name)) { error(n, "'_' cannot be used as a value"); @@ -5361,7 +5361,7 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast * } } else */if (node->kind == Ast_Ident) { String name = node->Ident.token.string; - return scope_lookup(c->scope, name); + return scope_lookup(c->scope, name, node->Ident.hash); } else if (!ident_only) if (node->kind == Ast_SelectorExpr) { ast_node(se, SelectorExpr, node); if (se->token.kind == Token_ArrowRight) { @@ -5383,7 +5383,7 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast * if (op_expr->kind == Ast_Ident) { String op_name = op_expr->Ident.token.string; - Entity *e = scope_lookup(c->scope, op_name); + Entity *e = scope_lookup(c->scope, op_name, op_expr->Ident.hash); if (e == nullptr) { return nullptr; } @@ -5480,7 +5480,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod if (op_expr->kind == Ast_Ident) { String op_name = op_expr->Ident.token.string; - Entity *e = scope_lookup(c->scope, op_name); + Entity *e = scope_lookup(c->scope, op_name, op_expr->Ident.hash); add_entity_use(c, op_expr, e); expr_entity = e; @@ -5903,7 +5903,7 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals return true; } } else { - Entity *e = scope_lookup(s, name); + Entity *e = scope_lookup(s, name, i->hash); if (e != nullptr) { if (out_scope) *out_scope = e->scope; return true; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index ba9c08fed..62cfc256a 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -484,7 +484,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O } if (ident_node != nullptr) { ast_node(i, Ident, ident_node); - e = scope_lookup(ctx->scope, i->token.string); + e = scope_lookup(ctx->scope, i->token.string, i->hash); if (e != nullptr && e->kind == Entity_Variable) { used = (e->flags & EntityFlag_Used) != 0; // NOTE(bill): Make backup just in case } @@ -1812,8 +1812,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) error(node, "Iteration over a bit_set of an enum is not allowed runtime type information (RTTI) has been disallowed"); } if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) { - String name = rs->vals[0]->Ident.token.string; - Entity *found = scope_lookup(ctx->scope, name); + AstIdent *ident = &rs->vals[0]->Ident; + String name = ident->token.string; + Entity *found = scope_lookup(ctx->scope, name, ident->hash); if (found && are_types_identical(found->type, t->BitSet.elem)) { ERROR_BLOCK(); gbString s = expr_to_string(expr); @@ -1857,8 +1858,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) error(node, "#reverse for is not supported for map types, as maps are unordered"); } if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) { - String name = rs->vals[0]->Ident.token.string; - Entity *found = scope_lookup(ctx->scope, name); + AstIdent *ident = &rs->vals[0]->Ident; + String name = ident->token.string; + Entity *found = scope_lookup(ctx->scope, name, ident->hash); if (found && are_types_identical(found->type, t->Map.key)) { ERROR_BLOCK(); gbString s = expr_to_string(expr); diff --git a/src/checker.cpp b/src/checker.cpp index eb334e147..7da3948dc 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -380,16 +380,25 @@ gb_internal Entity *scope_lookup_current(Scope *s, String const &name) { } -gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entity **entity_) { +gb_global std::atomic in_single_threaded_checker_stage; + +gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entity **entity_, u32 hash) { + bool is_single_threaded = in_single_threaded_checker_stage.load(std::memory_order_relaxed); if (scope != nullptr) { bool gone_thru_proc = false; bool gone_thru_package = false; - StringHashKey key = string_hash_string(name); + StringHashKey key = {}; + if (hash) { + key.hash = hash; + key.string = name; + } else { + key = string_hash_string(name); + } for (Scope *s = scope; s != nullptr; s = s->parent) { Entity **found = nullptr; - rw_mutex_shared_lock(&s->mutex); + if (!is_single_threaded) rw_mutex_shared_lock(&s->mutex); found = string_map_get(&s->elements, key); - rw_mutex_shared_unlock(&s->mutex); + if (!is_single_threaded) rw_mutex_shared_unlock(&s->mutex); if (found) { Entity *e = *found; if (gone_thru_proc) { @@ -424,9 +433,9 @@ gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **s if (scope_) *scope_ = nullptr; } -gb_internal Entity *scope_lookup(Scope *s, String const &name) { +gb_internal Entity *scope_lookup(Scope *s, String const &name, u32 hash) { Entity *entity = nullptr; - scope_lookup_parent(s, name, nullptr, &entity); + scope_lookup_parent(s, name, nullptr, &entity, hash); return entity; } @@ -507,11 +516,9 @@ end:; return result; } -gb_global bool in_single_threaded_checker_stage = false; - gb_internal Entity *scope_insert(Scope *s, Entity *entity) { String name = entity->token.string; - if (in_single_threaded_checker_stage) { + if (in_single_threaded_checker_stage.load(std::memory_order_relaxed)) { return scope_insert_with_name_no_mutex(s, name, entity); } else { return scope_insert_with_name(s, name, entity); @@ -853,9 +860,13 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { gb_internal void add_dependency(CheckerInfo *info, DeclInfo *d, Entity *e) { - rw_mutex_lock(&d->deps_mutex); - ptr_set_add(&d->deps, e); - rw_mutex_unlock(&d->deps_mutex); + if (in_single_threaded_checker_stage.load(std::memory_order_relaxed)) { + ptr_set_add(&d->deps, e); + } else { + rw_mutex_lock(&d->deps_mutex); + ptr_set_add(&d->deps, e); + rw_mutex_unlock(&d->deps_mutex); + } } gb_internal void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *type) { if (d == nullptr || type == nullptr) { @@ -4958,7 +4969,7 @@ gb_internal void check_single_global_entity(Checker *c, Entity *e, DeclInfo *d) } gb_internal void check_all_global_entities(Checker *c) { - in_single_threaded_checker_stage = true; + in_single_threaded_checker_stage.store(true, std::memory_order_relaxed); // NOTE(bill): This must be single threaded // Don't bother trying @@ -4980,7 +4991,7 @@ gb_internal void check_all_global_entities(Checker *c) { } } - in_single_threaded_checker_stage = false; + in_single_threaded_checker_stage.store(false, std::memory_order_relaxed); } diff --git a/src/checker.hpp b/src/checker.hpp index 968988962..68779c280 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -623,8 +623,8 @@ gb_internal Entity *entity_of_node(Ast *expr); gb_internal Entity *scope_lookup_current(Scope *s, String const &name); -gb_internal Entity *scope_lookup (Scope *s, String const &name); -gb_internal void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_); +gb_internal Entity *scope_lookup (Scope *s, String const &name, u32 hash=0); +gb_internal void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_, u32 hash=0); gb_internal Entity *scope_insert (Scope *s, Entity *entity); diff --git a/src/parser.cpp b/src/parser.cpp index a7006dd39..a32494c05 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -750,6 +750,7 @@ gb_internal Ast *ast_matrix_index_expr(AstFile *f, Ast *expr, Token open, Token gb_internal Ast *ast_ident(AstFile *f, Token token) { Ast *result = alloc_ast_node(f, Ast_Ident); result->Ident.token = token; + result->Ident.hash = string_hash(token.string); return result; } diff --git a/src/parser.hpp b/src/parser.hpp index d2dd22667..56447df43 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -396,6 +396,7 @@ struct AstSplitArgs { AST_KIND(Ident, "identifier", struct { \ Token token; \ Entity *entity; \ + u32 hash; \ }) \ AST_KIND(Implicit, "implicit", Token) \ AST_KIND(Uninit, "uninitialized value", Token) \ -- cgit v1.2.3 From 6ce889f4ebf10d44fc6c1e5fba794e412dfcf183 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 19 Sep 2025 11:01:41 +0100 Subject: `Entity *` to `std::atomic` to remove the need for a PtrMap+Mutex --- src/check_decl.cpp | 8 ++++---- src/check_expr.cpp | 9 +++++---- src/checker.cpp | 2 +- src/checker.hpp | 2 +- src/llvm_backend.hpp | 3 --- src/llvm_backend_general.cpp | 21 +++++++++------------ src/llvm_backend_proc.cpp | 2 +- src/llvm_backend_type.cpp | 5 +++-- 8 files changed, 24 insertions(+), 28 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 8fbcb5e40..092222d3c 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1549,7 +1549,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { "\tother at %s", LIT(name), token_pos_to_string(pos)); } else if (name == "main") { - if (d->entity->pkg->kind != Package_Runtime) { + if (d->entity.load()->pkg->kind != Package_Runtime) { error(d->proc_lit, "The link name 'main' is reserved for internal use"); } } else { @@ -1967,8 +1967,8 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de ctx->curr_proc_sig = type; ctx->curr_proc_calling_convention = type->Proc.calling_convention; - if (decl->parent && decl->entity && decl->parent->entity) { - decl->entity->parent_proc_decl = decl->parent; + if (decl->parent && decl->entity.load() && decl->parent->entity) { + decl->entity.load()->parent_proc_decl = decl->parent; } if (ctx->pkg->name != "runtime") { @@ -2072,7 +2072,7 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de GB_ASSERT(decl->proc_checked_state != ProcCheckedState_Checked); if (decl->defer_use_checked) { GB_ASSERT(is_type_polymorphic(type, true)); - error(token, "Defer Use Checked: %.*s", LIT(decl->entity->token.string)); + error(token, "Defer Use Checked: %.*s", LIT(decl->entity.load()->token.string)); GB_ASSERT(decl->defer_use_checked == false); } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d2505c047..d863d6cf6 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -608,7 +608,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E entity->flags |= EntityFlag_Disabled; } - d->entity = entity; + d->entity.store(entity); AstFile *file = nullptr; { @@ -8335,9 +8335,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c if (c->curr_proc_decl == nullptr) { error(call, "Calling a '#force_inline' procedure that enables target features is not allowed at file scope"); } else { - GB_ASSERT(c->curr_proc_decl->entity); - GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc); - String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature; + Entity *e = c->curr_proc_decl->entity.load(); + GB_ASSERT(e); + GB_ASSERT(e->type->kind == Type_Proc); + String scope_features = e->type->Proc.enable_target_feature; if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) { ERROR_BLOCK(); error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid)); diff --git a/src/checker.cpp b/src/checker.cpp index 7da3948dc..32bda2e43 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2055,8 +2055,8 @@ gb_internal void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, En add_entity_definition(info, identifier, e); GB_ASSERT(e->decl_info == nullptr); e->decl_info = d; - d->entity = e; e->pkg = c->pkg; + d->entity.store(e); isize queue_count = -1; bool is_lazy = false; diff --git a/src/checker.hpp b/src/checker.hpp index 68779c280..d22b99e89 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -209,7 +209,7 @@ struct DeclInfo { Scope * scope; - Entity *entity; + std::atomic entity; Ast * decl_node; Ast * type_expr; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 370a6f2ca..7fe4651bb 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -238,9 +238,6 @@ struct lbGenerator : LinkerData { PtrMap modules_through_ctx; lbModule default_module; - RecursiveMutex anonymous_proc_lits_mutex; - PtrMap anonymous_proc_lits; - isize used_module_count; lbProcedure *startup_runtime; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 64d574920..129634ee6 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -157,7 +157,6 @@ 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->anonymous_proc_lits, 1024); if (USE_SEPARATE_MODULES) { bool module_per_file = build_context.module_per_file && build_context.optimization_level <= 0; @@ -3084,18 +3083,14 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) { - lbGenerator *gen = m->gen; - - mutex_lock(&gen->anonymous_proc_lits_mutex); - defer (mutex_unlock(&gen->anonymous_proc_lits_mutex)); + // lbGenerator *gen = m->gen; + ast_node(pl, ProcLit, expr); - TokenPos pos = ast_token(expr).pos; - lbProcedure **found = map_get(&gen->anonymous_proc_lits, expr); - if (found) { - return lb_find_procedure_value_from_entity(m, (*found)->entity); + if (pl->decl->entity.load() != nullptr) { + return lb_find_procedure_value_from_entity(m, pl->decl->entity.load()); } - ast_node(pl, ProcLit, expr); + TokenPos pos = ast_token(expr).pos; // NOTE(bill): Generate a new name // parent$count @@ -3114,15 +3109,18 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr token.string = name; Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags); e->file = expr->file(); + e->pkg = e->file->pkg; + e->scope = e->file->scope; // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time pl->decl->code_gen_module = m; e->decl_info = pl->decl; - pl->decl->entity = e; e->parent_proc_decl = pl->decl->parent; e->Procedure.is_anonymous = true; e->flags |= EntityFlag_ProcBodyChecked; + pl->decl->entity.store(e); + lbProcedure *p = lb_create_procedure(m, e); GB_ASSERT(e->code_gen_module == m); @@ -3130,7 +3128,6 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr value.value = p->value; value.type = p->type; - map_set(&gen->anonymous_proc_lits, expr, p); array_add(&m->procedures_to_generate, p); if (parent != nullptr) { array_add(&parent->children, p); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 20d627fa2..f71b693eb 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2211,7 +2211,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu GB_ASSERT(e != nullptr); if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) { - procedure = e->parent_proc_decl->entity->token.string; + procedure = e->parent_proc_decl->entity.load()->token.string; } else { procedure = str_lit(""); } diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index d1e7c0559..b2eec218f 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -394,8 +394,9 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ String proc_name = {}; if (t->Named.type_name->parent_proc_decl) { DeclInfo *decl = t->Named.type_name->parent_proc_decl; - if (decl->entity && decl->entity->kind == Entity_Procedure) { - proc_name = decl->entity->token.string; + Entity *e = decl->entity.load(); + if (e && e->kind == Entity_Procedure) { + proc_name = e->token.string; } } TokenPos pos = t->Named.type_name->token.pos; -- cgit v1.2.3 From 5af13f5d53b4e5f5d472cd8a8bc4444f05ea36d6 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Tue, 16 Sep 2025 00:49:31 -0400 Subject: Automatically emit objc_msgSend calls when calling imported or implemented Objective-C methods - Add intrinsics.objc_super() - Emit objc_msgSendSuper2 calls when an objc method call is combined with objc_super(self) - Fix objc_block return value ABI for large struct returns - Fix objc_implement method wrappers bad ABI for large struct returns and indirect args - Simplify parameter forwarding for objc_imlpement methods - Add intrinsics.objc_instancetype to mimi Objective-C instancetype* returns This facilitates returning the correct type on subclasses when calling mehtods such as `alloc`, `init`, `retain`, etc. - Refactor Objective-C class implementations generation so that hierarchies are properly initialized - Better codegen for context passing with ivar-based autocontext - Allow @superclass on imported objc-c objects - Better codegen for block forwarding invoker, arguments are forwarded directly --- base/intrinsics/intrinsics.odin | 10 +- base/runtime/procs_darwin.odin | 16 ++- src/check_builtin.cpp | 59 ++++++++++- src/check_decl.cpp | 122 +++++++++++++++-------- src/check_expr.cpp | 73 ++++++++++++++ src/checker.cpp | 8 ++ src/checker_builtin_procs.hpp | 4 +- src/entity.cpp | 4 + src/llvm_backend.cpp | 170 ++++++++++++++++++++++---------- src/llvm_backend_proc.cpp | 22 +++-- src/llvm_backend_utility.cpp | 212 ++++++++++++++++++++++++++++++++++------ src/parser.hpp | 3 +- src/types.cpp | 11 +++ 13 files changed, 569 insertions(+), 145 deletions(-) (limited to 'src/checker.cpp') diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 2d940cf67..dd508180d 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -374,10 +374,11 @@ objc_selector :: struct{} objc_class :: struct{} objc_ivar :: struct{} -objc_id :: ^objc_object -objc_SEL :: ^objc_selector -objc_Class :: ^objc_class -objc_Ivar :: ^objc_ivar +objc_id :: ^objc_object +objc_SEL :: ^objc_selector +objc_Class :: ^objc_class +objc_Ivar :: ^objc_ivar +objc_instancetype :: distinct objc_id objc_find_selector :: proc($name: string) -> objc_SEL --- objc_register_selector :: proc($name: string) -> objc_SEL --- @@ -385,6 +386,7 @@ objc_find_class :: proc($name: string) -> objc_Class --- objc_register_class :: proc($name: string) -> objc_Class --- objc_ivar_get :: proc(self: ^$T) -> ^$U --- objc_block :: proc(invoke: $T, ..any) -> ^Objc_Block(T) where type_is_proc(T) --- +objc_super :: proc(obj: ^$T) -> ^$U where type_is_subtype_of(T, objc_object) && type_is_subtype_of(U, objc_object) --- valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2, a3, a4: uintptr) -> uintptr --- diff --git a/base/runtime/procs_darwin.odin b/base/runtime/procs_darwin.odin index d176f0f63..cc3dabc9b 100644 --- a/base/runtime/procs_darwin.odin +++ b/base/runtime/procs_darwin.odin @@ -15,16 +15,23 @@ objc_SEL :: ^intrinsics.objc_selector objc_Ivar :: ^intrinsics.objc_ivar objc_BOOL :: bool +objc_super :: struct { + receiver: objc_id, + super_class: objc_Class, +} objc_IMP :: proc "c" (object: objc_id, sel: objc_SEL, #c_vararg args: ..any) -> objc_id foreign ObjC { sel_registerName :: proc "c" (name: cstring) -> objc_SEL --- - objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) --- - objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 --- - objc_msgSend_fp2ret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> complex128 --- - objc_msgSend_stret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) --- + objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) --- + objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 --- + objc_msgSend_fp2ret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> complex128 --- + objc_msgSend_stret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) --- + objc_msgSendSuper2 :: proc "c" (super: ^objc_super, op: objc_SEL, #c_vararg args: ..any) --- + objc_msgSendSuper2_stret :: proc "c" (super: ^objc_super, op: objc_SEL, #c_vararg args: ..any) --- + objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class --- objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) -> objc_Class --- @@ -33,6 +40,7 @@ foreign ObjC { class_addIvar :: proc "c" (cls: objc_Class, name: cstring, size: uint, alignment: u8, types: cstring) -> objc_BOOL --- class_getInstanceVariable :: proc "c" (cls : objc_Class, name: cstring) -> objc_Ivar --- class_getInstanceSize :: proc "c" (cls : objc_Class) -> uint --- + class_getSuperclass :: proc "c" (cls : objc_Class) -> objc_Class --- ivar_getOffset :: proc "c" (v: objc_Ivar) -> uintptr --- object_getClass :: proc "c" (obj: objc_id) -> objc_Class --- } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 7e1567750..f142f04b7 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -210,7 +210,7 @@ gb_internal ObjcMsgKind get_objc_proc_kind(Type *return_type) { return ObjcMsg_normal; } -gb_internal void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice param_types) { +void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice param_types) { ObjcMsgKind kind = get_objc_proc_kind(return_type); Scope *scope = create_scope(c->info, nullptr); @@ -248,6 +248,12 @@ gb_internal void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_t try_to_add_package_dependency(c, "runtime", "objc_msgSend_fpret"); try_to_add_package_dependency(c, "runtime", "objc_msgSend_fp2ret"); try_to_add_package_dependency(c, "runtime", "objc_msgSend_stret"); + + Slice args = call->CallExpr.args; + if (args.count > 0 && args[0]->tav.objc_super_target) { + try_to_add_package_dependency(c, "runtime", "objc_msgSendSuper2"); + try_to_add_package_dependency(c, "runtime", "objc_msgSendSuper2_stret"); + } } gb_internal bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) { @@ -466,8 +472,8 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan isize capture_arg_count = ce->args.count - 1; - // NOTE(harold): The first parameter is already checked at check_builtin_procedure(). - // Checking again would invalidate the Entity -> Value map for direct parameters if it's the handler proc. + // NOTE(harold): The first argument is already checked at check_builtin_procedure(). + // Checking again would invalidate the Entity -> Value map for direct arguments if it's the handler proc. param_operands[0] = *operand; for (isize i = 0; i < ce->args.count-1; i++) { @@ -680,6 +686,52 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan operand->mode = Addressing_Value; return true; } break; + + case BuiltinProc_objc_super: + { + // Must be a pointer to an Objective-C object. + Type *objc_obj = operand->type; + if (!is_type_objc_ptr_to_object(objc_obj)) { + gbString e = expr_to_string(operand->expr); + gbString t = type_to_string(objc_obj); + error(operand->expr, "'%.*s' expected a pointer to an Objective-C object, but got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + if (operand->mode != Addressing_Value && operand->mode != Addressing_Variable) { + gbString e = expr_to_string(operand->expr); + gbString t = type_to_string(operand->type); + error(operand->expr, "'%.*s' expression '%s', of type %s, must be a value or variable.", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + Type *obj_type = type_deref(objc_obj); + GB_ASSERT(obj_type->kind == Type_Named); + + // NOTE(harold) Track original type before transforming it to the superclass. + // This is needed because objc_msgSendSuper2 must start its search on the subclass, not the superclass. + call->tav.objc_super_target = obj_type; + + // The superclass type must be known at compile time. We require this so that the selector method expressions + // methods are resolved to the superclass's methods instead of the subclass's. + Type *superclass = obj_type->Named.type_name->TypeName.objc_superclass; + if (superclass == nullptr) { + gbString t = type_to_string(obj_type); + error(operand->expr, "'%.*s' target object '%.*s' does not have an Objective-C superclass. One must be set via the @(objc_superclass) attribute", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + GB_ASSERT(superclass->Named.type_name->TypeName.objc_class_name.len > 0); + + operand->type = alloc_type_pointer(superclass); + return true; + + } break; } } @@ -2515,6 +2567,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_objc_register_class: case BuiltinProc_objc_ivar_get: case BuiltinProc_objc_block: + case BuiltinProc_objc_super: return check_builtin_objc_procedure(c, operand, call, id, type_hint); case BuiltinProc___entry_point: diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 842f8653c..113c1e171 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -587,9 +587,7 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, super = named_type->Named.type_name->TypeName.objc_superclass; } } else { - if (ac.objc_superclass != nullptr) { - error(e->token, "@(objc_superclass) may only be applied when the @(obj_implement) attribute is also applied"); - } else if (ac.objc_ivar != nullptr) { + if (ac.objc_ivar != nullptr) { error(e->token, "@(objc_ivar) may only be applied when the @(obj_implement) attribute is also applied"); } else if (ac.objc_context_provider != nullptr) { error(e->token, "@(objc_context_provider) may only be applied when the @(obj_implement) attribute is also applied"); @@ -1040,61 +1038,100 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon // Enable implementation by default if the class is an implementer too and // @objc_implement was not set to false explicitly in this proc. bool implement = tn->TypeName.objc_is_implementation; + if( ac.objc_is_implementation && !tn->TypeName.objc_is_implementation ) { + error(e->token, "Cannot apply @(objc_is_implement) to a procedure whose type does not also have @(objc_is_implement) set"); + } + if (ac.objc_is_disabled_implement) { implement = false; } - if (implement) { - GB_ASSERT(e->kind == Entity_Procedure); + String objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name; + + if (e->kind == Entity_Procedure) { + bool has_body = e->decl_info->proc_lit->ProcLit.body != nullptr; + e->Procedure.is_objc_impl_or_import = implement || !has_body; + e->Procedure.is_objc_class_method = ac.objc_is_class_method; + e->Procedure.objc_selector_name = objc_selector; + e->Procedure.objc_class = tn; auto &proc = e->type->Proc; Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil; - if (!tn->TypeName.objc_is_implementation) { - error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied"); - } else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) { - error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)"); - } else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) { - error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set"); - } else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) { - error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention"); - } else if (proc.result_count > 1) { - error(e->token, "Objective-C method implementations may return at most 1 value"); - } else { - // Always export unconditionally - // NOTE(harold): This means check_objc_methods() MUST be called before - // e->Procedure.is_export is set in check_proc_decl()! - if (ac.is_export) { - error(e->token, "Explicit export not allowed when @(objc_implement) is set. It set exported implicitly"); - } - if (ac.link_name != "") { - error(e->token, "Explicit linkage not allowed when @(objc_implement) is set. It set to \"strong\" implicitly"); - } + if (implement) { + if( !has_body ) { + error(e->token, "Procedures with @(objc_is_implement) must have a body"); + } else if (!tn->TypeName.objc_is_implementation) { + error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied"); + } else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) { + error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)"); + } else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) { + error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set"); + } else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) { + error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention"); + } else if (proc.result_count > 1) { + error(e->token, "Objective-C method implementations may return at most 1 value"); + } else { + // Always export unconditionally + // NOTE(harold): This means check_objc_methods() MUST be called before + // e->Procedure.is_export is set in check_proc_decl()! + if (ac.is_export) { + error(e->token, "Explicit export not allowed when @(objc_implement) is set. It set exported implicitly"); + } + if (ac.link_name != "") { + error(e->token, "Explicit linkage not allowed when @(objc_implement) is set. It set to \"strong\" implicitly"); + } - ac.is_export = true; - ac.linkage = STR_LIT("strong"); + ac.is_export = true; + ac.linkage = STR_LIT("strong"); - auto method = ObjcMethodData{ ac, e }; - method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name; + auto method = ObjcMethodData{ ac, e }; + method.ac.objc_selector = objc_selector; - CheckerInfo *info = ctx->info; - mutex_lock(&info->objc_method_mutex); - defer (mutex_unlock(&info->objc_method_mutex)); + CheckerInfo *info = ctx->info; + mutex_lock(&info->objc_method_mutex); + defer (mutex_unlock(&info->objc_method_mutex)); - Array* method_list = map_get(&info->objc_method_implementations, t); - if (method_list) { - array_add(method_list, method); - } else { - auto list = array_make(permanent_allocator(), 1, 8); - list[0] = method; + Array* method_list = map_get(&info->objc_method_implementations, t); + if (method_list) { + array_add(method_list, method); + } else { + auto list = array_make(permanent_allocator(), 1, 8); + list[0] = method; - map_set(&info->objc_method_implementations, t, list); + map_set(&info->objc_method_implementations, t, list); + } + } + } else if (!has_body) { + if (ac.objc_selector == "The @(objc_selector) attribute is required for imported Objective-C methods.") { + return; + } else if (proc.calling_convention != ProcCC_CDecl) { + error(e->token, "Imported Objective-C methods must use the \"c\" calling convention"); + return; + } else if (tn->TypeName.objc_context_provider) { + error(e->token, "Imported Objective-C class '%.*s' must not declare context providers.", tn->type->Named.name); + return; + } else if (tn->TypeName.objc_is_implementation) { + error(e->token, "Imported Objective-C methods used in a class with @(objc_implement) is not allowed."); + return; + } else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) { + error(e->token, "Objective-C instance methods require the first parameter to be a pointer to the class type set by @(objc_type)"); + return; } } - } else if (ac.objc_selector != "") { - error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C implementations."); + else if(ac.objc_selector != "") { + error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C method implementations or are imported."); + return; + } + } else { + GB_ASSERT(e->kind == Entity_ProcGroup); + if (tn->TypeName.objc_is_implementation) { + error(e->token, "Objective-C procedure groups cannot use the @(objc_implement) attribute."); + return; + } } + mutex_lock(&global_type_name_objc_metadata_mutex); defer (mutex_unlock(&global_type_name_objc_metadata_mutex)); @@ -1479,7 +1516,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { if (!pt->is_polymorphic) { check_procedure_later(ctx->checker, ctx->file, e->token, d, proc_type, pl->body, pl->tags); } - } else if (!is_foreign) { + } else if (!is_foreign && !e->Procedure.is_objc_impl_or_import) { if (e->Procedure.is_export) { error(e->token, "Foreign export procedures must have a body"); } else { @@ -1527,6 +1564,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { // NOTE(bill): this must be delayed because the foreign import paths might not be evaluated yet until much later mpsc_enqueue(&ctx->info->foreign_decls_to_check, e); } else { + // TODO(harold): Check if it's an objective-C foreign, if so, I don't think we need to check it. check_foreign_procedure(ctx, e, d); } } else { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2a22e5c48..5f36bf3a1 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8151,6 +8151,73 @@ gb_internal ExprKind check_call_expr_as_type_cast(CheckerContext *c, Operand *op } +void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice param_types); + +gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast *call, Entity *proc_entity, Type *proc_type) { + auto &proc = proc_type->Proc; + Slice params = proc.params ? proc.params->Tuple.variables : Slice{}; + + Type *self_type = nullptr; + isize params_start = 1; + + ast_node(ce, CallExpr, call); + + Type *return_type = proc.result_count == 0 ? nullptr : proc.results->Tuple.variables[0]->type; + bool is_return_instancetype = return_type != nullptr && return_type == t_objc_instancetype; + + if (params.count == 0 || !is_type_objc_ptr_to_object(params[0]->type)) { + if (!proc_entity->Procedure.is_objc_class_method) { + // Not a class method, invalid call + error(call, "Invalid Objective-C call: The Objective-C method is not a class method but this first parameter is not an Objective-C object pointer."); + return; + } + + if (is_return_instancetype) { + if (ce->proc->kind == Ast_SelectorExpr) { + ast_node(se, SelectorExpr, ce->proc); + + // NOTE(harold): These should have already been checked, right? + GB_ASSERT(se->expr->tav.mode == Addressing_Type && se->expr->tav.type->kind == Type_Named); + + return_type = alloc_type_pointer(se->expr->tav.type); + } else { + return_type = proc_entity->Procedure.objc_class->type; + } + } + + self_type = t_objc_Class; + params_start = 0; + } else if (ce->args.count > 0) { + GB_ASSERT(is_type_objc_ptr_to_object(params[0]->type)); + + if (ce->args[0]->tav.objc_super_target) { + self_type = t_objc_super_ptr; + } else { + self_type = ce->args[0]->tav.type; + } + + if (is_return_instancetype) { + // NOTE(harold): These should have already been checked, right? + GB_ASSERT(ce->args[0]->tav.type && ce->args[0]->tav.type->kind == Type_Pointer && ce->args[0]->tav.type->Pointer.elem->kind == Type_Named); + + return_type = ce->args[0]->tav.type; + } + } + + auto param_types = slice_make(permanent_allocator(), proc.param_count + 2 - params_start); + param_types[0] = self_type; + param_types[1] = t_objc_SEL; + + for (isize i = params_start; i < params.count; i++) { + param_types[i+2-params_start] = params[i]->type; + } + + if (is_return_instancetype) { + operand->type = return_type; + } + + add_objc_proc_type(c, call, return_type, param_types); +} gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice const &args, ProcInlining inlining, Type *type_hint) { if (proc != nullptr && @@ -8414,6 +8481,12 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } + Entity *proc_entity = entity_from_expr(call->CallExpr.proc); + bool is_objc_call = proc_entity && proc_entity->kind == Entity_Procedure && proc_entity->Procedure.is_objc_impl_or_import; + if (is_objc_call) { + check_objc_call_expr(c, operand, call, proc_entity, pt); + } + return Expr_Expr; } diff --git a/src/checker.cpp b/src/checker.cpp index 32bda2e43..d3c111de4 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1416,6 +1416,8 @@ gb_internal void init_universal(void) { t_objc_SEL = alloc_type_pointer(t_objc_selector); t_objc_Class = alloc_type_pointer(t_objc_class); t_objc_Ivar = alloc_type_pointer(t_objc_ivar); + + t_objc_instancetype = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_instancetype"), t_objc_id); } } @@ -3386,12 +3388,18 @@ gb_internal void init_core_map_type(Checker *c) { t_raw_map_ptr = alloc_type_pointer(t_raw_map); } +gb_internal void init_core_objc_c(Checker *c) { + t_objc_super = find_core_type(c, str_lit("objc_super")); + t_objc_super_ptr = alloc_type_pointer(t_objc_super); +} + gb_internal void init_preload(Checker *c) { init_core_type_info(c); init_mem_allocator(c); init_core_context(c); init_core_source_code_location(c); init_core_map_type(c); + init_core_objc_c(c); } gb_internal ExactValue check_decl_attribute_value(CheckerContext *c, Ast *value) { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index c6071bf98..c2255a6ba 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -354,6 +354,7 @@ BuiltinProc__type_end, BuiltinProc_objc_register_class, BuiltinProc_objc_ivar_get, BuiltinProc_objc_block, + BuiltinProc_objc_super, BuiltinProc_constant_utf16_cstring, @@ -715,7 +716,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("objc_ivar_get"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, - {STR_LIT("objc_block"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("objc_block"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("objc_super"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/entity.cpp b/src/entity.cpp index d6d8f58de..2b21fdcac 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -251,6 +251,8 @@ struct Entity { String link_name; String link_prefix; String link_suffix; + String objc_selector_name; + Entity *objc_class; DeferredProcedure deferred_procedure; struct GenProcsData *gen_procs; @@ -266,6 +268,8 @@ struct Entity { bool is_anonymous : 1; bool no_sanitize_address : 1; bool no_sanitize_memory : 1; + bool is_objc_impl_or_import : 1; + bool is_objc_class_method : 1; } Procedure; struct { Array entities; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index b47e2788f..86c83b91f 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1417,8 +1417,21 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) { return str_lit("?"); case Type_Proc: return str_lit("?"); - case Type_BitSet: - return lb_get_objc_type_encoding(t->BitSet.underlying, pointer_depth); + case Type_BitSet: { + Type *bitset_integer_type = t->BitSet.underlying; + if (!bitset_integer_type) { + switch (t->cached_size) { + case 1: bitset_integer_type = t_u8; break; + case 2: bitset_integer_type = t_u16; break; + case 4: bitset_integer_type = t_u32; break; + case 8: bitset_integer_type = t_u64; break; + case 16: bitset_integer_type = t_u128; break; + } + } + GB_ASSERT_MSG(bitset_integer_type, "Could not determine bit_set integer size for objc_type_encoding"); + + return lb_get_objc_type_encoding(bitset_integer_type, pointer_depth); + } case Type_SimdVector: { String type_str = lb_get_objc_type_encoding(t->SimdVector.elem, pointer_depth); @@ -1452,7 +1465,10 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) { struct lbObjCGlobalClass { lbObjCGlobal g; - lbValue class_value; // Local registered class value + union { + lbValue class_value; // Local registered class value + lbAddr class_global; // Global class pointer. Placeholder for class implementations which are registered in order of definition. + }; }; gb_internal void lb_register_objc_thing( @@ -1482,44 +1498,43 @@ gb_internal void lb_register_objc_thing( LLVMSetInitializer(v.value, LLVMConstNull(t)); } - lbValue class_ptr = {}; - lbValue class_name = lb_const_value(m, t_cstring, exact_value_string(g.name)); - // If this class requires an implementation, save it for registration below. if (g.class_impl_type != nullptr) { // Make sure the superclass has been initialized before us - lbValue superclass_value = lb_const_nil(m, t_objc_Class); - auto &tn = g.class_impl_type->Named.type_name->TypeName; Type *superclass = tn.objc_superclass; if (superclass != nullptr) { auto& superclass_global = string_map_must_get(&class_map, superclass->Named.type_name->TypeName.objc_class_name); lb_register_objc_thing(handled, m, args, class_impls, class_map, p, superclass_global.g, call); - GB_ASSERT(superclass_global.class_value.value); - - superclass_value = superclass_global.class_value; + GB_ASSERT(superclass_global.class_global.addr.value); } - args.count = 3; - args[0] = superclass_value; - args[1] = class_name; - args[2] = lb_const_int(m, t_uint, 0); - class_ptr = lb_emit_runtime_call(p, "objc_allocateClassPair", args); + lbObjCGlobalClass impl_global = {}; + impl_global.g = g; + impl_global.class_global = addr; - array_add(&class_impls, lbObjCGlobalClass{g, class_ptr}); + array_add(&class_impls, impl_global); + + lbObjCGlobalClass* class_global = string_map_get(&class_map, g.name); + if (class_global != nullptr) { + class_global->class_global = addr; + } } else { + lbValue class_ptr = {}; + lbValue class_name = lb_const_value(m, t_cstring, exact_value_string(g.name)); + args.count = 1; args[0] = class_name; class_ptr = lb_emit_runtime_call(p, call, args); - } - lb_addr_store(p, addr, class_ptr); + lb_addr_store(p, addr, class_ptr); - lbObjCGlobalClass* class_global = string_map_get(&class_map, g.name); - if (class_global != nullptr) { - class_global->class_value = class_ptr; + lbObjCGlobalClass* class_global = string_map_get(&class_map, g.name); + if (class_global != nullptr) { + class_global->class_value = class_ptr; + } } } @@ -1582,7 +1597,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { string_map_init(&global_class_map, (usize)gen->objc_classes.count); defer (string_map_destroy(&global_class_map)); - for (lbObjCGlobal g :referenced_classes) { + for (lbObjCGlobal g : referenced_classes) { string_map_set(&global_class_map, g.name, lbObjCGlobalClass{g}); } @@ -1629,9 +1644,36 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { for (const auto &cd : class_impls) { auto &g = cd.g; - Type *class_type = g.class_impl_type; + + Type *class_type = g.class_impl_type; Type *class_ptr_type = alloc_type_pointer(class_type); - lbValue class_value = cd.class_value; + + // Begin class registration: create class pair and update global reference + lbValue class_value = {}; + + { + lbValue superclass_value = lb_const_nil(m, t_objc_Class); + + auto& tn = class_type->Named.type_name->TypeName; + Type *superclass = tn.objc_superclass; + + if (superclass != nullptr) { + auto& superclass_global = string_map_must_get(&global_class_map, superclass->Named.type_name->TypeName.objc_class_name); + superclass_value = superclass_global.class_value; + } + + args.count = 3; + args[0] = superclass_value; + args[1] = lb_const_value(m, t_cstring, exact_value_string(g.name)); + args[2] = lb_const_int(m, t_uint, 0); + class_value = lb_emit_runtime_call(p, "objc_allocateClassPair", args); + + lbObjCGlobalClass &mapped_global = string_map_must_get(&global_class_map, tn.objc_class_name); + lb_addr_store(p, mapped_global.class_global, class_value); + + mapped_global.class_value = class_value; + } + Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar; @@ -1651,7 +1693,6 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { is_context_provider_ivar = ivar_type != nullptr && internal_check_is_assignable_to(contex_provider_self_named_type, ivar_type); } - Array *methods = map_get(&m->info->objc_method_implementations, class_type); if (!methods) { continue; @@ -1710,17 +1751,21 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { wrapper_results_tuple, method_type->Proc.result_count, false, ProcCC_CDecl); lbProcedure *wrapper_proc = lb_create_dummy_procedure(m, proc_name, wrapper_proc_type); - lb_add_attribute_to_proc(wrapper_proc->module, wrapper_proc->value, "nounwind"); + + lb_add_function_type_attributes(wrapper_proc->value, lb_get_function_type(m, wrapper_proc_type), ProcCC_CDecl); // Emit the wrapper - LLVMSetLinkage(wrapper_proc->value, LLVMExternalLinkage); + // LLVMSetLinkage(wrapper_proc->value, LLVMInternalLinkage); + LLVMSetDLLStorageClass(wrapper_proc->value, LLVMDLLExportStorageClass); + lb_add_attribute_to_proc(wrapper_proc->module, wrapper_proc->value, "nounwind"); + lb_begin_procedure_body(wrapper_proc); { + LLVMValueRef context_addr = nullptr; if (method_type->Proc.calling_convention == ProcCC_Odin) { GB_ASSERT(context_provider); // Emit the get odin context call - get_context_args[0] = lbValue { wrapper_proc->raw_input_parameters[0], contex_provider_self_ptr_type, @@ -1736,44 +1781,58 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { get_context_args[0] = lb_handle_objc_ivar_for_objc_object_pointer(wrapper_proc, real_self); } - lbValue context = lb_emit_call(wrapper_proc, context_provider_proc_value, get_context_args); - lbAddr context_addr = lb_addr(lb_address_from_load_or_generate_local(wrapper_proc, context)); - lb_push_context_onto_stack(wrapper_proc, context_addr); + lbValue context = lb_emit_call(wrapper_proc, context_provider_proc_value, get_context_args); + context_addr = lb_address_from_load(wrapper_proc, context).value;//lb_address_from_load_or_generate_local(wrapper_proc, context)); + // context_addr = LLVMGetOperand(context.value, 0); } + isize method_forward_arg_count = method_param_count + method_param_offset; + isize method_forward_return_arg_offset = 0; + auto raw_method_args = array_make(temporary_allocator(), 0, method_forward_arg_count+1); - auto method_call_args = array_make(temporary_allocator(), method_param_count + method_param_offset); + lbValue method_proc_value = lb_find_procedure_value_from_entity(m, md.proc_entity); + lbFunctionType* ft = lb_get_function_type(m, method_type); + bool has_return = false; + lbArgKind return_kind = {}; + + if (wrapper_results_tuple != nullptr) { + has_return = true; + return_kind = ft->ret.kind; + + if (return_kind == lbArg_Indirect) { + method_forward_return_arg_offset = 1; + array_add(&raw_method_args, wrapper_proc->return_ptr.addr.value); + } + } if (!md.ac.objc_is_class_method) { - method_call_args[0] = lbValue { - wrapper_proc->raw_input_parameters[0], - class_ptr_type, - }; + array_add(&raw_method_args, wrapper_proc->raw_input_parameters[method_forward_return_arg_offset]); } for (isize i = 0; i < method_param_count; i++) { - method_call_args[i+method_param_offset] = lbValue { - wrapper_proc->raw_input_parameters[i+2], - method_type->Proc.params->Tuple.variables[i+method_param_offset]->type, - }; + array_add(&raw_method_args, wrapper_proc->raw_input_parameters[i+2+method_forward_return_arg_offset]); + } + + if (method_type->Proc.calling_convention == ProcCC_Odin) { + array_add(&raw_method_args, context_addr); } - lbValue method_proc_value = lb_find_procedure_value_from_entity(m, md.proc_entity); // Call real procedure for method from here, passing the parameters expected, if any. - lbValue return_value = lb_emit_call(wrapper_proc, method_proc_value, method_call_args); + LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(m, method_type); + LLVMValueRef ret_val_raw = LLVMBuildCall2(wrapper_proc->builder, fnp, method_proc_value.value, raw_method_args.data, (unsigned)raw_method_args.count, ""); - if (wrapper_results_tuple != nullptr) { - auto &result_var = method_type->Proc.results->Tuple.variables[0]; - return_value = lb_emit_conv(wrapper_proc, return_value, result_var->type); - lb_build_return_stmt_internal(wrapper_proc, return_value, result_var->token.pos); + if (has_return && return_kind != lbArg_Indirect) { + LLVMBuildRet(wrapper_proc->builder, ret_val_raw); + } + else { + LLVMBuildRetVoid(wrapper_proc->builder); } } lb_end_procedure_body(wrapper_proc); - // Add the method to the class String method_encoding = str_lit("v"); - // TODO (harold): Checker must ensure that objc_methods have a single return value or none! + GB_ASSERT(method_type->Proc.result_count <= 1); if (method_type->Proc.result_count != 0) { method_encoding = lb_get_objc_type_encoding(method_type->Proc.results->Tuple.variables[0]->type); @@ -1785,8 +1844,8 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("#:")); } - for (isize i = method_param_offset; i < method_param_count; i++) { - Type *param_type = method_type->Proc.params->Tuple.variables[i]->type; + for (isize i = 0; i < method_param_count; i++) { + Type *param_type = method_type->Proc.params->Tuple.variables[i + method_param_offset]->type; String param_encoding = lb_get_objc_type_encoding(param_type); method_encoding = concatenate_strings(temporary_allocator(), method_encoding, param_encoding); @@ -1805,7 +1864,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { args[2] = lbValue { wrapper_proc->value, wrapper_proc->type }; args[3] = lb_const_value(m, t_cstring, exact_value_string(method_encoding)); - // TODO(harold): Emit check BOOL result and panic if false. + // TODO(harold): Emit check BOOL result and panic if false? lb_emit_runtime_call(p, "class_addMethod", args); } // End methods @@ -1853,7 +1912,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { // Defined in an external package, define it now in the main package LLVMTypeRef t = lb_type(m, t_int); - lbValue global{}; + lbValue global = {}; global.value = LLVMAddGlobal(m->mod, t, g.global_name); global.type = t_int_ptr; @@ -2192,6 +2251,11 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker GB_ASSERT(m != nullptr); if (e->kind == Entity_Procedure) { + if (e->Procedure.is_foreign && e->Procedure.is_objc_impl_or_import) { + // Do not generate declarations for foreign Objective-C methods. These are called indirectly through the Objective-C runtime. + continue; + } + array_add(&m->global_procedures_to_create, e); } else if (e->kind == Entity_TypeName) { array_add(&m->global_types_to_create, e); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 7680c5e76..3bd5f4ef2 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3753,6 +3753,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr); case BuiltinProc_objc_ivar_get: return lb_handle_objc_ivar_get(p, expr); case BuiltinProc_objc_block: return lb_handle_objc_block(p, expr); + case BuiltinProc_objc_super: return lb_handle_objc_super(p, expr); case BuiltinProc_constant_utf16_cstring: @@ -4122,21 +4123,23 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { } Ast *proc_expr = unparen_expr(ce->proc); + Entity *proc_entity = entity_of_node(proc_expr); + if (proc_mode == Addressing_Builtin) { - Entity *e = entity_of_node(proc_expr); BuiltinProcId id = BuiltinProc_Invalid; - if (e != nullptr) { - id = cast(BuiltinProcId)e->Builtin.id; + if (proc_entity != nullptr) { + id = cast(BuiltinProcId)proc_entity->Builtin.id; } else { id = BuiltinProc_DIRECTIVE; } return lb_build_builtin_proc(p, expr, tv, id); } + bool is_objc_call = proc_entity->Procedure.is_objc_impl_or_import; + // NOTE(bill): Regular call lbValue value = {}; - Entity *proc_entity = entity_of_node(proc_expr); if (proc_entity != nullptr) { if (proc_entity->flags & EntityFlag_Disabled) { GB_ASSERT(tv.type == nullptr); @@ -4170,11 +4173,13 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { } } - if (value.value == nullptr) { + if (is_objc_call) { + value.type = proc_tv.type; + } else if (value.value == nullptr) { value = lb_build_expr(p, proc_expr); } - GB_ASSERT(value.value != nullptr); + GB_ASSERT(value.value != nullptr || is_objc_call); Type *proc_type_ = base_type(value.type); GB_ASSERT(proc_type_->kind == Type_Proc); TypeProc *pt = &proc_type_->Proc; @@ -4402,6 +4407,11 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { isize final_count = is_c_vararg ? args.count : pt->param_count; auto call_args = array_slice(args, 0, final_count); + + if (is_objc_call) { + return lb_handle_objc_auto_send(p, expr, slice(call_args, 0, call_args.count)); + } + return lb_emit_call(p, value, call_args, ce->inlining); } diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 7fe6b1458..d124f164e 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2373,7 +2373,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) { /// https://www.newosxbook.com/src.php?tree=xnu&file=/libkern/libkern/Block_private.h /// https://github.com/llvm/llvm-project/blob/21f1f9558df3830ffa637def364e3c0cb0dbb3c0/compiler-rt/lib/BlocksRuntime/Block_private.h /// https://github.com/apple-oss-distributions/libclosure/blob/3668b0837f47be3cc1c404fb5e360f4ff178ca13/runtime.cpp - + // TODO(harold): Ensure we don't have any issues with large struct arguments or returns in block wrappers. ast_node(ce, CallExpr, expr); GB_ASSERT(ce->args.count > 0); @@ -2452,7 +2452,9 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) { lbProcedure *invoker_proc = lb_create_dummy_procedure(m, make_string((u8*)block_invoker_name, gb_string_length(block_invoker_name)), invoker_proc_type); + LLVMSetLinkage(invoker_proc->value, LLVMPrivateLinkage); + lb_add_function_type_attributes(invoker_proc->value, lb_get_function_type(m, invoker_proc_type), ProcCC_CDecl); // Create the block descriptor and block literal gbString block_lit_type_name = gb_string_make(temporary_allocator(), "__$ObjC_Block_Literal_"); @@ -2531,45 +2533,66 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) { /// Invoker body lb_begin_procedure_body(invoker_proc); { - auto call_args = array_make(temporary_allocator(), user_proc.param_count, user_proc.param_count); + // Reserve 2 extra arguments for: Indirect return values and context. + auto call_args = array_make(temporary_allocator(), 0, user_proc.param_count + 2); - for (isize i = 1; i < invoker_proc->raw_input_parameters.count; i++) { - lbValue arg = {}; - arg.type = invoker_args[i]; - arg.value = invoker_proc->raw_input_parameters[i], - call_args[i-1] = arg; - } + isize block_literal_arg_index = 0; - LLVMValueRef block_literal = invoker_proc->raw_input_parameters[0]; + lbFunctionType* user_proc_ft = lb_get_function_type(m, user_proc_value.type); - // Push context, if needed - if (user_proc.calling_convention == ProcCC_Odin) { - LLVMValueRef p_context = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, 5, "context"); - lbValue ctx_val = {}; - ctx_val.type = t_context_ptr; - ctx_val.value = p_context; + lbArgKind return_kind = {}; + + GB_ASSERT(user_proc.result_count <= 1); + if (user_proc.result_count > 0) { + return_kind = user_proc_ft->ret.kind; + + if (return_kind == lbArg_Indirect) { + // Forward indirect return value + array_add(&call_args, invoker_proc->raw_input_parameters[0]); + block_literal_arg_index = 1; + } + } - lb_push_context_onto_stack(invoker_proc, lb_addr(ctx_val)); + // Forward raw arguments + for (isize i = block_literal_arg_index+1; i < invoker_proc->raw_input_parameters.count; i++) { + array_add(&call_args, invoker_proc->raw_input_parameters[i]); } + LLVMValueRef block_literal = invoker_proc->raw_input_parameters[block_literal_arg_index]; + // Copy capture parameters from the block literal + isize capture_arg_in_user_proc_start_index = user_proc_ft->args.count - capture_arg_count; + if (user_proc.calling_convention == ProcCC_Odin) { + capture_arg_in_user_proc_start_index -= 1; + } + for (isize i = 0; i < capture_arg_count; i++) { LLVMValueRef cap_value = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, unsigned(capture_fields_offset + i), ""); - lbValue cap_arg = {}; - cap_arg.value = cap_value; - cap_arg.type = alloc_type_pointer(captured_values[i].type); + // Don't emit load if indirect. Pass the pointer as-is + isize cap_arg_index_in_user_proc = capture_arg_in_user_proc_start_index + i; - lbValue arg = lb_emit_load(invoker_proc, cap_arg); - call_args[block_forward_args+i] = arg; + if (user_proc_ft->args[cap_arg_index_in_user_proc].kind != lbArg_Indirect) { + cap_value = OdinLLVMBuildLoad(invoker_proc, lb_type(invoker_proc->module, captured_values[i].type), cap_value); + } + + array_add(&call_args, cap_value); } - lbValue result = lb_emit_call(invoker_proc, user_proc_value, call_args, proc_lit->ProcLit.inlining); + // Push context, if needed + if (user_proc.calling_convention == ProcCC_Odin) { + LLVMValueRef p_context = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, 5, "context"); + array_add(&call_args, p_context); + } - GB_ASSERT(user_proc.result_count <= 1); - if (user_proc.result_count > 0) { - GB_ASSERT(result.value != nullptr); - LLVMBuildRet(p->builder, result.value); + LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(m, user_proc_value.type); + LLVMValueRef ret_val = LLVMBuildCall2(invoker_proc->builder, fnp, user_proc_value.value, call_args.data, (unsigned)call_args.count, ""); + + if (user_proc.result_count > 0 && return_kind != lbArg_Indirect) { + LLVMBuildRet(invoker_proc->builder, ret_val); + } + else { + LLVMBuildRetVoid(invoker_proc->builder); } } lb_end_procedure_body(invoker_proc); @@ -2587,8 +2610,8 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) { gbString block_var_name = gb_string_make(temporary_allocator(), "__$objc_block_literal_"); block_var_name = gb_string_append_fmt(block_var_name, "%lld", m->objc_next_block_id); - lbValue result = {}; - result.type = block_result_type; + lbValue block_result = {}; + block_result.type = block_result_type; lbValue isa_val = lb_find_runtime_value(m, is_global ? str_lit("_NSConcreteGlobalBlock") : str_lit("_NSConcreteStackBlock")); lbValue flags_val = lb_const_int(m, t_i32, (u64)raw_flags); @@ -2596,7 +2619,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) { if (is_global) { LLVMValueRef p_block_lit = LLVMAddGlobal(m->mod, block_lit_type, block_var_name); - result.value = p_block_lit; + block_result.value = p_block_lit; LLVMValueRef fields_values[5] = { isa_val.value, // isa @@ -2611,7 +2634,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) { } else { LLVMValueRef p_block_lit = llvm_alloca(p, block_lit_type, lb_alignof(block_lit_type), block_var_name); - result.value = p_block_lit; + block_result.value = p_block_lit; // Initialize it LLVMValueRef f_isa = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 0, "isa"); @@ -2651,7 +2674,20 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) { } } - return result; + return block_result; +} + +gb_internal lbValue lb_handle_objc_block_invoke(lbProcedure *p, Ast *expr) { + return {}; +} + +gb_internal lbValue lb_handle_objc_super(lbProcedure *p, Ast *expr) { + ast_node(ce, CallExpr, expr); + GB_ASSERT(ce->args.count == 1); + + // NOTE(harold): This doesn't actually do anything by itself, + // it has an effect when used on the left side of a selector call expression. + return lb_build_expr(p, ce->args[0]); } gb_internal lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) { @@ -2767,6 +2803,120 @@ gb_internal lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) { return lb_emit_call(p, the_proc, args); } +gb_internal lbValue lb_handle_objc_auto_send(lbProcedure *p, Ast *expr, Slice const arg_values) { + ast_node(ce, CallExpr, expr); + + lbModule *m = p->module; + CheckerInfo *info = m->info; + ObjcMsgData data = map_must_get(&info->objc_msgSend_types, expr); + + Type *proc_type = data.proc_type; + GB_ASSERT(proc_type != nullptr); + + Entity *objc_method_ent = entity_of_node(ce->proc); + GB_ASSERT(objc_method_ent != nullptr); + GB_ASSERT(objc_method_ent->kind == Entity_Procedure); + GB_ASSERT(objc_method_ent->Procedure.objc_selector_name.len > 0); + + auto &proc = proc_type->Proc; + GB_ASSERT(proc.param_count >= 2); + + Type *objc_super_orig_type = nullptr; + if (ce->args.count > 0) { + objc_super_orig_type = unparen_expr(ce->args[0])->tav.objc_super_target; + } + + isize arg_offset = 1; + lbValue id = {}; + if (!objc_method_ent->Procedure.is_objc_class_method) { + GB_ASSERT(ce->args.count > 0); + id = arg_values[0]; + + if (objc_super_orig_type) { + GB_ASSERT(objc_super_orig_type->kind == Type_Named); + + auto& tn = objc_super_orig_type->Named.type_name->TypeName; + lbAddr p_supercls = lb_handle_objc_find_or_register_class(p, tn.objc_class_name, tn.objc_is_implementation ? objc_super_orig_type : nullptr); + + lbValue supercls = lb_addr_load(p, p_supercls); + lbAddr p_objc_super = lb_add_local_generated(p, t_objc_super, false); + + lbValue f_id = lb_emit_struct_ep(p, p_objc_super.addr, 0); + lbValue f_superclass = lb_emit_struct_ep(p, p_objc_super.addr, 1); + + id = lb_emit_conv(p, id, t_objc_id); + lb_emit_store(p, f_id, id); + lb_emit_store(p, f_superclass, supercls); + + id = p_objc_super.addr; + } + } else { + Entity *objc_class = objc_method_ent->Procedure.objc_class; + if (ce->proc->kind == Ast_SelectorExpr) { + // NOTE (harold): If called via a selector expression (ex: Foo.alloc()), then we should use + // the lhs-side to determine the class. This allows for class methods to be called + // with the correct class as the target, even when the method is defined in a superclass. + ast_node(se, SelectorExpr, ce->proc); + GB_ASSERT(se->expr->tav.mode == Addressing_Type && se->expr->tav.type->kind == Type_Named); + + objc_class = entity_from_expr(se->expr); + + GB_ASSERT(objc_class); + GB_ASSERT(objc_class->kind == Entity_TypeName); + GB_ASSERT(objc_class->TypeName.objc_class_name != ""); + } + + Type *class_impl_type = objc_class->TypeName.objc_is_implementation ? objc_class->type : nullptr; + + id = lb_addr_load(p, lb_handle_objc_find_or_register_class(p, objc_class->TypeName.objc_class_name, class_impl_type)); + arg_offset = 0; + } + + lbValue sel = lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, objc_method_ent->Procedure.objc_selector_name)); + + auto args = array_make(permanent_allocator(), 0, arg_values.count + 2 - arg_offset); + + array_add(&args, id); + array_add(&args, sel); + + for (isize i = arg_offset; i < ce->args.count; i++) { + array_add(&args, arg_values[i]); + } + + lbValue the_proc = {}; + + if (!objc_super_orig_type) { + switch (data.kind) { + default: + GB_PANIC("unhandled ObjcMsgKind %u", data.kind); + break; + case ObjcMsg_normal: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend")); break; + case ObjcMsg_fpret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fpret")); break; + case ObjcMsg_fp2ret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fp2ret")); break; + case ObjcMsg_stret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_stret")); break; + } + } else { + switch (data.kind) { + default: + GB_PANIC("unhandled ObjcMsgKind %u", data.kind); + break; + case ObjcMsg_normal: + case ObjcMsg_fpret: + case ObjcMsg_fp2ret: + the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSendSuper2")); + break; + case ObjcMsg_stret: + the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSendSuper2_stret")); + break; + } + } + + the_proc = lb_emit_conv(p, the_proc, data.proc_type); + + return lb_emit_call(p, the_proc, args); +} + + gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) { GB_ASSERT(value.kind == ExactValue_Integer); i64 v = exact_value_to_i64(value); diff --git a/src/parser.hpp b/src/parser.hpp index 979b44618..6127468d4 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -48,7 +48,8 @@ gb_global String const addressing_mode_strings[] = { struct TypeAndValue { Type * type; AddressingMode mode; - bool is_lhs; // Debug info + bool is_lhs; // Debug info + Type * objc_super_target; // Original type of the Obj-C object before being converted to the superclass' type by the objc_super() intrinsic. ExactValue value; }; diff --git a/src/types.cpp b/src/types.cpp index effa8ef64..372c2e991 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -752,11 +752,14 @@ gb_global Type *t_objc_object = nullptr; gb_global Type *t_objc_selector = nullptr; gb_global Type *t_objc_class = nullptr; gb_global Type *t_objc_ivar = nullptr; +gb_global Type *t_objc_super = nullptr; // Struct used in lieu of the 'self' instance when calling objc_msgSendSuper. +gb_global Type *t_objc_super_ptr = nullptr; gb_global Type *t_objc_id = nullptr; gb_global Type *t_objc_SEL = nullptr; gb_global Type *t_objc_Class = nullptr; gb_global Type *t_objc_Ivar = nullptr; +gb_global Type *t_objc_instancetype = nullptr; // Special distinct variant of t_objc_id used mimic auto-typing of instancetype* in Objective-C enum OdinAtomicMemoryOrder : i32 { OdinAtomicMemoryOrder_relaxed = 0, // unordered @@ -4735,6 +4738,14 @@ gb_internal bool is_type_objc_object(Type *t) { return internal_check_is_assignable_to(t, t_objc_object); } +gb_internal bool is_type_objc_ptr_to_object(Type *t) { + // NOTE (harold): is_type_objc_object() returns true if it's a pointer to an object or the object itself. + // This returns true ONLY if Type is a shallow pointer to an Objective-C object. + + Type *elem = type_deref(t); + return elem != t && elem->kind == Type_Named && is_type_objc_object(elem); +} + gb_internal Type *get_struct_field_type(Type *t, isize index) { t = base_type(type_deref(t)); GB_ASSERT(t->kind == Type_Struct); -- cgit v1.2.3 From fc44b104315ddac114ae756f5d3f1ed236636782 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Mon, 29 Sep 2025 20:20:54 -0400 Subject: Include objc runtime type dependencies only on darwin --- src/checker.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index d3c111de4..0a8590570 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3389,8 +3389,10 @@ gb_internal void init_core_map_type(Checker *c) { } gb_internal void init_core_objc_c(Checker *c) { - t_objc_super = find_core_type(c, str_lit("objc_super")); - t_objc_super_ptr = alloc_type_pointer(t_objc_super); + if (build_context.metrics.os == TargetOs_darwin) { + t_objc_super = find_core_type(c, str_lit("objc_super")); + t_objc_super_ptr = alloc_type_pointer(t_objc_super); + } } gb_internal void init_preload(Checker *c) { -- cgit v1.2.3 From 86f9c920dafc40c6bb1eb5cc5461b20ca954459c Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Sun, 5 Oct 2025 15:50:01 -0400 Subject: Fix Objective-C block symbols naming conflict across modules. Fix assert triggered when there's checker errors and Objective-C method implementations. --- src/checker.cpp | 5 ++++- src/llvm_backend.hpp | 2 +- src/llvm_backend_utility.cpp | 21 +++++++++++---------- 3 files changed, 16 insertions(+), 12 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 0a8590570..2a82203ef 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1501,9 +1501,12 @@ gb_internal void destroy_checker_info(CheckerInfo *i) { map_destroy(&i->objc_msgSend_types); string_set_destroy(&i->obcj_class_name_set); - mpsc_destroy(&i->objc_class_implementations); map_destroy(&i->objc_method_implementations); + // NOTE(harold): Disabling this: It can cause the 'count == 0' assert to trigger + // when there's checker errors and the queue is still full as it did not reach the generation stage. + // mpsc_destroy(&i->objc_class_implementations); + string_map_destroy(&i->load_file_cache); string_map_destroy(&i->load_directory_cache); map_destroy(&i->load_directory_map); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 6870f6259..6a4616694 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -206,7 +206,7 @@ struct lbModule { StringMap objc_classes; StringMap objc_selectors; StringMap objc_ivars; - isize objc_next_block_id; // Used to name objective-c blocks, per module + std::atomic objc_next_block_id; // Used to name objective-c blocks. IMPORTANT: Tracked on the main module PtrMap map_cell_info_map; // address of runtime.Map_Info PtrMap map_info_map; // address of runtime.Map_Cell_Info diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index ca7bf34e3..60dcf0992 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2271,12 +2271,12 @@ gb_internal lbValue lb_handle_objc_ivar_get(lbProcedure *p, Ast *expr) { } gb_internal void lb_create_objc_block_helper_procs( - lbModule *m, LLVMTypeRef block_lit_type, isize capture_field_offset, + lbModule *m, LLVMTypeRef block_lit_type, isize capture_field_offset, isize block_id, Slice capture_values, Slice objc_object_indices, lbProcedure *&out_copy_helper, lbProcedure *&out_dispose_helper ) { - gbString copy_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$objc_block_copy_helper_%lld", m->objc_next_block_id); - gbString dispose_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$objc_block_dispose_helper_%lld", m->objc_next_block_id); + gbString copy_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$objc_block_copy_helper_%lld", block_id); + gbString dispose_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$objc_block_dispose_helper_%lld", block_id); // copy: Block_Literal *dst, Block_Literal *src, i32 field_apropos // dispose: Block_Literal *src, i32 field_apropos @@ -2385,7 +2385,8 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) { lbModule *m = p->module; - m->objc_next_block_id += 1; + lbModule *default_module = &m->gen->default_module; + const isize block_id = default_module->objc_next_block_id++; const isize capture_arg_count = ce->args.count - 1; @@ -2431,7 +2432,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) { // Create proc with the block signature // (takes a block literal pointer as the first parameter, followed by any expected ones from the user's proc) - gbString block_invoker_name = gb_string_append_fmt(gb_string_make(permanent_allocator(), ""), "__$objc_block_invoker_%lld", m->objc_next_block_id); + gbString block_invoker_name = gb_string_append_fmt(gb_string_make(permanent_allocator(), ""), "__$objc_block_invoker_%lld", block_id); // Add + 1 because the first parameter received is the block literal pointer itself auto invoker_args = array_make(temporary_allocator(), block_forward_args + 1, block_forward_args + 1); @@ -2464,10 +2465,10 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) { // Create the block descriptor and block literal gbString block_lit_type_name = gb_string_make(temporary_allocator(), "__$ObjC_Block_Literal_"); - block_lit_type_name = gb_string_append_fmt(block_lit_type_name, "%lld", m->objc_next_block_id); + block_lit_type_name = gb_string_append_fmt(block_lit_type_name, "%lld", block_id); gbString block_desc_type_name = gb_string_make(temporary_allocator(), "__$ObjC_Block_Descriptor_"); - block_desc_type_name = gb_string_append_fmt(block_desc_type_name, "%lld", m->objc_next_block_id); + block_desc_type_name = gb_string_append_fmt(block_desc_type_name, "%lld", block_id); LLVMTypeRef block_lit_type = {}; LLVMTypeRef block_desc_type = {}; @@ -2511,7 +2512,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) { // Generate copy and dispose helper functions for captured params that are Objective-C objects (or a Block) if (has_objc_fields) { - lb_create_objc_block_helper_procs(m, block_lit_type, capture_fields_offset, + lb_create_objc_block_helper_procs(m, block_lit_type, capture_fields_offset, block_id, slice(captured_values, 0, captured_values.count), slice(objc_captures, 0, objc_captures.count), copy_helper, dispose_helper); @@ -2530,7 +2531,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) { // Create global block descriptor gbString desc_global_name = gb_string_make(temporary_allocator(), "__$objc_block_desc_"); - desc_global_name = gb_string_append_fmt(desc_global_name, "%lld", m->objc_next_block_id); + desc_global_name = gb_string_append_fmt(desc_global_name, "%lld", block_id); LLVMValueRef p_descriptor = LLVMAddGlobal(m->mod, block_desc_type, desc_global_name); LLVMSetInitializer(p_descriptor, block_desc_initializer); @@ -2614,7 +2615,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) { } gbString block_var_name = gb_string_make(temporary_allocator(), "__$objc_block_literal_"); - block_var_name = gb_string_append_fmt(block_var_name, "%lld", m->objc_next_block_id); + block_var_name = gb_string_append_fmt(block_var_name, "%lld", block_id); lbValue block_result = {}; block_result.type = block_result_type; -- cgit v1.2.3 From e4db455458cc4b376f9983fc9968f6e82fc5ba17 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 11 Oct 2025 20:32:09 +0200 Subject: Fix #5785 --- src/checker.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 2a82203ef..8b3638c9d 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -5093,26 +5093,22 @@ gb_internal void add_import_dependency_node(Checker *c, Ast *decl, PtrMapscope != nullptr); - - id->package = pkg; + AstPackage *child_pkg = *found; + GB_ASSERT(child_pkg->scope != nullptr); - ImportGraphNode **found_node = nullptr; - ImportGraphNode *m = nullptr; - ImportGraphNode *n = nullptr; + id->package = child_pkg; - found_node = map_get(M, pkg); + ImportGraphNode **found_node = map_get(M, child_pkg); GB_ASSERT(found_node != nullptr); - m = *found_node; + ImportGraphNode *child = *found_node; found_node = map_get(M, parent_pkg); GB_ASSERT(found_node != nullptr); - n = *found_node; + ImportGraphNode *parent = *found_node; - import_graph_node_set_add(&n->succ, m); - import_graph_node_set_add(&m->pred, n); - ptr_set_add(&m->scope->imported, n->scope); + import_graph_node_set_add(&parent->succ, child); + import_graph_node_set_add(&child->pred, parent); + ptr_set_add(&parent->scope->imported, child->scope); case_end; case_ast_node(ws, WhenStmt, decl); -- cgit v1.2.3 From 816c47058da777bafb4bba17d0648a6e221b4a94 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 30 Oct 2025 16:14:32 +0000 Subject: For test runner, try to look for `os.exit` or `os2.exit` --- src/checker.cpp | 16 ++++++++++++++++ src/llvm_backend.cpp | 19 ++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'src/checker.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 8b3638c9d..1daacd9ce 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -923,6 +923,22 @@ gb_internal AstPackage *get_core_package(CheckerInfo *info, String name) { return *found; } + +gb_internal AstPackage *try_get_core_package(CheckerInfo *info, String name) { + if (name == "runtime") { + return get_runtime_package(info); + } + + gbAllocator a = heap_allocator(); + String path = get_fullpath_core_collection(a, name, nullptr); + defer (gb_free(a, path.text)); + auto found = string_map_get(&info->packages, path); + if (found == nullptr) { + return nullptr; + } + return *found; +} + gb_internal void add_package_dependency(CheckerContext *c, char const *package_name, char const *name, bool required=false) { String n = make_string_c(name); AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name)); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 22a481187..3c9d92095 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2900,7 +2900,24 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star args[0] = lb_addr_load(p, all_tests_slice); lbValue result = lb_emit_call(p, runner, args); - lbValue exit_runner = lb_find_package_value(m, str_lit("os"), str_lit("exit")); + lbValue exit_runner = {}; + { + AstPackage *pkg = try_get_core_package(m->info, str_lit("os")); + if (pkg == nullptr) { + pkg = try_get_core_package(m->info, str_lit("os2")); + } + if (pkg == nullptr) { + pkg = get_core_package(m->info, str_lit("os2")); + } + + String name = str_lit("exit"); + Entity *e = scope_lookup_current(pkg->scope, name); + if (e == nullptr) { + compiler_error("Could not find type declaration for '%.*s.%.*s'\n", LIT(pkg->name), LIT(name)); + } + exit_runner = lb_find_value_from_entity(m, e); + } + auto exit_args = array_make(temporary_allocator(), 1); exit_args[0] = lb_emit_select(p, result, lb_const_int(m, t_int, 0), lb_const_int(m, t_int, 1)); lb_emit_call(p, exit_runner, exit_args, ProcInlining_none); -- cgit v1.2.3 From eb089a1c9884b39d5a2fbfd449e183661fa51e88 Mon Sep 17 00:00:00 2001 From: Laytan Date: Sun, 16 Nov 2025 15:06:30 +0100 Subject: skip collision panic when package names aren't unique --- src/checker.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 1daacd9ce..235d4def9 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -7031,9 +7031,11 @@ gb_internal void check_objc_context_provider_procedures(Checker *c) { } } -gb_internal void check_unique_package_names(Checker *c) { +gb_internal bool check_unique_package_names(Checker *c) { ERROR_BLOCK(); + bool ok = true; + StringMap pkgs = {}; // Key: package name string_map_init(&pkgs, 2*c->info.packages.count); defer (string_map_destroy(&pkgs)); @@ -7058,6 +7060,7 @@ gb_internal void check_unique_package_names(Checker *c) { continue; } + ok = false; begin_error_block(); error(curr, "Duplicate declaration of 'package %.*s'", LIT(name)); @@ -7080,6 +7083,8 @@ gb_internal void check_unique_package_names(Checker *c) { end_error_block(); } + + return ok; } gb_internal void check_add_entities_from_queues(Checker *c) { @@ -7462,7 +7467,7 @@ gb_internal void check_parsed_files(Checker *c) { debugf("Total Procedure Bodies Checked: %td\n", total_bodies_checked.load(std::memory_order_relaxed)); TIME_SECTION("check unique package names"); - check_unique_package_names(c); + bool package_names_are_unique = check_unique_package_names(c); TIME_SECTION("sanity checks"); check_merge_queues_into_arrays(c); @@ -7519,7 +7524,8 @@ gb_internal void check_parsed_files(Checker *c) { c->info.type_info_types_hash_map[index] = tt; bool exists = map_set_if_not_previously_exists(&c->info.min_dep_type_info_index_map, tt.hash, index); - if (exists) { + // Because we've already written a nice error about a duplicate package declaration, skip this panic if the package names aren't unique. + if (package_names_are_unique && exists) { for (auto const &entry : c->info.min_dep_type_info_index_map) { if (entry.key != tt.hash) { continue; -- cgit v1.2.3 From ba9b9eacb500c0431baddf69d459f5314ebc3128 Mon Sep 17 00:00:00 2001 From: Franz Date: Fri, 28 Nov 2025 13:55:07 +0100 Subject: Fix #5967 (Incorrect stack overflow warning for by ref switches over unions) --- src/checker.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/checker.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 235d4def9..4d5482933 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -790,8 +790,10 @@ gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_fl // Is >256 KiB good enough? if (sz > 1ll<<18) { bool is_ref = false; - if((e->flags & EntityFlag_ForValue) != 0) { + if ((e->flags & EntityFlag_ForValue) != 0) { is_ref = type_deref(e->Variable.for_loop_parent_type) != NULL; + } else if ((e->flags & EntityFlag_SwitchValue) != 0) { + is_ref = !(e->flags & EntityFlag_Value); } if(!is_ref) { gbString type_str = type_to_string(e->type); -- cgit v1.2.3 From 8ca7fc481fad1a335b5ca46b9f997396df9be1aa Mon Sep 17 00:00:00 2001 From: Krzesimir Nowak Date: Sun, 21 Dec 2025 18:59:21 +0100 Subject: Fix misleading error messages for init, fini and test attributes These look like copy-pasta from the private attribute. --- src/checker.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 4d5482933..33121b453 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3581,7 +3581,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { return true; } else if (name == "test") { if (value != nullptr) { - error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name)); + error(value, "Expected no value for '%.*s'", LIT(name)); } ac->test = true; return true; @@ -3629,13 +3629,13 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { return true; } else if (name == "init") { if (value != nullptr) { - error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name)); + error(value, "Expected no value for '%.*s'", LIT(name)); } ac->init = true; return true; } else if (name == "fini") { if (value != nullptr) { - error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name)); + error(value, "Expected no value for '%.*s'", LIT(name)); } ac->fini = true; return true; -- cgit v1.2.3