From fcee19434eb24cee9fc027401b1dd345b630765a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 Mar 2025 09:49:30 +0000 Subject: Fix object name generation to previous behaviour --- src/llvm_backend_general.cpp | 73 ++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 23 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 0705e2e93..71d368ec9 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -18,23 +18,36 @@ gb_global isize lb_global_type_info_member_tags_index = 0; gb_internal void lb_init_module(lbModule *m, Checker *c) { m->info = &c->info; - gbString module_name = gb_string_make(heap_allocator(), "odin_package"); - if (m->file) { - if (m->pkg) { + + String name = build_context.build_paths[BuildPath_Output].name; + gbString module_name = gb_string_make(heap_allocator(), ""); + module_name = gb_string_append_length(module_name, name.text, name.len); + + if (!USE_SEPARATE_MODULES) { + // ignore suffixes + } else if (m->file) { + if (gb_string_length(module_name)) { module_name = gb_string_appendc(module_name, "-"); + } + if (m->pkg) { module_name = gb_string_append_length(module_name, m->pkg->name.text, m->pkg->name.len); + module_name = gb_string_appendc(module_name, "-"); } - module_name = gb_string_appendc(module_name, "-"); String filename = filename_from_path(m->file->filename); module_name = gb_string_append_length(module_name, filename.text, filename.len); } else if (m->pkg) { - module_name = gb_string_appendc(module_name, "-"); + if (gb_string_length(module_name)) { + module_name = gb_string_appendc(module_name, "-"); + } module_name = gb_string_append_length(module_name, m->pkg->name.text, m->pkg->name.len); - } else if (USE_SEPARATE_MODULES) { - module_name = gb_string_appendc(module_name, "-builtin"); + } else { + if (gb_string_length(module_name)) { + module_name = gb_string_appendc(module_name, "-"); + } + module_name = gb_string_appendc(module_name, "builtin"); } - m->module_name = module_name ? module_name : "odin_package"; + m->module_name = module_name; m->ctx = LLVMContextCreate(); m->mod = LLVMModuleCreateWithNameInContext(m->module_name, m->ctx); // m->debug_builder = nullptr; @@ -1982,26 +1995,26 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { case Type_Struct: { type_set_offsets(type); - + i64 full_type_size = type_size_of(type); i64 full_type_align = type_align_of(type); GB_ASSERT(full_type_size % full_type_align == 0); - + if (type->Struct.is_raw_union) { - + lbStructFieldRemapping field_remapping = {}; slice_init(&field_remapping, permanent_allocator(), 1); - + LLVMTypeRef fields[1] = {}; fields[0] = lb_type_padding_filler(m, full_type_size, full_type_align); field_remapping[0] = 0; - + LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false); map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping); map_set(&m->struct_field_remapping, cast(void *)type, field_remapping); return struct_type; } - + lbStructFieldRemapping field_remapping = {}; slice_init(&field_remapping, permanent_allocator(), type->Struct.fields.count); @@ -2014,7 +2027,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { LLVMTypeRef padding_type = lb_type_padding_filler(m, 0, type_align_of(type)); array_add(&fields, padding_type); } - + i64 prev_offset = 0; bool requires_packing = type->Struct.is_packed; for (i32 field_index : struct_fields_index_by_increasing_offset(temporary_allocator(), type)) { @@ -2045,7 +2058,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { prev_offset = offset + type_size_of(field->type); } - + i64 end_padding = full_type_size-prev_offset; if (end_padding > 0) { array_add(&fields, lb_type_padding_filler(m, end_padding, 1)); @@ -2054,14 +2067,14 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { for_array(i, fields) { GB_ASSERT(fields[i] != nullptr); } - + LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, requires_packing); map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping); - map_set(&m->struct_field_remapping, cast(void *)type, field_remapping); + map_set(&m->struct_field_remapping, cast(void *)type, field_remapping); #if 0 - GB_ASSERT_MSG(lb_sizeof(struct_type) == full_type_size, - "(%lld) %s vs (%lld) %s", - cast(long long)lb_sizeof(struct_type), LLVMPrintTypeToString(struct_type), + GB_ASSERT_MSG(lb_sizeof(struct_type) == full_type_size, + "(%lld) %s vs (%lld) %s", + cast(long long)lb_sizeof(struct_type), LLVMPrintTypeToString(struct_type), cast(long long)full_type_size, type_to_string(type)); #endif return struct_type; @@ -2090,8 +2103,22 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { LLVMTypeRef variant = lb_type(m, type->Union.variants[0]); array_add(&fields, variant); } else { - LLVMTypeRef block_type = lb_type_padding_filler(m, block_size, align); - LLVMTypeRef tag_type = lb_type(m, union_tag_type(type)); + LLVMTypeRef block_type = nullptr; + + bool all_pointers = align == build_context.ptr_size; + for (isize i = 0; all_pointers && i < type->Union.variants.count; i++) { + Type *t = type->Union.variants[i]; + if (!is_type_internally_pointer_like(t)) { + all_pointers = false; + } + } + if (all_pointers) { + block_type = lb_type(m, t_rawptr); + } else { + block_type = lb_type_padding_filler(m, block_size, align); + } + + LLVMTypeRef tag_type = lb_type(m, union_tag_type(type)); array_add(&fields, block_type); array_add(&fields, tag_type); i64 used_size = lb_sizeof(block_type) + lb_sizeof(tag_type); -- cgit v1.2.3 From f85db012b80f0ddbd33833c0aa617c8d5a6892cb Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Fri, 21 Mar 2025 22:53:00 +0100 Subject: fix off by one temp cstring and put objc names on permanent allocator to be safe Fixes #4922 --- src/llvm_backend_general.cpp | 6 +----- src/llvm_backend_utility.cpp | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 71d368ec9..b7f70893f 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2812,15 +2812,11 @@ gb_internal lbAddr lb_add_global_generated_with_name(lbModule *m, Type *type, lb GB_ASSERT(type != nullptr); type = default_type(type); - u8 *str = cast(u8 *)gb_alloc_array(temporary_allocator(), u8, name.len); - memcpy(str, name.text, name.len); - str[name.len] = 0; - Scope *scope = nullptr; Entity *e = alloc_entity_variable(scope, make_token_ident(name), type); lbValue g = {}; g.type = alloc_type_pointer(type); - g.value = LLVMAddGlobal(m->mod, lb_type(m, type), cast(char const *)str); + g.value = LLVMAddGlobal(m->mod, lb_type(m, type), alloc_cstring(temporary_allocator(), name)); if (value.value != nullptr) { GB_ASSERT_MSG(LLVMIsConstant(value.value), LLVMPrintValueToString(value.value)); LLVMSetInitializer(g.value, value.value); diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index c21e88792..e06369be3 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2112,7 +2112,7 @@ gb_internal lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, Stri } if (!entity) { - gbString global_name = gb_string_make(temporary_allocator(), "__$objc_SEL::"); + gbString global_name = gb_string_make(permanent_allocator(), "__$objc_SEL::"); global_name = gb_string_append_length(global_name, name.text, name.len); lbAddr default_addr = lb_add_global_generated_with_name(default_module, t_objc_SEL, {}, @@ -2174,7 +2174,7 @@ gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String } if (!entity) { - gbString global_name = gb_string_make(temporary_allocator(), "__$objc_Class::"); + gbString global_name = gb_string_make(permanent_allocator(), "__$objc_Class::"); global_name = gb_string_append_length(global_name, name.text, name.len); lbAddr default_addr = lb_add_global_generated_with_name(default_module, t_objc_Class, {}, -- cgit v1.2.3 From cd5bef4f610ec3ee32957cbca354ccbfef310921 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 3 Apr 2025 09:33:14 +0100 Subject: Rewrite objc SEL/Class register handling code --- src/llvm_backend.cpp | 45 ++++++++++++----- src/llvm_backend.hpp | 21 ++++---- src/llvm_backend_general.cpp | 2 + src/llvm_backend_utility.cpp | 118 ++++++++++++++++++------------------------- 4 files changed, 96 insertions(+), 90 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 6f3abc607..396b94f98 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1126,30 +1126,51 @@ gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) { return p; } -gb_internal void lb_finalize_objc_names(lbProcedure *p) { +gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { if (p == nullptr) { return; } lbModule *m = p->module; + GB_ASSERT(m == &p->module->gen->default_module); TEMPORARY_ALLOCATOR_GUARD(); + StringSet handled = {}; + string_set_init(&handled); + defer (string_set_destroy(&handled)); + auto args = array_make(temporary_allocator(), 1); LLVMSetLinkage(p->value, LLVMInternalLinkage); lb_begin_procedure_body(p); - for (auto const &entry : m->objc_classes) { - String name = entry.key; - args[0] = lb_const_value(m, t_cstring, exact_value_string(name)); - lbValue ptr = lb_emit_runtime_call(p, "objc_lookUpClass", args); - lb_addr_store(p, entry.value.local_module_addr, ptr); + + auto register_thing = [&handled, &m, &args](lbProcedure *p, lbObjCGlobal const &g, char const *call) { + if (!string_set_update(&handled, g.name)) { + lbAddr addr = {}; + lbValue *found = string_map_get(&m->members, g.global_name); + if (found) { + addr = lb_addr(*found); + } else { + lbValue v = {}; + LLVMTypeRef t = lb_type(m, g.type); + v.value = LLVMAddGlobal(m->mod, t, g.global_name); + v.type = alloc_type_pointer(g.type); + addr = lb_addr(v); + LLVMSetInitializer(v.value, LLVMConstNull(t)); + } + + args[0] = lb_const_value(m, t_cstring, exact_value_string(g.name)); + lbValue ptr = lb_emit_runtime_call(p, call, args); + lb_addr_store(p, addr, ptr); + } + }; + + for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) { + register_thing(p, g, "objc_lookUpClass"); } - for (auto const &entry : m->objc_selectors) { - String name = entry.key; - args[0] = lb_const_value(m, t_cstring, exact_value_string(name)); - lbValue ptr = lb_emit_runtime_call(p, "sel_registerName", args); - lb_addr_store(p, entry.value.local_module_addr, ptr); + for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_selectors, &g); /**/) { + register_thing(p, g, "sel_registerName"); } lb_end_procedure_body(p); @@ -2637,7 +2658,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { if (gen->objc_names) { TIME_SECTION("Finalize objc names"); - lb_finalize_objc_names(gen->objc_names); + lb_finalize_objc_names(gen, gen->objc_names); } if (build_context.ODIN_DEBUG) { diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index f9f96a906..3e01ada5f 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -143,11 +143,6 @@ struct lbPadType { LLVMTypeRef type; }; -struct lbObjcRef { - Entity * entity; - lbAddr local_module_addr; -}; - struct lbModule { LLVMModuleRef mod; LLVMContextRef ctx; @@ -199,11 +194,8 @@ struct lbModule { PtrMap debug_values; - RecursiveMutex objc_classes_mutex; - RecursiveMutex objc_selectors_mutex; - - StringMap objc_classes; - StringMap objc_selectors; + StringMap objc_classes; + StringMap objc_selectors; PtrMap map_cell_info_map; // address of runtime.Map_Info PtrMap map_info_map; // address of runtime.Map_Cell_Info @@ -222,6 +214,13 @@ struct lbEntityCorrection { char const *cname; }; +struct lbObjCGlobal { + lbModule *module; + gbString global_name; + String name; + Type * type; +}; + struct lbGenerator : LinkerData { CheckerInfo *info; @@ -239,6 +238,8 @@ struct lbGenerator : LinkerData { lbProcedure *objc_names; MPSCQueue entities_to_correct_linkage; + MPSCQueue objc_selectors; + MPSCQueue objc_classes; }; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index b7f70893f..ce2c70661 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -171,6 +171,8 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { } mpsc_init(&gen->entities_to_correct_linkage, heap_allocator()); + mpsc_init(&gen->objc_selectors, heap_allocator()); + mpsc_init(&gen->objc_classes, heap_allocator()); return true; } diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index efe196e58..0decbcdb8 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2094,48 +2094,71 @@ gb_internal void lb_set_wasm_export_attributes(LLVMValueRef value, String export } - gb_internal lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, String const &name) { - mutex_lock(&p->module->objc_selectors_mutex); - defer (mutex_unlock(&p->module->objc_selectors_mutex)); - - lbObjcRef *found = string_map_get(&p->module->objc_selectors, name); - + lbModule *m = p->module; + lbAddr *found = string_map_get(&m->objc_selectors, name); if (found) { - return found->local_module_addr; + return *found; } lbModule *default_module = &p->module->gen->default_module; - Entity *entity = {}; - if (default_module != p->module) { - found = string_map_get(&default_module->objc_selectors, name); - if (found) { - entity = found->entity; - } + + gbString global_name = gb_string_make(permanent_allocator(), "__$objc_SEL::"); + global_name = gb_string_append_length(global_name, name.text, name.len); + + LLVMTypeRef t = lb_type(m, t_objc_SEL); + lbValue g = {}; + g.value = LLVMAddGlobal(m->mod, t, global_name); + g.type = alloc_type_pointer(t_objc_SEL); + + if (default_module == m) { + LLVMSetInitializer(g.value, LLVMConstNull(t)); + lb_add_member(m, make_string_c(global_name), g); + } else { + LLVMSetLinkage(g.value, LLVMExternalLinkage); } - if (!entity) { - gbString global_name = gb_string_make(permanent_allocator(), "__$objc_SEL::"); - global_name = gb_string_append_length(global_name, name.text, name.len); + mpsc_enqueue(&m->gen->objc_selectors, lbObjCGlobal{m, global_name, name, t_objc_SEL}); + + lbAddr addr = lb_addr(g); - lbAddr default_addr = lb_add_global_generated_with_name(default_module, t_objc_SEL, {}, - make_string(cast(u8 const *)global_name, gb_string_length(global_name)), - &entity); + string_map_set(&m->objc_selectors, name, addr); - mutex_lock(&default_module->objc_selectors_mutex); - string_map_set(&default_module->objc_selectors, name, lbObjcRef{entity, default_addr}); - mutex_unlock(&default_module->objc_selectors_mutex); + return addr; +} + +gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name) { + lbModule *m = p->module; + lbAddr *found = string_map_get(&m->objc_classes, name); + if (found) { + return *found; } - lbValue ptr = lb_find_value_from_entity(p->module, entity); - lbAddr local_addr = lb_addr(ptr); + lbModule *default_module = &p->module->gen->default_module; + + + gbString global_name = gb_string_make(permanent_allocator(), "__$objc_Class::"); + global_name = gb_string_append_length(global_name, name.text, name.len); - if (default_module != p->module) { - string_map_set(&p->module->objc_selectors, name, lbObjcRef{entity, local_addr}); + LLVMTypeRef t = lb_type(m, t_objc_Class); + lbValue g = {}; + g.value = LLVMAddGlobal(m->mod, t, global_name); + g.type = alloc_type_pointer(t_objc_Class); + + if (default_module == m) { + LLVMSetInitializer(g.value, LLVMConstNull(t)); + lb_add_member(m, make_string_c(global_name), g); + } else { + LLVMSetLinkage(g.value, LLVMExternalLinkage); } + mpsc_enqueue(&m->gen->objc_classes, lbObjCGlobal{m, global_name, name, t_objc_Class}); - return local_addr; + lbAddr addr = lb_addr(g); + + string_map_set(&m->objc_classes, name, addr); + + return addr; } gb_internal lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) { @@ -2164,47 +2187,6 @@ gb_internal lbValue lb_handle_objc_register_selector(lbProcedure *p, Ast *expr) return lb_addr_load(p, dst); } -gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name) { - mutex_lock(&p->module->objc_classes_mutex); - defer (mutex_unlock(&p->module->objc_classes_mutex)); - - lbObjcRef *found = string_map_get(&p->module->objc_classes, name); - if (found) { - return found->local_module_addr; - } - - lbModule *default_module = &p->module->gen->default_module; - Entity *entity = {}; - - if (default_module != p->module) { - found = string_map_get(&default_module->objc_classes, name); - if (found) { - entity = found->entity; - } - } - - if (!entity) { - gbString global_name = gb_string_make(permanent_allocator(), "__$objc_Class::"); - global_name = gb_string_append_length(global_name, name.text, name.len); - - lbAddr default_addr = lb_add_global_generated_with_name(default_module, t_objc_Class, {}, - make_string(cast(u8 const *)global_name, gb_string_length(global_name)), - &entity); - - mutex_lock(&default_module->objc_classes_mutex); - string_map_set(&default_module->objc_classes, name, lbObjcRef{entity, default_addr}); - mutex_unlock(&default_module->objc_classes_mutex); - } - - lbValue ptr = lb_find_value_from_entity(p->module, entity); - lbAddr local_addr = lb_addr(ptr); - - if (default_module != p->module) { - string_map_set(&p->module->objc_classes, name, lbObjcRef{entity, local_addr}); - } - - return local_addr; -} gb_internal lbValue lb_handle_objc_find_class(lbProcedure *p, Ast *expr) { ast_node(ce, CallExpr, expr); -- cgit v1.2.3 From fe040d1bbd22c78081ffc1d45b3462f40f8eb17a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 8 Apr 2025 11:36:53 +0100 Subject: Propagate `@(link_section=)` to nested declarations --- src/llvm_backend.cpp | 19 ++++++--- src/llvm_backend.hpp | 14 ++++++- src/llvm_backend_const.cpp | 92 +++++++++++++++++++++++++------------------- src/llvm_backend_expr.cpp | 7 ++-- src/llvm_backend_general.cpp | 23 +++++++---- src/llvm_backend_opt.cpp | 4 +- src/llvm_backend_stmt.cpp | 3 +- 7 files changed, 99 insertions(+), 63 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 396b94f98..ee0ea7567 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2056,8 +2056,8 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star if (testing_proc->pkg != nullptr) { pkg_name = testing_proc->pkg->name; } - lbValue v_pkg = lb_find_or_add_entity_string(m, pkg_name); - lbValue v_name = lb_find_or_add_entity_string(m, name); + lbValue v_pkg = lb_find_or_add_entity_string(m, pkg_name, false); + lbValue v_name = lb_find_or_add_entity_string(m, name, false); lbValue v_proc = lb_find_procedure_value_from_entity(m, testing_proc); indices[1] = LLVMConstInt(lb_type(m, t_int), testing_proc_index++, false); @@ -2565,12 +2565,16 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { if (!is_type_any(e->type) && !is_type_union(e->type)) { if (tav.mode != Addressing_Invalid) { if (tav.value.kind != ExactValue_Invalid) { - bool is_rodata = e->kind == Entity_Variable && e->Variable.is_rodata; + auto cc = LB_CONST_CONTEXT_DEFAULT; + cc.is_rodata = e->kind == Entity_Variable && e->Variable.is_rodata; + cc.allow_local = false; + cc.link_section = e->Variable.link_section; + ExactValue v = tav.value; - lbValue init = lb_const_value(m, tav.type, v, false, is_rodata); + lbValue init = lb_const_value(m, tav.type, v, cc); LLVMSetInitializer(g.value, init.value); var.is_initialized = true; - if (is_rodata) { + if (cc.is_rodata) { LLVMSetGlobalConstant(g.value, true); } } @@ -2585,6 +2589,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { } else if (e->kind == Entity_Variable && e->Variable.is_rodata) { LLVMSetGlobalConstant(g.value, true); } + + if (e->flags & EntityFlag_Require) { + lb_append_to_compiler_used(m, g.value); + } + array_add(&global_variables, var); lb_add_entity(m, e, g); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 3e01ada5f..6177fcf6e 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -415,9 +415,19 @@ gb_internal LLVMTypeRef llvm_get_element_type(LLVMTypeRef type); gb_internal lbBlock *lb_create_block(lbProcedure *p, char const *name, bool append=false); +struct lbConstContext { + bool allow_local; + bool is_rodata; + String link_section; +}; + +static lbConstContext const LB_CONST_CONTEXT_DEFAULT = {true, false, {}}; +static lbConstContext const LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL = {true, false, {}}; +static lbConstContext const LB_CONST_CONTEXT_DEFAULT_NO_LOCAL = {false, false, {}}; + gb_internal lbValue lb_const_nil(lbModule *m, Type *type); gb_internal lbValue lb_const_undef(lbModule *m, Type *type); -gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local=true, bool is_rodata=false); +gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc = LB_CONST_CONTEXT_DEFAULT); gb_internal lbValue lb_const_bool(lbModule *m, Type *type, bool value); gb_internal lbValue lb_const_int(lbModule *m, Type *type, u64 value); @@ -514,7 +524,7 @@ gb_internal void lb_fill_slice(lbProcedure *p, lbAddr const &slice, lbValue base gb_internal lbValue lb_type_info(lbProcedure *p, Type *type); -gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str); +gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str, bool custom_link_section); gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent = nullptr); gb_internal bool lb_is_const(lbValue value); diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 9401e4d55..dada2cff5 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -301,10 +301,10 @@ gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String cons } LLVMValueRef fields[4] = {}; - fields[0]/*file*/ = lb_find_or_add_entity_string(m, file).value; + fields[0]/*file*/ = lb_find_or_add_entity_string(m, file, false).value; fields[1]/*line*/ = lb_const_int(m, t_i32, line).value; fields[2]/*column*/ = lb_const_int(m, t_i32, column).value; - fields[3]/*procedure*/ = lb_find_or_add_entity_string(m, procedure).value; + fields[3]/*procedure*/ = lb_find_or_add_entity_string(m, procedure, false).value; lbValue res = {}; res.value = llvm_const_named_struct(m, t_source_code_location, fields, gb_count_of(fields)); @@ -391,12 +391,12 @@ gb_internal lbValue lb_emit_source_code_location_as_global(lbProcedure *p, Ast * -gb_internal LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, bool allow_local, bool is_rodata) { - if (allow_local) { - is_rodata = false; +gb_internal LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, lbConstContext cc) { + if (cc.allow_local) { + cc.is_rodata = false; } - bool is_local = allow_local && m->curr_procedure != nullptr; + bool is_local = cc.allow_local && m->curr_procedure != nullptr; bool is_const = true; if (is_local) { for (isize i = 0; i < count; i++) { @@ -500,9 +500,9 @@ gb_internal bool lb_is_nested_possibly_constant(Type *ft, Selection const &sel, return lb_is_elem_const(elem, ft); } -gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local, bool is_rodata) { - if (allow_local) { - is_rodata = false; +gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc) { + if (cc.allow_local) { + cc.is_rodata = false; } LLVMContextRef ctx = m->ctx; @@ -543,7 +543,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo return res; } - bool is_local = allow_local && m->curr_procedure != nullptr; + bool is_local = cc.allow_local && m->curr_procedure != nullptr; // GB_ASSERT_MSG(is_type_typed(type), "%s", type_to_string(type)); @@ -562,7 +562,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo count = gb_max(cast(isize)cl->max_count, count); Type *elem = base_type(type)->Slice.elem; Type *t = alloc_type_array(elem, count); - lbValue backing_array = lb_const_value(m, t, value, allow_local, is_rodata); + lbValue backing_array = lb_const_value(m, t, value, cc); LLVMValueRef array_data = nullptr; @@ -599,7 +599,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo array_data = LLVMAddGlobal(m->mod, lb_type(m, t), str); LLVMSetInitializer(array_data, backing_array.value); - if (is_rodata) { + if (cc.link_section.len > 0) { + LLVMSetSection(array_data, alloc_cstring(permanent_allocator(), cc.link_section)); + } + if (cc.is_rodata) { LLVMSetGlobalConstant(array_data, true); } @@ -650,7 +653,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo } // NOTE(bill, 2021-10-07): Allow for array programming value constants Type *core_elem = core_array_type(type); - return lb_const_value(m, core_elem, value, allow_local, is_rodata); + return lb_const_value(m, core_elem, value, cc); } else if (is_type_u8_array(type) && value.kind == ExactValue_String) { GB_ASSERT(type->Array.count == value.value_string.len); LLVMValueRef data = LLVMConstStringInContext(ctx, @@ -668,7 +671,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo Type *elem = type->Array.elem; - lbValue single_elem = lb_const_value(m, elem, value, allow_local, is_rodata); + lbValue single_elem = lb_const_value(m, elem, value, cc); LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, cast(isize)count); for (i64 i = 0; i < count; i++) { @@ -686,7 +689,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo Type *elem = type->Matrix.elem; - lbValue single_elem = lb_const_value(m, elem, value, allow_local, is_rodata); + lbValue single_elem = lb_const_value(m, elem, value, cc); single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem)); i64 total_elem_count = matrix_type_total_internal_elems(type); @@ -708,7 +711,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo i64 count = type->SimdVector.count; Type *elem = type->SimdVector.elem; - lbValue single_elem = lb_const_value(m, elem, value, allow_local, is_rodata); + lbValue single_elem = lb_const_value(m, elem, value, cc); single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem)); LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count); @@ -729,9 +732,16 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo return res; case ExactValue_String: { - LLVMValueRef ptr = lb_find_or_add_entity_string_ptr(m, value.value_string); + bool custom_link_section = cc.link_section.len > 0; + + LLVMValueRef ptr = lb_find_or_add_entity_string_ptr(m, value.value_string, custom_link_section); lbValue res = {}; res.type = default_type(original_type); + + if (custom_link_section) { + LLVMSetSection(ptr, alloc_cstring(permanent_allocator(), cc.link_section)); + } + if (is_type_cstring(res.type)) { res.value = ptr; } else { @@ -837,7 +847,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo case ExactValue_Compound: if (is_type_slice(type)) { - return lb_const_value(m, type, value, allow_local, is_rodata); + return lb_const_value(m, type, value, cc); } else if (is_type_array(type)) { ast_node(cl, CompoundLit, value.value_compound); Type *elem_type = type->Array.elem; @@ -871,7 +881,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo } if (lo == i) { TypeAndValue tav = fv->value->tav; - LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; for (i64 k = lo; k < hi; k++) { values[value_index++] = val; } @@ -886,7 +896,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo i64 index = exact_value_to_i64(index_tav.value); if (index == i) { TypeAndValue tav = fv->value->tav; - LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; values[value_index++] = val; found = true; break; @@ -899,7 +909,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo } } - res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, allow_local, is_rodata); + res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc); return res; } else { GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count); @@ -909,13 +919,13 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo for (isize i = 0; i < elem_count; i++) { TypeAndValue tav = cl->elems[i]->tav; GB_ASSERT(tav.mode != Addressing_Invalid); - values[i] = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value; + values[i] = lb_const_value(m, elem_type, tav.value, cc).value; } for (isize i = elem_count; i < type->Array.count; i++) { values[i] = LLVMConstNull(lb_type(m, elem_type)); } - res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, allow_local, is_rodata); + res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc); return res; } } else if (is_type_enumerated_array(type)) { @@ -955,7 +965,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo } if (lo == i) { TypeAndValue tav = fv->value->tav; - LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; for (i64 k = lo; k < hi; k++) { values[value_index++] = val; } @@ -970,7 +980,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo i64 index = exact_value_to_i64(index_tav.value); if (index == i) { TypeAndValue tav = fv->value->tav; - LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; values[value_index++] = val; found = true; break; @@ -983,7 +993,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo } } - res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, allow_local, is_rodata); + res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, cc); return res; } else { GB_ASSERT_MSG(elem_count == type->EnumeratedArray.count, "%td != %td", elem_count, type->EnumeratedArray.count); @@ -993,13 +1003,13 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo for (isize i = 0; i < elem_count; i++) { TypeAndValue tav = cl->elems[i]->tav; GB_ASSERT(tav.mode != Addressing_Invalid); - values[i] = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value; + values[i] = lb_const_value(m, elem_type, tav.value, cc).value; } for (isize i = elem_count; i < type->EnumeratedArray.count; i++) { values[i] = LLVMConstNull(lb_type(m, elem_type)); } - res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, allow_local, is_rodata); + res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, cc); return res; } } else if (is_type_simd_vector(type)) { @@ -1038,7 +1048,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo } if (lo == i) { TypeAndValue tav = fv->value->tav; - LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; for (i64 k = lo; k < hi; k++) { values[value_index++] = val; } @@ -1053,7 +1063,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo i64 index = exact_value_to_i64(index_tav.value); if (index == i) { TypeAndValue tav = fv->value->tav; - LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; values[value_index++] = val; found = true; break; @@ -1072,7 +1082,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo for (isize i = 0; i < elem_count; i++) { TypeAndValue tav = cl->elems[i]->tav; GB_ASSERT(tav.mode != Addressing_Invalid); - values[i] = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value; + values[i] = lb_const_value(m, elem_type, tav.value, cc).value; } LLVMTypeRef et = lb_type(m, elem_type); @@ -1121,11 +1131,13 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo i32 index = field_remapping[f->Variable.field_index]; if (elem_type_can_be_constant(f->type)) { if (sel.index.count == 1) { - values[index] = lb_const_value(m, f->type, tav.value, allow_local, is_rodata).value; + values[index] = lb_const_value(m, f->type, tav.value, cc).value; visited[index] = true; } else { if (!visited[index]) { - values[index] = lb_const_value(m, f->type, {}, /*allow_local*/false, is_rodata).value; + auto new_cc = cc; + new_cc.allow_local = false; + values[index] = lb_const_value(m, f->type, {}, new_cc).value; visited[index] = true; } @@ -1165,7 +1177,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo } } if (is_constant) { - LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local, is_rodata).value; + LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, cc).value; if (LLVMIsConstant(elem_value) && LLVMIsConstant(values[index])) { values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len); } else if (is_local) { @@ -1219,7 +1231,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo i32 index = field_remapping[f->Variable.field_index]; if (elem_type_can_be_constant(f->type)) { - values[index] = lb_const_value(m, f->type, val, allow_local, is_rodata).value; + values[index] = lb_const_value(m, f->type, val, cc).value; visited[index] = true; } } @@ -1353,7 +1365,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo TypeAndValue tav = fv->value->tav; - LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; for (i64 k = lo; k < hi; k++) { i64 offset = matrix_row_major_index_to_offset(type, k); GB_ASSERT(values[offset] == nullptr); @@ -1365,7 +1377,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo i64 index = exact_value_to_i64(index_tav.value); GB_ASSERT(index < max_count); TypeAndValue tav = fv->value->tav; - LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; i64 offset = matrix_row_major_index_to_offset(type, index); GB_ASSERT(values[offset] == nullptr); values[offset] = val; @@ -1378,7 +1390,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo } } - res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, allow_local, is_rodata); + res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, cc); return res; } else { GB_ASSERT_MSG(elem_count == max_count, "%td != %td", elem_count, max_count); @@ -1389,7 +1401,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo GB_ASSERT(tav.mode != Addressing_Invalid); i64 offset = 0; offset = matrix_row_major_index_to_offset(type, i); - values[offset] = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value; + values[offset] = lb_const_value(m, elem_type, tav.value, cc).value; } for (isize i = 0; i < total_count; i++) { if (values[i] == nullptr) { @@ -1397,7 +1409,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo } } - res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, allow_local, is_rodata); + res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, cc); return res; } } else { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 0c82180ec..20b8d3cf8 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2352,7 +2352,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { Type *elem = base_array_type(dst); lbValue e = lb_emit_conv(p, value, elem); lbAddr v = lb_add_local_generated(p, t, false); - lbValue zero = lb_const_value(p->module, elem, exact_value_i64(0), true); + lbValue zero = lb_const_value(p->module, elem, exact_value_i64(0), LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL); for (i64 j = 0; j < dst->Matrix.column_count; j++) { for (i64 i = 0; i < dst->Matrix.row_count; i++) { lbValue ptr = lb_emit_matrix_epi(p, v.addr, i, j); @@ -2389,7 +2389,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { lb_emit_store(p, d, s); } else if (i == j) { lbValue d = lb_emit_matrix_epi(p, v.addr, i, j); - lbValue s = lb_const_value(p->module, dst->Matrix.elem, exact_value_i64(1), true); + lbValue s = lb_const_value(p->module, dst->Matrix.elem, exact_value_i64(1), LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL); lb_emit_store(p, d, s); } } @@ -3493,8 +3493,7 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) { if (tv.value.kind != ExactValue_Invalid) { // NOTE(bill): Short on constant values - bool allow_local = true; - return lb_const_value(p->module, type, tv.value, allow_local); + return lb_const_value(p->module, type, tv.value, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL); } else if (tv.mode == Addressing_Type) { // NOTE(bill, 2023-01-16): is this correct? I hope so at least return lb_typeid(m, tv.type); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index ce2c70661..421720c4c 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -356,7 +356,7 @@ gb_internal LLVMValueRef llvm_const_insert_value(lbModule *m, LLVMValueRef agg, gb_internal LLVMValueRef llvm_cstring(lbModule *m, String const &str) { - lbValue v = lb_find_or_add_entity_string(m, str); + lbValue v = lb_find_or_add_entity_string(m, str, false); unsigned indices[1] = {0}; return llvm_const_extract_value(m, v.value, indices, gb_count_of(indices)); } @@ -568,7 +568,7 @@ gb_internal void lb_set_file_line_col(lbProcedure *p, Array arr, TokenP col = obfuscate_i32(col); } - arr[0] = lb_find_or_add_entity_string(p->module, file); + arr[0] = lb_find_or_add_entity_string(p->module, file, false); arr[1] = lb_const_int(p->module, t_i32, line); arr[2] = lb_const_int(p->module, t_i32, col); } @@ -2543,9 +2543,14 @@ general_end:; -gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) { - StringHashKey key = string_hash_string(str); - LLVMValueRef *found = string_map_get(&m->const_strings, key); +gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str, bool custom_link_section) { + StringHashKey key = {}; + LLVMValueRef *found = nullptr; + + if (!custom_link_section) { + key = string_hash_string(str); + found = string_map_get(&m->const_strings, key); + } if (found != nullptr) { return *found; } else { @@ -2568,15 +2573,17 @@ gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String co LLVMSetAlignment(global_data, 1); LLVMValueRef ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2); - string_map_set(&m->const_strings, key, ptr); + if (!custom_link_section) { + string_map_set(&m->const_strings, key, ptr); + } return ptr; } } -gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str) { +gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str, bool custom_link_section) { LLVMValueRef ptr = nullptr; if (str.len != 0) { - ptr = lb_find_or_add_entity_string_ptr(m, str); + ptr = lb_find_or_add_entity_string_ptr(m, str, custom_link_section); } else { ptr = LLVMConstNull(lb_type(m, t_u8_ptr)); } diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 7fe1359b4..8d5cfcb70 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -516,7 +516,7 @@ gb_internal void llvm_delete_function(LLVMValueRef func) { LLVMDeleteFunction(func); } -gb_internal void lb_append_to_compiler_used(lbModule *m, LLVMValueRef func) { +gb_internal void lb_append_to_compiler_used(lbModule *m, LLVMValueRef value) { LLVMValueRef global = LLVMGetNamedGlobal(m->mod, "llvm.compiler.used"); LLVMValueRef *constants; @@ -544,7 +544,7 @@ gb_internal void lb_append_to_compiler_used(lbModule *m, LLVMValueRef func) { LLVMTypeRef Int8PtrTy = LLVMPointerType(LLVMInt8TypeInContext(m->ctx), 0); LLVMTypeRef ATy = llvm_array_type(Int8PtrTy, operands); - constants[operands - 1] = LLVMConstBitCast(func, Int8PtrTy); + constants[operands - 1] = LLVMConstBitCast(value, Int8PtrTy); LLVMValueRef initializer = LLVMConstArray(Int8PtrTy, constants, operands); global = LLVMAddGlobal(m->mod, ATy, "llvm.compiler.used"); diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 1f783b1be..a0b7e8340 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1963,8 +1963,7 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) { GB_ASSERT(ast_value->tav.mode == Addressing_Constant || ast_value->tav.mode == Addressing_Invalid); - bool allow_local = false; - value = lb_const_value(p->module, ast_value->tav.type, ast_value->tav.value, allow_local); + value = lb_const_value(p->module, ast_value->tav.type, ast_value->tav.value, LB_CONST_CONTEXT_DEFAULT_NO_LOCAL); } Ast *ident = vd->names[i]; -- cgit v1.2.3 From a3de9c8de4e539905a85f3cc060f95529b402f18 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Sat, 19 Apr 2025 08:04:23 -0400 Subject: Add initial support for Objective-C class implementation --- base/intrinsics/intrinsics.odin | 5 +- base/runtime/procs_darwin.odin | 25 +- src/check_builtin.cpp | 77 +++++- src/check_decl.cpp | 73 +++++ src/checker.cpp | 77 +++++- src/checker.hpp | 17 +- src/checker_builtin_procs.hpp | 2 + src/entity.cpp | 3 + src/llvm_backend.cpp | 591 +++++++++++++++++++++++++++++++++++++--- src/llvm_backend.hpp | 3 + src/llvm_backend_general.cpp | 2 + src/llvm_backend_proc.cpp | 1 + src/llvm_backend_utility.cpp | 74 ++++- src/types.cpp | 2 + 14 files changed, 900 insertions(+), 52 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index bec452007..515e8d48a 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -353,15 +353,18 @@ x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) --- objc_object :: struct{} 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_find_selector :: proc($name: string) -> objc_SEL --- objc_register_selector :: proc($name: string) -> objc_SEL --- objc_find_class :: proc($name: string) -> objc_Class --- objc_register_class :: proc($name: string) -> objc_Class --- - +ivar_get :: proc(self: ^$T, $U: typeid) -> ^U --- 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 c3fc46af1..0aec57e80 100644 --- a/base/runtime/procs_darwin.odin +++ b/base/runtime/procs_darwin.odin @@ -2,21 +2,34 @@ package runtime @(priority_index=-1e6) -foreign import "system:Foundation.framework" +foreign import ObjC "system:objc" import "base:intrinsics" -objc_id :: ^intrinsics.objc_object +objc_id :: ^intrinsics.objc_object objc_Class :: ^intrinsics.objc_class -objc_SEL :: ^intrinsics.objc_selector +objc_SEL :: ^intrinsics.objc_selector +objc_Ivar :: ^intrinsics.objc_ivar +objc_BOOL :: bool -foreign Foundation { - objc_lookUpClass :: proc "c" (name: cstring) -> 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_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) -> objc_Class --- 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_lookUpClass :: proc "c" (name: cstring) -> objc_Class --- + objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) -> objc_Class --- + objc_registerClassPair :: proc "c" (cls : objc_Class) --- + class_addMethod :: proc "c" (cls: objc_Class, name: objc_SEL, imp: objc_IMP, types: cstring) -> objc_BOOL --- + 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 --- + ivar_getOffset :: proc "c" (v: objc_Ivar) -> uintptr --- } + diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index f66a8605c..c44d1c123 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -387,6 +387,80 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan try_to_add_package_dependency(c, "runtime", "objc_allocateClassPair"); return true; } break; + + case BuiltinProc_objc_ivar_get: + { + Type *self_type = nullptr; + Type *ivar_type = nullptr; + + Operand self {}; + check_expr_or_type(c, &self, ce->args[0]); + + if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) { + gbString e = expr_to_string(self.expr); + gbString t = type_to_string(self.type); + error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + else if (!is_type_pointer(self.type)) { + gbString e = expr_to_string(self.expr); + gbString t = type_to_string(self.type); + error(self.expr, "'%.*s' expected a pointer of a value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + self_type = type_deref(self.type); + + if (!(self_type->kind == Type_Named && + self_type->Named.type_name != nullptr && + self_type->Named.type_name->TypeName.objc_class_name != "")) { + gbString t = type_to_string(self_type); + error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=) , got type %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + if (self_type->Named.type_name->TypeName.objc_ivar == nullptr) { + gbString t = type_to_string(self_type); + error(self.expr, "'%.*s' requires that type %s have the attribute @(obj_ivar=).", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + Operand ivar {}; + check_expr_or_type(c, &ivar, ce->args[1]); + if (ivar.mode == Addressing_Type) { + ivar_type = ivar.type; + } else { + return false; + } + + if (self_type->Named.type_name->TypeName.objc_ivar != ivar_type) { + gbString name_self = type_to_string(self_type); + gbString name_expected = type_to_string(self_type->Named.type_name->TypeName.objc_ivar); + gbString name_given = type_to_string(ivar_type); + error(self.expr, "'%.*s' ivar type %s does not match @obj_ivar type %s on Objective-C class %s.", + LIT(builtin_name), name_given, name_expected, name_self); + gb_string_free(name_self); + gb_string_free(name_expected); + gb_string_free(name_given); + return false; + } + + if (type_hint != nullptr && type_hint->kind == Type_Pointer && type_hint->Pointer.elem == ivar_type) { + operand->type = type_hint; + } else { + operand->type = alloc_type_pointer(ivar_type); + } + + operand->mode = Addressing_Value; + + return true; + } break; } } @@ -2132,7 +2206,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_objc_find_selector: case BuiltinProc_objc_find_class: case BuiltinProc_objc_register_selector: - case BuiltinProc_objc_register_class: + case BuiltinProc_objc_register_class: + case BuiltinProc_objc_ivar_get: 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 ba6445ea4..dffe0b48e 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -526,6 +526,54 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac); if (e->kind == Entity_TypeName && ac.objc_class != "") { e->TypeName.objc_class_name = ac.objc_class; + e->TypeName.objc_superclass = ac.objc_superclass; + e->TypeName.objc_ivar = ac.objc_ivar; + + if (ac.objc_is_implementation) { + e->TypeName.objc_is_implementation = true; + mpsc_enqueue(&ctx->info->objc_class_implementations, e); // TODO(harold): Don't need this for anything. Remove. + + GB_ASSERT(e->TypeName.objc_ivar == nullptr || e->TypeName.objc_ivar->kind == Type_Named); + + // Ensure superclass hierarchy are all Objective-C classes and does not cycle + Type *super = ac.objc_superclass; + if (super != nullptr) { + TypeSet super_set{}; + type_set_init(&super_set, 8); + defer (type_set_destroy(&super_set)); + + type_set_update(&super_set, e->type); + + for (;;) { + if (type_set_update(&super_set, super)) { + error(e->token, "@(objc_superclass) Superclass hierarchy cycle encountered"); + break; + } + + if (super->kind != Type_Named) { + error(e->token, "@(objc_superclass) References type must be a named struct."); + break; + } + + Type* named_type = base_type(super->Named.type_name->type); + if (!is_type_objc_object(named_type)) { + error(e->token, "@(objc_superclass) Superclass must be an Objective-C class."); + break; + } + + super = super->Named.type_name->TypeName.objc_superclass; + if (super == nullptr) { + break; + } + + // TODO(harold): Is this the right way to do this??? The referenced entity must be already resolved + // so that we can access its objc_superclass attribute + check_single_global_entity(ctx->checker, super->Named.type_name, super->Named.type_name->decl_info); + } + } + } else if (e->TypeName.objc_superclass != nullptr) { + error(e->token, "@(objc_superclass) can only be applied when the obj_implement attribute is also applied"); + } if (type_size_of(e->type) > 0) { error(e->token, "@(objc_class) marked type must be of zero size"); @@ -942,6 +990,31 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon if (tn->scope != e->scope) { error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope"); } else { + + if (ac.objc_is_implementation) { + GB_ASSERT(e->kind == Entity_Procedure); + + CheckerInfo *info = ctx->info; + mutex_lock(&info->objc_method_mutex); + defer (mutex_unlock(&info->objc_method_mutex)); + + auto method = ObjcMethodData{ ac, e }; + + if (ac.objc_selector == "") { + method.ac.objc_selector = ac.objc_name; + } + + 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); + } + } + mutex_lock(&global_type_name_objc_metadata_mutex); defer (mutex_unlock(&global_type_name_objc_metadata_mutex)); diff --git a/src/checker.cpp b/src/checker.cpp index 5a5ec9706..29ef7d2b3 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1351,10 +1351,12 @@ gb_internal void init_universal(void) { t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct_complete()); t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct_complete()); t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct_complete()); + t_objc_ivar = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_ivar"), alloc_type_struct_complete()); t_objc_id = alloc_type_pointer(t_objc_object); 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); } } @@ -1387,6 +1389,9 @@ gb_internal void init_checker_info(CheckerInfo *i) { array_init(&i->defineables, a); map_init(&i->objc_msgSend_types); + mpsc_init(&i->objc_class_implementations, a); + map_init(&i->objc_method_implementations); + string_map_init(&i->load_file_cache); array_init(&i->all_procedures, heap_allocator()); @@ -3345,6 +3350,11 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { ac->test = true; return true; } else if (name == "export") { + if (ac->objc_is_implementation) { + error(value, "Setting @(export) explicitly is not allowed when @(objc_implement) is set. It is exported implicitly."); + return false; + } + ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind == ExactValue_Invalid) { ac->is_export = true; @@ -3356,6 +3366,12 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { } return true; } else if (name == "linkage") { + + if (ac->objc_is_implementation) { + error(value, "Explicit linkage not allowed when @(objc_implement) is set. It is set implicitly"); + return false; + } + ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind != ExactValue_String) { error(value, "Expected either a string 'linkage'"); @@ -3662,6 +3678,35 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { } } return true; + } else if (name == "objc_implement") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_Bool) { + ac->objc_is_implementation = ev.value_bool; + } else if (ev.kind == ExactValue_Invalid) { + ac->objc_is_implementation = true; + } else { + error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name)); + } + + // This implies exported, strongly linked + if (ac->objc_is_implementation) { + ac->is_export = true; + ac->linkage = str_lit("strong"); + } + + return true; + } else if (name == "objc_selector") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_String) { + if (string_is_valid_identifier(ev.value_string)) { + ac->objc_selector = ev.value_string; + } else { + error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string)); + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } else if (name == "require_target_feature") { ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind == ExactValue_String) { @@ -3901,8 +3946,36 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) { ac->objc_class = ev.value_string; } return true; - } - return false; + } else if (name == "objc_implement") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_Bool) { + ac->objc_is_implementation = ev.value_bool; + } else if (ev.kind == ExactValue_Invalid) { + ac->objc_is_implementation = true; + } else { + error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name)); + } + return true; + } else if (name == "objc_superclass") { + Type *objc_superclass = check_type(c, value); + + if (objc_superclass != nullptr) { + ac->objc_superclass = objc_superclass; + } else { + error(value, "'%.*s' expected a named type", LIT(name)); + } + return true; + } else if (name == "objc_ivar") { + Type *objc_ivar = check_type(c, value); + + if (objc_ivar != nullptr) { + ac->objc_ivar = objc_ivar; + } else { + error(value, "'%.*s' expected a named type", LIT(name)); + } + return true; + } + return false; } diff --git a/src/checker.hpp b/src/checker.hpp index d3b2d7d89..9910ed17b 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -148,8 +148,12 @@ struct AttributeContext { String objc_class; String objc_name; - bool objc_is_class_method; + String objc_selector; Type * objc_type; + Type * objc_superclass; + Type * objc_ivar; + bool objc_is_class_method : 1; + bool objc_is_implementation : 1; // This struct or proc provides a class/method implementation, not a binding to an existing type. String require_target_feature; // required by the target micro-architecture String enable_target_feature; // will be enabled for the procedure only @@ -365,6 +369,11 @@ struct ObjcMsgData { Type *proc_type; }; +struct ObjcMethodData { + AttributeContext ac; + Entity *proc_entity; +}; + enum LoadFileTier { LoadFileTier_Invalid, LoadFileTier_Exists, @@ -479,6 +488,12 @@ struct CheckerInfo { BlockingMutex objc_types_mutex; PtrMap objc_msgSend_types; + MPSCQueue objc_class_implementations; + + BlockingMutex objc_method_mutex; + PtrMap> objc_method_implementations; + + BlockingMutex load_file_mutex; StringMap load_file_cache; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 40dde8240..cb2ce3915 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -331,6 +331,7 @@ BuiltinProc__type_end, BuiltinProc_objc_find_class, BuiltinProc_objc_register_selector, BuiltinProc_objc_register_class, + BuiltinProc_objc_ivar_get, BuiltinProc_constant_utf16_cstring, @@ -673,6 +674,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {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("ivar_get"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/entity.cpp b/src/entity.cpp index b2148aa7b..9a5996e3d 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -235,6 +235,9 @@ struct Entity { Type * type_parameter_specialization; String ir_mangled_name; bool is_type_alias; + bool objc_is_implementation; + Type* objc_superclass; + Type* objc_ivar; String objc_class_name; TypeNameObjCMetadata *objc_metadata; } TypeName; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 083a1d90e..23ad81847 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1173,6 +1173,332 @@ gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) { return p; } +// TODO(harold): Move this out of here and into a more suitable place. +// TODO(harold): Should not take an allocator, but always use temp, as we return string literals as well. +String lb_get_objc_type_encoding(Type *t, gbAllocator allocator, isize pointer_depth = 0) { + // NOTE(harold): See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100 + + // NOTE(harold): Darwin targets are always 64-bit. Should we drop this and assume "q" always? + #define INT_SIZE_ENCODING (build_context.metrics.ptr_size == 4 ? "i" : "q") + switch (t->kind) { + case Type_Basic: { + switch (t->Basic.kind) { + case Basic_Invalid: + return str_lit("?"); + + case Basic_llvm_bool: + case Basic_bool: + case Basic_b8: + return str_lit("B"); + + case Basic_b16: + return str_lit("C"); + case Basic_b32: + return str_lit("I"); + case Basic_b64: + return str_lit("q"); + case Basic_i8: + return str_lit("c"); + case Basic_u8: + return str_lit("C"); + case Basic_i16: + case Basic_i16le: + case Basic_i16be: + return str_lit("s"); + case Basic_u16: + case Basic_u16le: + case Basic_u16be: + return str_lit("S"); + case Basic_i32: + case Basic_i32le: + case Basic_i32be: + return str_lit("i"); + case Basic_u32le: + case Basic_u32: + case Basic_u32be: + return str_lit("I"); + case Basic_i64: + case Basic_i64le: + case Basic_i64be: + return str_lit("q"); + case Basic_u64: + case Basic_u64le: + case Basic_u64be: + return str_lit("Q"); + case Basic_i128: + case Basic_i128le: + case Basic_i128be: + return str_lit("t"); + case Basic_u128: + case Basic_u128le: + case Basic_u128be: + return str_lit("T"); + case Basic_rune: + return str_lit("I"); + case Basic_f16: + case Basic_f16le: + case Basic_f16be: + return str_lit("s"); // @harold: Closest we've got? + case Basic_f32: + case Basic_f32le: + case Basic_f32be: + return str_lit("f"); + case Basic_f64: + case Basic_f64le: + case Basic_f64be: + return str_lit("d"); + + // TODO(harold) These: + case Basic_complex32: + case Basic_complex64: + case Basic_complex128: + case Basic_quaternion64: + case Basic_quaternion128: + case Basic_quaternion256: + return str_lit("?"); + + case Basic_int: + return str_lit(INT_SIZE_ENCODING); + case Basic_uint: + return build_context.metrics.ptr_size == 4 ? str_lit("I") : str_lit("Q"); + case Basic_uintptr: + case Basic_rawptr: + return str_lit("^v"); + + case Basic_string: + return build_context.metrics.ptr_size == 4 ? str_lit("{string=*i}") : str_lit("{string=*q}"); + + case Basic_cstring: return str_lit("*"); + case Basic_any: return str_lit("{any=^v^v"); // rawptr + ^Type_Info + + case Basic_typeid: + GB_ASSERT(t->Basic.size == 8); + return str_lit("q"); + + // Untyped types + case Basic_UntypedBool: + case Basic_UntypedInteger: + case Basic_UntypedFloat: + case Basic_UntypedComplex: + case Basic_UntypedQuaternion: + case Basic_UntypedString: + case Basic_UntypedRune: + case Basic_UntypedNil: + case Basic_UntypedUninit: + GB_PANIC("Untyped types cannot be @encoded()"); + return str_lit("?"); + } + break; + } + + case Type_Named: + case Type_Struct: + case Type_Union: { + Type* base = t; + if (base->kind == Type_Named) { + base = base_type(base); + if(base->kind != Type_Struct && base->kind != Type_Union) { + return lb_get_objc_type_encoding(base, allocator, pointer_depth); + } + } + + const bool is_union = base->kind == Type_Union; + if (!is_union) { + // Check for objc_SEL + if (internal_check_is_assignable_to(base, t_objc_SEL)) { + return str_lit(":"); + } + + // Check for objc_Class + if (internal_check_is_assignable_to(base, t_objc_SEL)) { + return str_lit("#"); + } + + // Treat struct as an Objective-C Class? + if (has_type_got_objc_class_attribute(base) && pointer_depth == 0) { + return str_lit("#"); + } + } + + if (is_type_objc_object(base)) { + return str_lit("@"); + } + + + gbString s = gb_string_make_reserve(allocator, 16); + s = gb_string_append_length(s, is_union ? "(" :"{", 1); + if (t->kind == Type_Named) { + s = gb_string_append_length(s, t->Named.name.text, t->Named.name.len); + } + + // Write fields + if (pointer_depth < 2) { + s = gb_string_append_length(s, "=", 1); + + if (!is_union) { + for( auto& f : t->Struct.fields ) { + String field_type = lb_get_objc_type_encoding(f->type, allocator, pointer_depth); + s = gb_string_append_length(s, field_type.text, field_type.len); + } + } else { + // #TODO(harold): Encode fields + } + } + + s = gb_string_append_length(s, is_union ? ")" :"}", 1); + + return make_string_c(s); + } + + case Type_Generic: + GB_PANIC("Generic types cannot be @encoded()"); + return str_lit("?"); + + case Type_Pointer: { + String pointee = lb_get_objc_type_encoding(t->Pointer.elem, allocator, pointer_depth +1); + // Special case for Objective-C Objects + if (pointer_depth == 0 && pointee == "@") { + return pointee; + } + + return concatenate_strings(allocator, str_lit("^"), pointee); + } + + case Type_MultiPointer: + return concatenate_strings(allocator, str_lit("^"), lb_get_objc_type_encoding(t->Pointer.elem, allocator, pointer_depth +1)); + + case Type_Array: { + String type_str = lb_get_objc_type_encoding(t->Array.elem, allocator, pointer_depth); + + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "[%lld%s]", t->Array.count, type_str.text); + return make_string_c(s); + } + + case Type_EnumeratedArray: { + String type_str = lb_get_objc_type_encoding(t->EnumeratedArray.elem, allocator, pointer_depth); + + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "[%lld%s]", t->EnumeratedArray.count, type_str.text); + return make_string_c(s); + } + + case Type_Slice: { + String type_str = lb_get_objc_type_encoding(t->Slice.elem, allocator, pointer_depth); + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "{slice=^%s%s}", type_str, INT_SIZE_ENCODING); + return make_string_c(s); + } + + case Type_DynamicArray: { + String type_str = lb_get_objc_type_encoding(t->DynamicArray.elem, allocator, pointer_depth); + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "{dynamic=^%s%s%sAllocator={?^v}}", type_str, INT_SIZE_ENCODING, INT_SIZE_ENCODING); + return make_string_c(s); + } + + case Type_Map: + return str_lit("{^v^v{Allocator=?^v}}"); + case Type_Enum: + return lb_get_objc_type_encoding(t->Enum.base_type, allocator, pointer_depth); + case Type_Tuple: + // NOTE(harold): Is this allowed here? + return str_lit("?"); + case Type_Proc: + return str_lit("?"); + case Type_BitSet: + return lb_get_objc_type_encoding(t->BitSet.underlying, allocator, pointer_depth); + case Type_SimdVector: + break; + case Type_Matrix: + break; + case Type_BitField: + return lb_get_objc_type_encoding(t->BitField.backing_type, allocator, pointer_depth); + case Type_SoaPointer: { + gbString s = gb_string_make_reserve(allocator, 8); + s = gb_string_append_fmt(s, "{=^v%s}", INT_SIZE_ENCODING); + return make_string_c(s); + } + + } // End switch t->kind + #undef INT_SIZE_ENCODING + + GB_PANIC("Unreachable"); +} + +struct lbObjCGlobalClass { + lbObjCGlobal g; + lbValue class_value; // Local registered class value +}; + +gb_internal void lb_register_objc_thing( + StringSet &handled, + lbModule *m, + Array &args, + Array &class_impls, + StringMap &class_map, + lbProcedure *p, + lbObjCGlobal const &g, + char const *call +) { + if (string_set_update(&handled, g.name)) { + return; + } + + lbAddr addr = {}; + lbValue *found = string_map_get(&m->members, g.global_name); + if (found) { + addr = lb_addr(*found); + } else { + lbValue v = {}; + LLVMTypeRef t = lb_type(m, g.type); + v.value = LLVMAddGlobal(m->mod, t, g.global_name); + v.type = alloc_type_pointer(g.type); + addr = lb_addr(v); + 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{}; + + 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; + } + + args.count = 3; + args[0] = superclass == nullptr ? lb_const_nil(m, t_objc_Class) : 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); + + array_add(&class_impls, lbObjCGlobalClass{g, class_ptr}); + } + else { + args.count = 1; + args[0] = class_name; + class_ptr = lb_emit_runtime_call(p, call, args); + } + + 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; + } +} + gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { if (p == nullptr) { return; @@ -1186,39 +1512,238 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { string_set_init(&handled); defer (string_set_destroy(&handled)); - auto args = array_make(temporary_allocator(), 1); - - LLVMSetLinkage(p->value, LLVMInternalLinkage); - lb_begin_procedure_body(p); - - auto register_thing = [&handled, &m, &args](lbProcedure *p, lbObjCGlobal const &g, char const *call) { - if (!string_set_update(&handled, g.name)) { - lbAddr addr = {}; - lbValue *found = string_map_get(&m->members, g.global_name); - if (found) { - addr = lb_addr(*found); - } else { - lbValue v = {}; - LLVMTypeRef t = lb_type(m, g.type); - v.value = LLVMAddGlobal(m->mod, t, g.global_name); - v.type = alloc_type_pointer(g.type); - addr = lb_addr(v); - LLVMSetInitializer(v.value, LLVMConstNull(t)); - } - - args[0] = lb_const_value(m, t_cstring, exact_value_string(g.name)); - lbValue ptr = lb_emit_runtime_call(p, call, args); - lb_addr_store(p, addr, ptr); - } - }; - - for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) { - register_thing(p, g, "objc_lookUpClass"); - } - - for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_selectors, &g); /**/) { - register_thing(p, g, "sel_registerName"); - } + auto args = array_make(temporary_allocator(), 3, 8); + auto class_impls = array_make(temporary_allocator(), 0, 16); + + // Ensure classes that have been implicitly referenced through + // the objc_superclass attribute have a global variable available for them. + TypeSet class_set{}; + type_set_init(&class_set, gen->objc_classes.count+16); + defer (type_set_destroy(&class_set)); + + auto referenced_classes = array_make(temporary_allocator()); + for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) { + array_add( &referenced_classes, g); + + Type *cls = g.class_impl_type; + while (cls) { + if (type_set_update(&class_set, cls)) { + break; + } + GB_ASSERT(cls->kind == Type_Named); + + cls = cls->Named.type_name->TypeName.objc_superclass; + } + } + + for (auto pair : class_set) { + auto& tn = pair.type->Named.type_name->TypeName; + Type *class_impl = !tn.objc_is_implementation ? nullptr : pair.type; + lb_handle_objc_find_or_register_class(p, tn.objc_class_name, class_impl); + } + for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) { + array_add( &referenced_classes, g ); + } + + // Add all class globals to a map so that we can look them up dynamically + // in order to resolve out-of-order because classes that are being implemented + // need their superclasses to have been registered before them. + StringMap global_class_map{}; + string_map_init(&global_class_map, (usize)gen->objc_classes.count); + defer (string_map_destroy(&global_class_map)); + + for (lbObjCGlobal g :referenced_classes) { + string_map_set(&global_class_map, g.name, lbObjCGlobalClass{g}); + } + + LLVMSetLinkage(p->value, LLVMInternalLinkage); + lb_begin_procedure_body(p); + + // Register class globals, gathering classes that must be implemented + for (auto& kv : global_class_map) { + lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, kv.value.g, "objc_lookUpClass"); + } + + // Prefetch selectors for implemented methods so that they can also be registered. + for (const auto& cd : class_impls) { + auto& g = cd.g; + Type *class_type = g.class_impl_type; + + Array* methods = map_get(&m->info->objc_method_implementations, class_type); + if (!methods) { + continue; + } + + for (const ObjcMethodData& md : *methods) { + lb_handle_objc_find_or_register_selector(p, md.ac.objc_selector); + } + } + + // Now we can register all referenced selectors + for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_selectors, &g); /**/) { + lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, g, "sel_registerName"); + } + + + // Emit method wrapper implementations and registration + auto wrapper_args = array_make(temporary_allocator(), 2, 8); + + for (const auto& cd : class_impls) { + auto& g = cd.g; + Type *class_type = g.class_impl_type; + + Array* methods = map_get(&m->info->objc_method_implementations, class_type); + if (!methods) { + continue; + } + + Type *class_ptr_type = alloc_type_pointer(class_type); + lbValue class_value = cd.class_value; + + for (const ObjcMethodData& md : *methods) { + GB_ASSERT( md.proc_entity->kind == Entity_Procedure); + Type *method_type = md.proc_entity->type; + + String proc_name = make_string_c("__$objc_method::"); + proc_name = concatenate_strings(temporary_allocator(), proc_name, g.name); + proc_name = concatenate_strings(temporary_allocator(), proc_name, str_lit("::")); + proc_name = concatenate_strings( permanent_allocator(), proc_name, md.ac.objc_name); + + wrapper_args.count = 2; + wrapper_args[0] = md.ac.objc_is_class_method ? t_objc_Class : class_ptr_type; + wrapper_args[1] = t_objc_SEL; + + auto method_param_count = (isize)method_type->Proc.param_count; + i32 method_param_offset = 0; + + // TODO(harold): Need to make sure (at checker stage) that the non-class method has the self parameter already. + // (Maybe this is already accounted for?.) + if (!md.ac.objc_is_class_method) { + GB_ASSERT(method_param_count >= 1); + method_param_count -= 1; + method_param_offset = 1; + } + + for (i32 i = 0; i < method_param_count; i++) { + array_add(&wrapper_args, method_type->Proc.params->Tuple.variables[method_param_offset+i]->type); + } + + Type *wrapper_args_tuple = alloc_type_tuple_from_field_types(wrapper_args.data, wrapper_args.count, false, true); + Type *wrapper_proc_type = alloc_type_proc(nullptr, wrapper_args_tuple, (isize)wrapper_args_tuple->Tuple.variables.count, nullptr, 0, 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"); + + // Emit the wrapper + LLVMSetLinkage(wrapper_proc->value, LLVMExternalLinkage); + lb_begin_procedure_body(wrapper_proc); + { + auto method_call_args = array_make(temporary_allocator(), method_param_count + (isize)method_param_offset); + + if (!md.ac.objc_is_class_method) { + method_call_args[0] = lbValue { + wrapper_proc->raw_input_parameters[0], + class_ptr_type, + }; + } + + 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, + }; + } + 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. + lb_emit_call(wrapper_proc, method_proc_value, method_call_args); + } + 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, temporary_allocator()); + } + + if (!md.ac.objc_is_class_method) { + method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("@:")); + } else { + method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("#:")); + } + + for (i32 i = method_param_offset; i < method_param_count; i++) { + Type *param_type = method_type->Proc.params->Tuple.variables[i]->type; + String param_encoding = lb_get_objc_type_encoding(param_type, temporary_allocator()); + + method_encoding = concatenate_strings(temporary_allocator(), method_encoding, param_encoding); + } + + // Emit method registration + lbAddr* sel_address = string_map_get(&m->objc_selectors, md.ac.objc_selector); + GB_ASSERT(sel_address); + lbValue selector_value = lb_addr_load(p, *sel_address); + + args.count = 4; + args[0] = class_value; // Class + args[1] = selector_value; // SEL + 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. + lb_emit_runtime_call(p, "class_addMethod", args); + + } // End methods + + // Add ivar if we have one + Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar; + if (ivar_type != nullptr) { + // Register a single ivar for this class + Type *ivar_base = ivar_type->Named.base; + // TODO(harold): No idea if I can use this, but I assume so? + const i64 size = ivar_base->cached_size; + const i64 alignment = ivar_base->cached_align; + // TODO(harold): Checker: Alignment must be compatible with ivar rules. Or we should increase the alignment if needed. + + String ivar_name = str_lit("__$ivar"); + String ivar_types = str_lit("{= }"); + args.count = 5; + args[0] = class_value; + args[1] = lb_const_value(m, t_cstring, exact_value_string(ivar_name)); + args[2] = lb_const_value(m, t_uint, exact_value_u64((u64)size)); + args[3] = lb_const_value(m, t_u8, exact_value_u64((u64)alignment)); + args[4] = lb_const_value(m, t_cstring, exact_value_string(ivar_types)); + lb_emit_runtime_call(p, "class_addIvar", args); + } + + // Complete the class registration + args.count = 1; + args[0] = class_value; + lb_emit_runtime_call(p, "objc_registerClassPair", args); + + // If we have an ivar, store its offset globally for an intrinsic + // TODO(harold): Only do this for types that had ivar_get calls registered! + if (ivar_type != nullptr) { + args.count = 2; + args[0] = class_value; + args[1] = lb_const_value(m, t_cstring, exact_value_string(str_lit("__$ivar"))); + lbValue ivar = lb_emit_runtime_call(p, "class_getInstanceVariable", args); + + args.count = 1; + args[0] = ivar; + lbValue ivar_offset = lb_emit_runtime_call(p, "ivar_getOffset", args); + lbValue ivar_offset_u32 = lb_emit_conv(p, ivar_offset, t_u32); + + String class_name = class_type->Named.type_name->TypeName.objc_class_name; + // TODO(harold): Oops! This is wrong, that map is there to prevent re-entry. + // Simply emit from referred ivars. For now use a single module only. + lbAddr ivar_addr = string_map_must_get(&m->objc_ivars, class_name); + lb_addr_store(p, ivar_addr, ivar_offset_u32); + } + } lb_end_procedure_body(p); } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 6177fcf6e..7694c65c3 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -196,6 +196,7 @@ struct lbModule { StringMap objc_classes; StringMap objc_selectors; + StringMap objc_ivars; PtrMap map_cell_info_map; // address of runtime.Map_Info PtrMap map_info_map; // address of runtime.Map_Cell_Info @@ -219,6 +220,7 @@ struct lbObjCGlobal { gbString global_name; String name; Type * type; + Type * class_impl_type; // This is set when the class has the objc_implement attribute set to true. }; struct lbGenerator : LinkerData { @@ -240,6 +242,7 @@ struct lbGenerator : LinkerData { MPSCQueue entities_to_correct_linkage; MPSCQueue objc_selectors; MPSCQueue objc_classes; + MPSCQueue objc_ivars; }; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 421720c4c..7f012e006 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -101,6 +101,7 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) { string_map_init(&m->objc_classes); string_map_init(&m->objc_selectors); + string_map_init(&m->objc_ivars); map_init(&m->map_info_map, 0); map_init(&m->map_cell_info_map, 0); @@ -173,6 +174,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { mpsc_init(&gen->entities_to_correct_linkage, heap_allocator()); mpsc_init(&gen->objc_selectors, heap_allocator()); mpsc_init(&gen->objc_classes, heap_allocator()); + mpsc_init(&gen->objc_ivars, heap_allocator()); return true; } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 3212abd9a..bf4ebf377 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3290,6 +3290,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu case BuiltinProc_objc_find_class: return lb_handle_objc_find_class(p, expr); case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr); 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_constant_utf16_cstring: diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index bfeebfcbe..897b71b5b 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2125,7 +2125,7 @@ gb_internal lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, Stri return addr; } -gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name) { +gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name, Type *class_impl_type) { lbModule *m = p->module; lbAddr *found = string_map_get(&m->objc_classes, name); if (found) { @@ -2148,13 +2148,72 @@ gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String } else { LLVMSetLinkage(g.value, LLVMExternalLinkage); } - mpsc_enqueue(&m->gen->objc_classes, lbObjCGlobal{m, global_name, name, t_objc_Class}); + mpsc_enqueue(&m->gen->objc_classes, lbObjCGlobal{m, global_name, name, t_objc_Class, class_impl_type}); lbAddr addr = lb_addr(g); string_map_set(&m->objc_classes, name, addr); return addr; } +gb_internal lbAddr lb_handle_objc_find_or_register_ivar(lbModule *m, Type *self_type) { + + String name = self_type->Named.type_name->TypeName.objc_class_name; + GB_ASSERT(name != ""); + + lbAddr *found = string_map_get(&m->objc_ivars, name); + if (found) { + return *found; + } + + + lbModule *default_module = &m->gen->default_module; + + gbString global_name = gb_string_make(permanent_allocator(), "__$objc_ivar::"); + global_name = gb_string_append_length(global_name, name.text, name.len); + + // Create a global variable to store offset of the ivar in an instance of an object + Type *p_ivar_offset = alloc_type_pointer(t_u32); + + LLVMTypeRef t = lb_type(m, p_ivar_offset); + lbValue g = {}; + g.value = LLVMAddGlobal(m->mod, t, global_name); + g.type = p_ivar_offset; + + if (default_module == m) { + LLVMSetInitializer(g.value, LLVMConstNull(t)); + lb_add_member(m, make_string_c(global_name), g); + } else { + LLVMSetLinkage(g.value, LLVMExternalLinkage); + } + + mpsc_enqueue(&m->gen->objc_ivars, lbObjCGlobal{m, global_name, name, self_type}); + + lbAddr addr = lb_addr(g); + string_map_set(&m->objc_ivars, name, addr); + return addr; +} + +gb_internal lbValue lb_handle_objc_ivar_get(lbProcedure *p, Ast *expr) { + ast_node(ce, CallExpr, expr); + lbModule *m = p->module; + + GB_ASSERT(ce->args[0]->tav.type->kind == Type_Pointer); + Type *self_type = ce->args[0]->tav.type->Pointer.elem; + Type *ivar_type = self_type->Named.type_name->TypeName.objc_ivar; + + Type* p_ivar = alloc_type_pointer(ivar_type); + + lbValue ivar_offset = lb_addr_load(p, lb_handle_objc_find_or_register_ivar(m, self_type)); + lbValue ivar_offset_uptr = lb_emit_conv(p, ivar_offset, t_uintptr); + + lbValue self = lb_build_expr(p, ce->args[0]); + lbValue self_uptr = lb_emit_conv(p, self, t_uintptr); + + lbValue ivar_uptr = lb_emit_arith(p, Token_Add, self_uptr, ivar_offset_uptr, t_uintptr); + + return lb_emit_conv(p, ivar_uptr, p_ivar); +} + gb_internal lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) { ast_node(ce, CallExpr, expr); @@ -2188,7 +2247,7 @@ gb_internal lbValue lb_handle_objc_find_class(lbProcedure *p, Ast *expr) { auto tav = ce->args[0]->tav; GB_ASSERT(tav.value.kind == ExactValue_String); String name = tav.value.value_string; - return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name)); + return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name, nullptr)); } gb_internal lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) { @@ -2198,7 +2257,7 @@ gb_internal lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) { auto tav = ce->args[0]->tav; GB_ASSERT(tav.value.kind == ExactValue_String); String name = tav.value.value_string; - lbAddr dst = lb_handle_objc_find_or_register_class(p, name); + lbAddr dst = lb_handle_objc_find_or_register_class(p, name, nullptr); auto args = array_make(permanent_allocator(), 3); args[0] = lb_const_nil(m, t_objc_Class); @@ -2220,7 +2279,9 @@ gb_internal lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) { GB_ASSERT(e->kind == Entity_TypeName); String name = e->TypeName.objc_class_name; - return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name)); + Type *class_impl_type = e->TypeName.objc_is_implementation ? type : nullptr; + + return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name, class_impl_type)); } return lb_build_expr(p, expr); @@ -2266,9 +2327,6 @@ gb_internal lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) { 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/types.cpp b/src/types.cpp index 9c9472a28..1b2545279 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -729,10 +729,12 @@ gb_global Type *t_map_set_proc = nullptr; 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_id = nullptr; gb_global Type *t_objc_SEL = nullptr; gb_global Type *t_objc_Class = nullptr; +gb_global Type *t_objc_Ivar = nullptr; enum OdinAtomicMemoryOrder : i32 { OdinAtomicMemoryOrder_relaxed = 0, // unordered -- cgit v1.2.3 From f3923ed66640ea9fd342ca851fdd2bd794405e0c Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Sun, 27 Apr 2025 22:48:16 -0400 Subject: Fix indentations Fix Objective-C wrapper procs not forwarding return value --- src/check_builtin.cpp | 144 +++--- src/check_decl.cpp | 174 +++---- src/checker.cpp | 150 +++--- src/checker.hpp | 18 +- src/checker_builtin_procs.hpp | 4 +- src/entity.cpp | 6 +- src/llvm_backend.cpp | 1108 +++++++++++++++++++++-------------------- src/llvm_backend.hpp | 6 +- src/llvm_backend_general.cpp | 4 +- src/llvm_backend_proc.cpp | 2 +- src/llvm_backend_utility.cpp | 14 +- 11 files changed, 822 insertions(+), 808 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 92942b4db..099f99045 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -389,77 +389,77 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan } break; case BuiltinProc_objc_ivar_get: - { - Type *self_type = nullptr; - Type *ivar_type = nullptr; - - Operand self = {}; - check_expr_or_type(c, &self, ce->args[0]); - - if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) { - gbString e = expr_to_string(self.expr); - gbString t = type_to_string(self.type); - error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t); - gb_string_free(t); - gb_string_free(e); - return false; - } else if (!is_type_pointer(self.type)) { - gbString e = expr_to_string(self.expr); - gbString t = type_to_string(self.type); - error(self.expr, "'%.*s' expected a pointer of a value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t); - gb_string_free(t); - gb_string_free(e); - return false; - } - - self_type = type_deref(self.type); - - if (!(self_type->kind == Type_Named && - self_type->Named.type_name != nullptr && - self_type->Named.type_name->TypeName.objc_class_name != "")) { - gbString t = type_to_string(self_type); - error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=) , got type %s", LIT(builtin_name), t); - gb_string_free(t); - return false; - } - - if (self_type->Named.type_name->TypeName.objc_ivar == nullptr) { - gbString t = type_to_string(self_type); - error(self.expr, "'%.*s' requires that type %s have the attribute @(obj_ivar=).", LIT(builtin_name), t); - gb_string_free(t); - return false; - } - - Operand ivar = {}; - check_expr_or_type(c, &ivar, ce->args[1]); - if (ivar.mode == Addressing_Type) { - ivar_type = ivar.type; - } else { - return false; - } - - if (self_type->Named.type_name->TypeName.objc_ivar != ivar_type) { - gbString name_self = type_to_string(self_type); - gbString name_expected = type_to_string(self_type->Named.type_name->TypeName.objc_ivar); - gbString name_given = type_to_string(ivar_type); - error(self.expr, "'%.*s' ivar type %s does not match @obj_ivar type %s on Objective-C class %s.", - LIT(builtin_name), name_given, name_expected, name_self); - gb_string_free(name_self); - gb_string_free(name_expected); - gb_string_free(name_given); - return false; - } - - if (type_hint != nullptr && type_hint->kind == Type_Pointer && type_hint->Pointer.elem == ivar_type) { - operand->type = type_hint; - } else { - operand->type = alloc_type_pointer(ivar_type); - } - - operand->mode = Addressing_Value; - - return true; - } break; + { + Type *self_type = nullptr; + Type *ivar_type = nullptr; + + Operand self = {}; + check_expr_or_type(c, &self, ce->args[0]); + + if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) { + gbString e = expr_to_string(self.expr); + gbString t = type_to_string(self.type); + error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } else if (!is_type_pointer(self.type)) { + gbString e = expr_to_string(self.expr); + gbString t = type_to_string(self.type); + error(self.expr, "'%.*s' expected a pointer of a value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + self_type = type_deref(self.type); + + if (!(self_type->kind == Type_Named && + self_type->Named.type_name != nullptr && + self_type->Named.type_name->TypeName.objc_class_name != "")) { + gbString t = type_to_string(self_type); + error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=) , got type %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + if (self_type->Named.type_name->TypeName.objc_ivar == nullptr) { + gbString t = type_to_string(self_type); + error(self.expr, "'%.*s' requires that type %s have the attribute @(obj_ivar=).", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + Operand ivar = {}; + check_expr_or_type(c, &ivar, ce->args[1]); + if (ivar.mode == Addressing_Type) { + ivar_type = ivar.type; + } else { + return false; + } + + if (self_type->Named.type_name->TypeName.objc_ivar != ivar_type) { + gbString name_self = type_to_string(self_type); + gbString name_expected = type_to_string(self_type->Named.type_name->TypeName.objc_ivar); + gbString name_given = type_to_string(ivar_type); + error(self.expr, "'%.*s' ivar type %s does not match @obj_ivar type %s on Objective-C class %s.", + LIT(builtin_name), name_given, name_expected, name_self); + gb_string_free(name_self); + gb_string_free(name_expected); + gb_string_free(name_given); + return false; + } + + if (type_hint != nullptr && type_hint->kind == Type_Pointer && type_hint->Pointer.elem == ivar_type) { + operand->type = type_hint; + } else { + operand->type = alloc_type_pointer(ivar_type); + } + + operand->mode = Addressing_Value; + + return true; + } break; } } @@ -2206,7 +2206,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_objc_find_class: case BuiltinProc_objc_register_selector: case BuiltinProc_objc_register_class: - case BuiltinProc_objc_ivar_get: + case BuiltinProc_objc_ivar_get: 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 e67241b31..48e5172d6 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -526,68 +526,68 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac); if (e->kind == Entity_TypeName && ac.objc_class != "") { e->TypeName.objc_class_name = ac.objc_class; - e->TypeName.objc_superclass = ac.objc_superclass; - e->TypeName.objc_ivar = ac.objc_ivar; + e->TypeName.objc_superclass = ac.objc_superclass; + e->TypeName.objc_ivar = ac.objc_ivar; e->TypeName.objc_context_provider = ac.objc_context_provider; - if (ac.objc_is_implementation) { - e->TypeName.objc_is_implementation = true; - mpsc_enqueue(&ctx->info->objc_class_implementations, e); // TODO(harold): Don't need this for anything? See if needed when using explicit @export - - GB_ASSERT(e->TypeName.objc_ivar == nullptr || e->TypeName.objc_ivar->kind == Type_Named); - - // Enqueue the proc to be checked when resolved - if (e->TypeName.objc_context_provider != nullptr) { - mpsc_enqueue(&ctx->checker->procs_with_objc_context_provider_to_check, e); - } - - // @TODO(harold): I think there's a Check elsewhere in the checker for checking cycles. - // See about moving this to the right location. - // Ensure superclass hierarchy are all Objective-C classes and does not cycle - Type *super = ac.objc_superclass; - if (super != nullptr) { - TypeSet super_set{}; - type_set_init(&super_set, 8); - defer (type_set_destroy(&super_set)); - - type_set_update(&super_set, e->type); - - for (;;) { - if (type_set_update(&super_set, super)) { - error(e->token, "@(objc_superclass) Superclass hierarchy cycle encountered"); - break; - } - - if (super->kind != Type_Named) { - error(e->token, "@(objc_superclass) References type must be a named struct."); - break; - } - - Type* named_type = base_type(super->Named.type_name->type); - if (!is_type_objc_object(named_type)) { - error(e->token, "@(objc_superclass) Superclass must be an Objective-C class."); - break; - } - - super = super->Named.type_name->TypeName.objc_superclass; - if (super == nullptr) { - break; - } - - // TODO(harold): Is this the right way to do this??? The referenced entity must be already resolved - // so that we can access its objc_superclass attribute - check_single_global_entity(ctx->checker, super->Named.type_name, super->Named.type_name->decl_info); - } - } - } else { - if (e->TypeName.objc_superclass != nullptr) { - error(e->token, "@(objc_superclass) can only be applied when the @(obj_implement) attribute is also applied"); - } else if (e->TypeName.objc_ivar != nullptr) { - error(e->token, "@(objc_ivar) can only be applied when the @(obj_implement) attribute is also applied"); - } else if (e->TypeName.objc_context_provider != nullptr) { - error(e->token, "@(objc_context_provider) can only be applied when the @(obj_implement) attribute is also applied"); - } - } + if (ac.objc_is_implementation) { + e->TypeName.objc_is_implementation = true; + mpsc_enqueue(&ctx->info->objc_class_implementations, e); // TODO(harold): Don't need this for anything? See if needed when using explicit @export + + GB_ASSERT(e->TypeName.objc_ivar == nullptr || e->TypeName.objc_ivar->kind == Type_Named); + + // Enqueue the proc to be checked when resolved + if (e->TypeName.objc_context_provider != nullptr) { + mpsc_enqueue(&ctx->checker->procs_with_objc_context_provider_to_check, e); + } + + // @TODO(harold): I think there's a Check elsewhere in the checker for checking cycles. + // See about moving this to the right location. + // Ensure superclass hierarchy are all Objective-C classes and does not cycle + Type *super = ac.objc_superclass; + if (super != nullptr) { + TypeSet super_set{}; + type_set_init(&super_set, 8); + defer (type_set_destroy(&super_set)); + + type_set_update(&super_set, e->type); + + for (;;) { + if (type_set_update(&super_set, super)) { + error(e->token, "@(objc_superclass) Superclass hierarchy cycle encountered"); + break; + } + + if (super->kind != Type_Named) { + error(e->token, "@(objc_superclass) References type must be a named struct."); + break; + } + + Type* named_type = base_type(super->Named.type_name->type); + if (!is_type_objc_object(named_type)) { + error(e->token, "@(objc_superclass) Superclass must be an Objective-C class."); + break; + } + + super = super->Named.type_name->TypeName.objc_superclass; + if (super == nullptr) { + break; + } + + // TODO(harold): Is this the right way to do this??? The referenced entity must be already resolved + // so that we can access its objc_superclass attribute + check_single_global_entity(ctx->checker, super->Named.type_name, super->Named.type_name->decl_info); + } + } + } else { + if (e->TypeName.objc_superclass != nullptr) { + error(e->token, "@(objc_superclass) can only be applied when the @(obj_implement) attribute is also applied"); + } else if (e->TypeName.objc_ivar != nullptr) { + error(e->token, "@(objc_ivar) can only be applied when the @(obj_implement) attribute is also applied"); + } else if (e->TypeName.objc_context_provider != nullptr) { + error(e->token, "@(objc_context_provider) can only be applied when the @(obj_implement) attribute is also applied"); + } + } if (type_size_of(e->type) > 0) { error(e->token, "@(objc_class) marked type must be of zero size"); @@ -1005,37 +1005,37 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope"); } else { - if (ac.objc_is_implementation) { - GB_ASSERT(e->kind == Entity_Procedure); + if (ac.objc_is_implementation) { + GB_ASSERT(e->kind == Entity_Procedure); - Type *proc_type = e->type; + Type *proc_type = e->type; - 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 (proc_type->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_type->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 (!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 (proc_type->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_type->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 { auto method = ObjcMethodData{ ac, e }; - method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name; - - 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; - - map_set(&info->objc_method_implementations, t, list); - } - } - } + method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name; + + 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; + + map_set(&info->objc_method_implementations, t, list); + } + } + } mutex_lock(&global_type_name_objc_metadata_mutex); defer (mutex_unlock(&global_type_name_objc_metadata_mutex)); diff --git a/src/checker.cpp b/src/checker.cpp index 79c773a3c..6563b1c58 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1351,12 +1351,12 @@ gb_internal void init_universal(void) { t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct_complete()); t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct_complete()); t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct_complete()); - t_objc_ivar = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_ivar"), alloc_type_struct_complete()); + t_objc_ivar = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_ivar"), alloc_type_struct_complete()); t_objc_id = alloc_type_pointer(t_objc_object); 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_Ivar = alloc_type_pointer(t_objc_ivar); } } @@ -1389,8 +1389,8 @@ gb_internal void init_checker_info(CheckerInfo *i) { array_init(&i->defineables, a); map_init(&i->objc_msgSend_types); - mpsc_init(&i->objc_class_implementations, a); - map_init(&i->objc_method_implementations); + mpsc_init(&i->objc_class_implementations, a); + map_init(&i->objc_method_implementations); string_map_init(&i->load_file_cache); array_init(&i->all_procedures, heap_allocator()); @@ -3352,10 +3352,10 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { ac->test = true; return true; } else if (name == "export") { - if (ac->objc_is_implementation) { - error(value, "Setting @(export) explicitly is not allowed when @(objc_implement) is set. It is exported implicitly."); - return false; - } + if (ac->objc_is_implementation) { + error(value, "Setting @(export) explicitly is not allowed when @(objc_implement) is set. It is exported implicitly."); + return false; + } ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind == ExactValue_Invalid) { @@ -3369,10 +3369,10 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { return true; } else if (name == "linkage") { - if (ac->objc_is_implementation) { - error(value, "Explicit linkage not allowed when @(objc_implement) is set. It is set implicitly"); - return false; - } + if (ac->objc_is_implementation) { + error(value, "Explicit linkage not allowed when @(objc_implement) is set. It is set implicitly"); + return false; + } ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind != ExactValue_String) { @@ -3681,23 +3681,23 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { } return true; } else if (name == "objc_implement") { - ExactValue ev = check_decl_attribute_value(c, value); - if (ev.kind == ExactValue_Bool) { - ac->objc_is_implementation = ev.value_bool; - } else if (ev.kind == ExactValue_Invalid) { - ac->objc_is_implementation = true; - } else { - error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name)); - } - - // This implies exported, strongly linked - if (ac->objc_is_implementation) { - ac->is_export = true; - ac->linkage = str_lit("strong"); - } - - return true; - } else if (name == "objc_selector") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_Bool) { + ac->objc_is_implementation = ev.value_bool; + } else if (ev.kind == ExactValue_Invalid) { + ac->objc_is_implementation = true; + } else { + error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name)); + } + + // This implies exported, strongly linked + if (ac->objc_is_implementation) { + ac->is_export = true; + ac->linkage = str_lit("strong"); + } + + return true; + } else if (name == "objc_selector") { ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind == ExactValue_String) { if (string_is_valid_identifier(ev.value_string)) { @@ -3949,52 +3949,52 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) { } return true; } else if (name == "objc_implement") { - ExactValue ev = check_decl_attribute_value(c, value); - if (ev.kind == ExactValue_Bool) { - ac->objc_is_implementation = ev.value_bool; - } else if (ev.kind == ExactValue_Invalid) { - ac->objc_is_implementation = true; - } else { - error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name)); - } - return true; - } else if (name == "objc_superclass") { - Type *objc_superclass = check_type(c, value); - - if (objc_superclass != nullptr) { - ac->objc_superclass = objc_superclass; - } else { - error(value, "'%.*s' expected a named type", LIT(name)); - } - return true; - } else if (name == "objc_ivar") { - Type *objc_ivar = check_type(c, value); - - if (objc_ivar != nullptr) { - ac->objc_ivar = objc_ivar; - } else { - error(value, "'%.*s' expected a named type", LIT(name)); - } - return true; - } else if (name == "objc_context_provider") { - Operand o = {}; - check_expr(c, &o, value); - Entity *e = entity_of_node(o.expr); - - if (e != nullptr) { - if (ac->objc_context_provider != nullptr) { - error(elem, "Previous usage of a 'objc_context_provider' attribute"); - } - if (e->kind != Entity_Procedure) { - error(elem, "'objc_context_provider' must refer to a procedure"); - } else { - ac->objc_context_provider = e; - } - - return true; - } - } - return false; + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_Bool) { + ac->objc_is_implementation = ev.value_bool; + } else if (ev.kind == ExactValue_Invalid) { + ac->objc_is_implementation = true; + } else { + error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name)); + } + return true; + } else if (name == "objc_superclass") { + Type *objc_superclass = check_type(c, value); + + if (objc_superclass != nullptr) { + ac->objc_superclass = objc_superclass; + } else { + error(value, "'%.*s' expected a named type", LIT(name)); + } + return true; + } else if (name == "objc_ivar") { + Type *objc_ivar = check_type(c, value); + + if (objc_ivar != nullptr) { + ac->objc_ivar = objc_ivar; + } else { + error(value, "'%.*s' expected a named type", LIT(name)); + } + return true; + } else if (name == "objc_context_provider") { + Operand o = {}; + check_expr(c, &o, value); + Entity *e = entity_of_node(o.expr); + + if (e != nullptr) { + if (ac->objc_context_provider != nullptr) { + error(elem, "Previous usage of a 'objc_context_provider' attribute"); + } + if (e->kind != Entity_Procedure) { + error(elem, "'objc_context_provider' must refer to a procedure"); + } else { + ac->objc_context_provider = e; + } + + return true; + } + } + return false; } diff --git a/src/checker.hpp b/src/checker.hpp index 574c71c7f..336f09a7e 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -148,13 +148,13 @@ struct AttributeContext { String objc_class; String objc_name; - String objc_selector; + String objc_selector; Type * objc_type; - Type * objc_superclass; - Type * objc_ivar; + Type * objc_superclass; + Type * objc_ivar; Entity *objc_context_provider; bool objc_is_class_method : 1; - bool objc_is_implementation : 1; // This struct or proc provides a class/method implementation, not a binding to an existing type. + bool objc_is_implementation : 1; // This struct or proc provides a class/method implementation, not a binding to an existing type. String require_target_feature; // required by the target micro-architecture String enable_target_feature; // will be enabled for the procedure only @@ -371,8 +371,8 @@ struct ObjcMsgData { }; struct ObjcMethodData { - AttributeContext ac; - Entity *proc_entity; + AttributeContext ac; + Entity *proc_entity; }; enum LoadFileTier { @@ -489,10 +489,10 @@ struct CheckerInfo { BlockingMutex objc_types_mutex; PtrMap objc_msgSend_types; - MPSCQueue objc_class_implementations; + MPSCQueue objc_class_implementations; - BlockingMutex objc_method_mutex; - PtrMap> objc_method_implementations; + BlockingMutex objc_method_mutex; + PtrMap> objc_method_implementations; BlockingMutex load_file_mutex; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index cb2ce3915..ce7d8349b 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -331,7 +331,7 @@ BuiltinProc__type_end, BuiltinProc_objc_find_class, BuiltinProc_objc_register_selector, BuiltinProc_objc_register_class, - BuiltinProc_objc_ivar_get, + BuiltinProc_objc_ivar_get, BuiltinProc_constant_utf16_cstring, @@ -674,7 +674,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {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("ivar_get"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("ivar_get"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/entity.cpp b/src/entity.cpp index a5443cf27..cc41b5e59 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -235,9 +235,9 @@ struct Entity { Type * type_parameter_specialization; String ir_mangled_name; bool is_type_alias; - bool objc_is_implementation; - Type* objc_superclass; - Type* objc_ivar; + bool objc_is_implementation; + Type* objc_superclass; + Type* objc_ivar; Entity*objc_context_provider; String objc_class_name; TypeNameObjCMetadata *objc_metadata; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index fad542d4a..7ffd4ea30 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1176,327 +1176,327 @@ gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) { // TODO(harold): Move this out of here and into a more suitable place. // TODO(harold): Should not take an allocator, but always use temp, as we return string literals as well. String lb_get_objc_type_encoding(Type *t, gbAllocator allocator, isize pointer_depth = 0) { - // NOTE(harold): See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100 - - // NOTE(harold): Darwin targets are always 64-bit. Should we drop this and assume "q" always? - #define INT_SIZE_ENCODING (build_context.metrics.ptr_size == 4 ? "i" : "q") - switch (t->kind) { - case Type_Basic: { - switch (t->Basic.kind) { - case Basic_Invalid: - return str_lit("?"); - - case Basic_llvm_bool: - case Basic_bool: - case Basic_b8: - return str_lit("B"); - - case Basic_b16: - return str_lit("C"); - case Basic_b32: - return str_lit("I"); - case Basic_b64: - return str_lit("q"); - case Basic_i8: - return str_lit("c"); - case Basic_u8: - return str_lit("C"); - case Basic_i16: - case Basic_i16le: - case Basic_i16be: - return str_lit("s"); - case Basic_u16: - case Basic_u16le: - case Basic_u16be: - return str_lit("S"); - case Basic_i32: - case Basic_i32le: - case Basic_i32be: - return str_lit("i"); - case Basic_u32le: - case Basic_u32: - case Basic_u32be: - return str_lit("I"); - case Basic_i64: - case Basic_i64le: - case Basic_i64be: - return str_lit("q"); - case Basic_u64: - case Basic_u64le: - case Basic_u64be: - return str_lit("Q"); - case Basic_i128: - case Basic_i128le: - case Basic_i128be: - return str_lit("t"); - case Basic_u128: - case Basic_u128le: - case Basic_u128be: - return str_lit("T"); - case Basic_rune: - return str_lit("I"); - case Basic_f16: - case Basic_f16le: - case Basic_f16be: - return str_lit("s"); // @harold: Closest we've got? - case Basic_f32: - case Basic_f32le: - case Basic_f32be: - return str_lit("f"); - case Basic_f64: - case Basic_f64le: - case Basic_f64be: - return str_lit("d"); - - // TODO(harold) These: - case Basic_complex32: - case Basic_complex64: - case Basic_complex128: - case Basic_quaternion64: - case Basic_quaternion128: - case Basic_quaternion256: - return str_lit("?"); - - case Basic_int: - return str_lit(INT_SIZE_ENCODING); - case Basic_uint: - return build_context.metrics.ptr_size == 4 ? str_lit("I") : str_lit("Q"); - case Basic_uintptr: - case Basic_rawptr: - return str_lit("^v"); - - case Basic_string: - return build_context.metrics.ptr_size == 4 ? str_lit("{string=*i}") : str_lit("{string=*q}"); - - case Basic_cstring: return str_lit("*"); - case Basic_any: return str_lit("{any=^v^v"); // rawptr + ^Type_Info - - case Basic_typeid: - GB_ASSERT(t->Basic.size == 8); - return str_lit("q"); - - // Untyped types - case Basic_UntypedBool: - case Basic_UntypedInteger: - case Basic_UntypedFloat: - case Basic_UntypedComplex: - case Basic_UntypedQuaternion: - case Basic_UntypedString: - case Basic_UntypedRune: - case Basic_UntypedNil: - case Basic_UntypedUninit: - GB_PANIC("Untyped types cannot be @encoded()"); - return str_lit("?"); - } - break; - } - - case Type_Named: - case Type_Struct: - case Type_Union: { - Type* base = t; - if (base->kind == Type_Named) { - base = base_type(base); - if(base->kind != Type_Struct && base->kind != Type_Union) { - return lb_get_objc_type_encoding(base, allocator, pointer_depth); - } - } - - const bool is_union = base->kind == Type_Union; - if (!is_union) { - // Check for objc_SEL - if (internal_check_is_assignable_to(base, t_objc_SEL)) { - return str_lit(":"); - } - - // Check for objc_Class - if (internal_check_is_assignable_to(base, t_objc_SEL)) { - return str_lit("#"); - } - - // Treat struct as an Objective-C Class? - if (has_type_got_objc_class_attribute(base) && pointer_depth == 0) { - return str_lit("#"); - } - } - - if (is_type_objc_object(base)) { - return str_lit("@"); - } - - - gbString s = gb_string_make_reserve(allocator, 16); - s = gb_string_append_length(s, is_union ? "(" :"{", 1); - if (t->kind == Type_Named) { - s = gb_string_append_length(s, t->Named.name.text, t->Named.name.len); - } - - // Write fields - if (pointer_depth < 2) { - s = gb_string_append_length(s, "=", 1); - - if (!is_union) { - for( auto& f : base->Struct.fields ) { - String field_type = lb_get_objc_type_encoding(f->type, allocator, pointer_depth); - s = gb_string_append_length(s, field_type.text, field_type.len); - } - } else { - // #TODO(harold): Encode fields - } - } - - s = gb_string_append_length(s, is_union ? ")" :"}", 1); - - return make_string_c(s); - } - - case Type_Generic: - GB_PANIC("Generic types cannot be @encoded()"); - return str_lit("?"); - - case Type_Pointer: { - String pointee = lb_get_objc_type_encoding(t->Pointer.elem, allocator, pointer_depth +1); - // Special case for Objective-C Objects - if (pointer_depth == 0 && pointee == "@") { - return pointee; - } - - return concatenate_strings(allocator, str_lit("^"), pointee); - } - - case Type_MultiPointer: - return concatenate_strings(allocator, str_lit("^"), lb_get_objc_type_encoding(t->Pointer.elem, allocator, pointer_depth +1)); - - case Type_Array: { - String type_str = lb_get_objc_type_encoding(t->Array.elem, allocator, pointer_depth); - - gbString s = gb_string_make_reserve(allocator, type_str.len + 8); - s = gb_string_append_fmt(s, "[%lld%s]", t->Array.count, type_str.text); - return make_string_c(s); - } - - case Type_EnumeratedArray: { - String type_str = lb_get_objc_type_encoding(t->EnumeratedArray.elem, allocator, pointer_depth); - - gbString s = gb_string_make_reserve(allocator, type_str.len + 8); - s = gb_string_append_fmt(s, "[%lld%s]", t->EnumeratedArray.count, type_str.text); - return make_string_c(s); - } - - case Type_Slice: { - String type_str = lb_get_objc_type_encoding(t->Slice.elem, allocator, pointer_depth); - gbString s = gb_string_make_reserve(allocator, type_str.len + 8); - s = gb_string_append_fmt(s, "{slice=^%s%s}", type_str, INT_SIZE_ENCODING); - return make_string_c(s); - } - - case Type_DynamicArray: { - String type_str = lb_get_objc_type_encoding(t->DynamicArray.elem, allocator, pointer_depth); - gbString s = gb_string_make_reserve(allocator, type_str.len + 8); - s = gb_string_append_fmt(s, "{dynamic=^%s%s%sAllocator={?^v}}", type_str, INT_SIZE_ENCODING, INT_SIZE_ENCODING); - return make_string_c(s); - } - - case Type_Map: - return str_lit("{^v^v{Allocator=?^v}}"); - case Type_Enum: - return lb_get_objc_type_encoding(t->Enum.base_type, allocator, pointer_depth); - case Type_Tuple: - // NOTE(harold): Is this allowed here? - return str_lit("?"); - case Type_Proc: - return str_lit("?"); - case Type_BitSet: - return lb_get_objc_type_encoding(t->BitSet.underlying, allocator, pointer_depth); - case Type_SimdVector: - break; - case Type_Matrix: - break; - case Type_BitField: - return lb_get_objc_type_encoding(t->BitField.backing_type, allocator, pointer_depth); - case Type_SoaPointer: { - gbString s = gb_string_make_reserve(allocator, 8); - s = gb_string_append_fmt(s, "{=^v%s}", INT_SIZE_ENCODING); - return make_string_c(s); - } - - } // End switch t->kind - #undef INT_SIZE_ENCODING - - GB_PANIC("Unreachable"); + // NOTE(harold): See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100 + + // NOTE(harold): Darwin targets are always 64-bit. Should we drop this and assume "q" always? + #define INT_SIZE_ENCODING (build_context.metrics.ptr_size == 4 ? "i" : "q") + switch (t->kind) { + case Type_Basic: { + switch (t->Basic.kind) { + case Basic_Invalid: + return str_lit("?"); + + case Basic_llvm_bool: + case Basic_bool: + case Basic_b8: + return str_lit("B"); + + case Basic_b16: + return str_lit("C"); + case Basic_b32: + return str_lit("I"); + case Basic_b64: + return str_lit("q"); + case Basic_i8: + return str_lit("c"); + case Basic_u8: + return str_lit("C"); + case Basic_i16: + case Basic_i16le: + case Basic_i16be: + return str_lit("s"); + case Basic_u16: + case Basic_u16le: + case Basic_u16be: + return str_lit("S"); + case Basic_i32: + case Basic_i32le: + case Basic_i32be: + return str_lit("i"); + case Basic_u32le: + case Basic_u32: + case Basic_u32be: + return str_lit("I"); + case Basic_i64: + case Basic_i64le: + case Basic_i64be: + return str_lit("q"); + case Basic_u64: + case Basic_u64le: + case Basic_u64be: + return str_lit("Q"); + case Basic_i128: + case Basic_i128le: + case Basic_i128be: + return str_lit("t"); + case Basic_u128: + case Basic_u128le: + case Basic_u128be: + return str_lit("T"); + case Basic_rune: + return str_lit("I"); + case Basic_f16: + case Basic_f16le: + case Basic_f16be: + return str_lit("s"); // @harold: Closest we've got? + case Basic_f32: + case Basic_f32le: + case Basic_f32be: + return str_lit("f"); + case Basic_f64: + case Basic_f64le: + case Basic_f64be: + return str_lit("d"); + + // TODO(harold) These: + case Basic_complex32: + case Basic_complex64: + case Basic_complex128: + case Basic_quaternion64: + case Basic_quaternion128: + case Basic_quaternion256: + return str_lit("?"); + + case Basic_int: + return str_lit(INT_SIZE_ENCODING); + case Basic_uint: + return build_context.metrics.ptr_size == 4 ? str_lit("I") : str_lit("Q"); + case Basic_uintptr: + case Basic_rawptr: + return str_lit("^v"); + + case Basic_string: + return build_context.metrics.ptr_size == 4 ? str_lit("{string=*i}") : str_lit("{string=*q}"); + + case Basic_cstring: return str_lit("*"); + case Basic_any: return str_lit("{any=^v^v"); // rawptr + ^Type_Info + + case Basic_typeid: + GB_ASSERT(t->Basic.size == 8); + return str_lit("q"); + + // Untyped types + case Basic_UntypedBool: + case Basic_UntypedInteger: + case Basic_UntypedFloat: + case Basic_UntypedComplex: + case Basic_UntypedQuaternion: + case Basic_UntypedString: + case Basic_UntypedRune: + case Basic_UntypedNil: + case Basic_UntypedUninit: + GB_PANIC("Untyped types cannot be @encoded()"); + return str_lit("?"); + } + break; + } + + case Type_Named: + case Type_Struct: + case Type_Union: { + Type* base = t; + if (base->kind == Type_Named) { + base = base_type(base); + if(base->kind != Type_Struct && base->kind != Type_Union) { + return lb_get_objc_type_encoding(base, allocator, pointer_depth); + } + } + + const bool is_union = base->kind == Type_Union; + if (!is_union) { + // Check for objc_SEL + if (internal_check_is_assignable_to(base, t_objc_SEL)) { + return str_lit(":"); + } + + // Check for objc_Class + if (internal_check_is_assignable_to(base, t_objc_SEL)) { + return str_lit("#"); + } + + // Treat struct as an Objective-C Class? + if (has_type_got_objc_class_attribute(base) && pointer_depth == 0) { + return str_lit("#"); + } + } + + if (is_type_objc_object(base)) { + return str_lit("@"); + } + + + gbString s = gb_string_make_reserve(allocator, 16); + s = gb_string_append_length(s, is_union ? "(" :"{", 1); + if (t->kind == Type_Named) { + s = gb_string_append_length(s, t->Named.name.text, t->Named.name.len); + } + + // Write fields + if (pointer_depth < 2) { + s = gb_string_append_length(s, "=", 1); + + if (!is_union) { + for( auto& f : base->Struct.fields ) { + String field_type = lb_get_objc_type_encoding(f->type, allocator, pointer_depth); + s = gb_string_append_length(s, field_type.text, field_type.len); + } + } else { + // #TODO(harold): Encode fields + } + } + + s = gb_string_append_length(s, is_union ? ")" :"}", 1); + + return make_string_c(s); + } + + case Type_Generic: + GB_PANIC("Generic types cannot be @encoded()"); + return str_lit("?"); + + case Type_Pointer: { + String pointee = lb_get_objc_type_encoding(t->Pointer.elem, allocator, pointer_depth +1); + // Special case for Objective-C Objects + if (pointer_depth == 0 && pointee == "@") { + return pointee; + } + + return concatenate_strings(allocator, str_lit("^"), pointee); + } + + case Type_MultiPointer: + return concatenate_strings(allocator, str_lit("^"), lb_get_objc_type_encoding(t->Pointer.elem, allocator, pointer_depth +1)); + + case Type_Array: { + String type_str = lb_get_objc_type_encoding(t->Array.elem, allocator, pointer_depth); + + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "[%lld%s]", t->Array.count, type_str.text); + return make_string_c(s); + } + + case Type_EnumeratedArray: { + String type_str = lb_get_objc_type_encoding(t->EnumeratedArray.elem, allocator, pointer_depth); + + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "[%lld%s]", t->EnumeratedArray.count, type_str.text); + return make_string_c(s); + } + + case Type_Slice: { + String type_str = lb_get_objc_type_encoding(t->Slice.elem, allocator, pointer_depth); + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "{slice=^%s%s}", type_str, INT_SIZE_ENCODING); + return make_string_c(s); + } + + case Type_DynamicArray: { + String type_str = lb_get_objc_type_encoding(t->DynamicArray.elem, allocator, pointer_depth); + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "{dynamic=^%s%s%sAllocator={?^v}}", type_str, INT_SIZE_ENCODING, INT_SIZE_ENCODING); + return make_string_c(s); + } + + case Type_Map: + return str_lit("{^v^v{Allocator=?^v}}"); + case Type_Enum: + return lb_get_objc_type_encoding(t->Enum.base_type, allocator, pointer_depth); + case Type_Tuple: + // NOTE(harold): Is this allowed here? + return str_lit("?"); + case Type_Proc: + return str_lit("?"); + case Type_BitSet: + return lb_get_objc_type_encoding(t->BitSet.underlying, allocator, pointer_depth); + case Type_SimdVector: + break; + case Type_Matrix: + break; + case Type_BitField: + return lb_get_objc_type_encoding(t->BitField.backing_type, allocator, pointer_depth); + case Type_SoaPointer: { + gbString s = gb_string_make_reserve(allocator, 8); + s = gb_string_append_fmt(s, "{=^v%s}", INT_SIZE_ENCODING); + return make_string_c(s); + } + + } // End switch t->kind + #undef INT_SIZE_ENCODING + + GB_PANIC("Unreachable"); } struct lbObjCGlobalClass { - lbObjCGlobal g; - lbValue class_value; // Local registered class value + lbObjCGlobal g; + lbValue class_value; // Local registered class value }; gb_internal void lb_register_objc_thing( - StringSet &handled, - lbModule *m, - Array &args, - Array &class_impls, - StringMap &class_map, - lbProcedure *p, - lbObjCGlobal const &g, - char const *call + StringSet &handled, + lbModule *m, + Array &args, + Array &class_impls, + StringMap &class_map, + lbProcedure *p, + lbObjCGlobal const &g, + char const *call ) { - if (string_set_update(&handled, g.name)) { - return; - } - - lbAddr addr = {}; - lbValue *found = string_map_get(&m->members, g.global_name); - if (found) { - addr = lb_addr(*found); - } else { - lbValue v = {}; - LLVMTypeRef t = lb_type(m, g.type); - v.value = LLVMAddGlobal(m->mod, t, g.global_name); - v.type = alloc_type_pointer(g.type); - addr = lb_addr(v); - 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{}; - - 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; - } - - args.count = 3; - args[0] = superclass == nullptr ? lb_const_nil(m, t_objc_Class) : 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); - - array_add(&class_impls, lbObjCGlobalClass{g, class_ptr}); - } - else { - args.count = 1; - args[0] = class_name; - class_ptr = lb_emit_runtime_call(p, call, args); - } - - 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; - } + if (string_set_update(&handled, g.name)) { + return; + } + + lbAddr addr = {}; + lbValue *found = string_map_get(&m->members, g.global_name); + if (found) { + addr = lb_addr(*found); + } else { + lbValue v = {}; + LLVMTypeRef t = lb_type(m, g.type); + v.value = LLVMAddGlobal(m->mod, t, g.global_name); + v.type = alloc_type_pointer(g.type); + addr = lb_addr(v); + 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{}; + + 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; + } + + args.count = 3; + args[0] = superclass == nullptr ? lb_const_nil(m, t_objc_Class) : 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); + + array_add(&class_impls, lbObjCGlobalClass{g, class_ptr}); + } + else { + args.count = 1; + args[0] = class_name; + class_ptr = lb_emit_runtime_call(p, call, args); + } + + 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; + } } gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { @@ -1513,80 +1513,80 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { defer (string_set_destroy(&handled)); auto args = array_make(temporary_allocator(), 3, 8); - auto class_impls = array_make(temporary_allocator(), 0, 16); - - // Ensure classes that have been implicitly referenced through - // the objc_superclass attribute have a global variable available for them. - TypeSet class_set{}; - type_set_init(&class_set, gen->objc_classes.count+16); - defer (type_set_destroy(&class_set)); - - auto referenced_classes = array_make(temporary_allocator()); - for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) { - array_add( &referenced_classes, g); - - Type *cls = g.class_impl_type; - while (cls) { - if (type_set_update(&class_set, cls)) { - break; - } - GB_ASSERT(cls->kind == Type_Named); - - cls = cls->Named.type_name->TypeName.objc_superclass; - } - } - - for (auto pair : class_set) { - auto& tn = pair.type->Named.type_name->TypeName; - Type *class_impl = !tn.objc_is_implementation ? nullptr : pair.type; - lb_handle_objc_find_or_register_class(p, tn.objc_class_name, class_impl); - } - for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) { - array_add( &referenced_classes, g ); - } - - // Add all class globals to a map so that we can look them up dynamically - // in order to resolve out-of-order because classes that are being implemented - // need their superclasses to have been registered before them. - StringMap global_class_map{}; - string_map_init(&global_class_map, (usize)gen->objc_classes.count); - defer (string_map_destroy(&global_class_map)); - - for (lbObjCGlobal g :referenced_classes) { - string_map_set(&global_class_map, g.name, lbObjCGlobalClass{g}); - } - - LLVMSetLinkage(p->value, LLVMInternalLinkage); - lb_begin_procedure_body(p); - - // Register class globals, gathering classes that must be implemented - for (auto& kv : global_class_map) { - lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, kv.value.g, "objc_lookUpClass"); - } - - // Prefetch selectors for implemented methods so that they can also be registered. - for (const auto& cd : class_impls) { - auto& g = cd.g; - Type *class_type = g.class_impl_type; - - Array* methods = map_get(&m->info->objc_method_implementations, class_type); - if (!methods) { - continue; - } - - for (const ObjcMethodData& md : *methods) { - lb_handle_objc_find_or_register_selector(p, md.ac.objc_selector); - } - } - - // Now we can register all referenced selectors - for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_selectors, &g); /**/) { - lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, g, "sel_registerName"); - } - - - // Emit method wrapper implementations and registration - auto wrapper_args = array_make(temporary_allocator(), 2, 8); + auto class_impls = array_make(temporary_allocator(), 0, 16); + + // Ensure classes that have been implicitly referenced through + // the objc_superclass attribute have a global variable available for them. + TypeSet class_set{}; + type_set_init(&class_set, gen->objc_classes.count+16); + defer (type_set_destroy(&class_set)); + + auto referenced_classes = array_make(temporary_allocator()); + for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) { + array_add( &referenced_classes, g); + + Type *cls = g.class_impl_type; + while (cls) { + if (type_set_update(&class_set, cls)) { + break; + } + GB_ASSERT(cls->kind == Type_Named); + + cls = cls->Named.type_name->TypeName.objc_superclass; + } + } + + for (auto pair : class_set) { + auto& tn = pair.type->Named.type_name->TypeName; + Type *class_impl = !tn.objc_is_implementation ? nullptr : pair.type; + lb_handle_objc_find_or_register_class(p, tn.objc_class_name, class_impl); + } + for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) { + array_add( &referenced_classes, g ); + } + + // Add all class globals to a map so that we can look them up dynamically + // in order to resolve out-of-order because classes that are being implemented + // need their superclasses to have been registered before them. + StringMap global_class_map{}; + string_map_init(&global_class_map, (usize)gen->objc_classes.count); + defer (string_map_destroy(&global_class_map)); + + for (lbObjCGlobal g :referenced_classes) { + string_map_set(&global_class_map, g.name, lbObjCGlobalClass{g}); + } + + LLVMSetLinkage(p->value, LLVMInternalLinkage); + lb_begin_procedure_body(p); + + // Register class globals, gathering classes that must be implemented + for (auto& kv : global_class_map) { + lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, kv.value.g, "objc_lookUpClass"); + } + + // Prefetch selectors for implemented methods so that they can also be registered. + for (const auto& cd : class_impls) { + auto& g = cd.g; + Type *class_type = g.class_impl_type; + + Array* methods = map_get(&m->info->objc_method_implementations, class_type); + if (!methods) { + continue; + } + + for (const ObjcMethodData& md : *methods) { + lb_handle_objc_find_or_register_selector(p, md.ac.objc_selector); + } + } + + // Now we can register all referenced selectors + for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_selectors, &g); /**/) { + lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, g, "sel_registerName"); + } + + + // Emit method wrapper implementations and registration + auto wrapper_args = array_make(temporary_allocator(), 2, 8); auto get_context_args = array_make(temporary_allocator(), 1); @@ -1597,186 +1597,200 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { map_set(&ivar_map, g.class_impl_type, g); } - for (const auto& cd : class_impls) { - auto& g = cd.g; - Type *class_type = g.class_impl_type; - Type *class_ptr_type = alloc_type_pointer(class_type); - lbValue class_value = cd.class_value; + for (const auto& cd : class_impls) { + auto& g = cd.g; + Type *class_type = g.class_impl_type; + Type *class_ptr_type = alloc_type_pointer(class_type); + lbValue class_value = cd.class_value; - Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar; + Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar; - Entity *context_provider = class_type->Named.type_name->TypeName.objc_context_provider; - Type *contex_provider_self_ptr_type = nullptr; - Type *contex_provider_self_named_type = nullptr; - bool is_context_provider_ivar = false; - lbValue context_provider_proc_value{}; + Entity *context_provider = class_type->Named.type_name->TypeName.objc_context_provider; + Type *contex_provider_self_ptr_type = nullptr; + Type *contex_provider_self_named_type = nullptr; + bool is_context_provider_ivar = false; + lbValue context_provider_proc_value{}; - if (context_provider) { - context_provider_proc_value = lb_find_procedure_value_from_entity(m, context_provider); + if (context_provider) { + context_provider_proc_value = lb_find_procedure_value_from_entity(m, context_provider); - contex_provider_self_ptr_type = base_type(context_provider->type->Proc.params->Tuple.variables[0]->type); - GB_ASSERT(contex_provider_self_ptr_type->kind == Type_Pointer); - contex_provider_self_named_type = base_named_type(type_deref(contex_provider_self_ptr_type)); + contex_provider_self_ptr_type = base_type(context_provider->type->Proc.params->Tuple.variables[0]->type); + GB_ASSERT(contex_provider_self_ptr_type->kind == Type_Pointer); + contex_provider_self_named_type = base_named_type(type_deref(contex_provider_self_ptr_type)); - is_context_provider_ivar = ivar_type != nullptr && internal_check_is_assignable_to(contex_provider_self_named_type, ivar_type); - } + 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; - } + Array* methods = map_get(&m->info->objc_method_implementations, class_type); + if (!methods) { + continue; + } - for (const ObjcMethodData& md : *methods) { - GB_ASSERT( md.proc_entity->kind == Entity_Procedure); - Type *method_type = md.proc_entity->type; + for (const ObjcMethodData& md : *methods) { + GB_ASSERT( md.proc_entity->kind == Entity_Procedure); + Type *method_type = md.proc_entity->type; - String proc_name = make_string_c("__$objc_method::"); - proc_name = concatenate_strings(temporary_allocator(), proc_name, g.name); - proc_name = concatenate_strings(temporary_allocator(), proc_name, str_lit("::")); - proc_name = concatenate_strings( permanent_allocator(), proc_name, md.ac.objc_name); + String proc_name = make_string_c("__$objc_method::"); + proc_name = concatenate_strings(temporary_allocator(), proc_name, g.name); + proc_name = concatenate_strings(temporary_allocator(), proc_name, str_lit("::")); + proc_name = concatenate_strings( permanent_allocator(), proc_name, md.ac.objc_name); - wrapper_args.count = 2; - wrapper_args[0] = md.ac.objc_is_class_method ? t_objc_Class : class_ptr_type; - wrapper_args[1] = t_objc_SEL; + wrapper_args.count = 2; + wrapper_args[0] = md.ac.objc_is_class_method ? t_objc_Class : class_ptr_type; + wrapper_args[1] = t_objc_SEL; - auto method_param_count = (isize)method_type->Proc.param_count; - i32 method_param_offset = 0; + auto method_param_count = (isize)method_type->Proc.param_count; + i32 method_param_offset = 0; - // TODO(harold): Need to make sure (at checker stage) that the non-class method has the self parameter already. - // (Maybe this is already accounted for?.) - if (!md.ac.objc_is_class_method) { - GB_ASSERT(method_param_count >= 1); - method_param_count -= 1; - method_param_offset = 1; - } + // TODO(harold): Need to make sure (at checker stage) that the non-class method has the self parameter already. + // (Maybe this is already accounted for?.) + if (!md.ac.objc_is_class_method) { + GB_ASSERT(method_param_count >= 1); + method_param_count -= 1; + method_param_offset = 1; + } - for (i32 i = 0; i < method_param_count; i++) { - array_add(&wrapper_args, method_type->Proc.params->Tuple.variables[method_param_offset+i]->type); - } + for (i32 i = 0; i < method_param_count; i++) { + array_add(&wrapper_args, method_type->Proc.params->Tuple.variables[method_param_offset+i]->type); + } - Type *wrapper_args_tuple = alloc_type_tuple_from_field_types(wrapper_args.data, wrapper_args.count, false, true); - Type *wrapper_proc_type = alloc_type_proc(nullptr, wrapper_args_tuple, (isize)wrapper_args_tuple->Tuple.variables.count, nullptr, 0, false, ProcCC_CDecl); + Type *wrapper_args_tuple = alloc_type_tuple_from_field_types(wrapper_args.data, wrapper_args.count, false, true); + Type *wrapper_results_tuple = nullptr; - 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"); + if (method_type->Proc.result_count > 0) { + GB_ASSERT(method_type->Proc.result_count == 1); + wrapper_results_tuple = alloc_type_tuple_from_field_types(&method_type->Proc.results->Tuple.variables[0]->type, 1, false, true); + } + + Type *wrapper_proc_type = alloc_type_proc(nullptr, wrapper_args_tuple, wrapper_args_tuple->Tuple.variables.count, + wrapper_results_tuple, method_type->Proc.result_count, false, ProcCC_CDecl); - // Emit the wrapper - LLVMSetLinkage(wrapper_proc->value, LLVMExternalLinkage); - lb_begin_procedure_body(wrapper_proc); - { - if (method_type->Proc.calling_convention == ProcCC_Odin) { - GB_ASSERT(context_provider); + 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"); - // Emit the get odin context call + // Emit the wrapper + LLVMSetLinkage(wrapper_proc->value, LLVMExternalLinkage); + lb_begin_procedure_body(wrapper_proc); + { + if (method_type->Proc.calling_convention == ProcCC_Odin) { + GB_ASSERT(context_provider); - get_context_args[0] = lbValue { - wrapper_proc->raw_input_parameters[0], + // Emit the get odin context call + + get_context_args[0] = lbValue { + wrapper_proc->raw_input_parameters[0], contex_provider_self_ptr_type, }; - if (is_context_provider_ivar) { - // The context provider takes the ivar's type. - // Emit an obj_ivar_get call and use that pointer for 'self' instead. - lbValue real_self { - wrapper_proc->raw_input_parameters[0], - class_ptr_type - }; - 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); - } - - - auto method_call_args = array_make(temporary_allocator(), method_param_count + (isize)method_param_offset); - - if (!md.ac.objc_is_class_method) { - method_call_args[0] = lbValue { - wrapper_proc->raw_input_parameters[0], - class_ptr_type, - }; - } - - 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, - }; - } - 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. - lb_emit_call(wrapper_proc, method_proc_value, method_call_args); - } - 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, temporary_allocator()); - } - - if (!md.ac.objc_is_class_method) { - method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("@:")); - } else { - method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("#:")); - } - - for (i32 i = method_param_offset; i < method_param_count; i++) { - Type *param_type = method_type->Proc.params->Tuple.variables[i]->type; - String param_encoding = lb_get_objc_type_encoding(param_type, temporary_allocator()); - - method_encoding = concatenate_strings(temporary_allocator(), method_encoding, param_encoding); - } - - // Emit method registration - lbAddr* sel_address = string_map_get(&m->objc_selectors, md.ac.objc_selector); - GB_ASSERT(sel_address); - lbValue selector_value = lb_addr_load(p, *sel_address); - - args.count = 4; - args[0] = class_value; // Class - args[1] = selector_value; // SEL - 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. - lb_emit_runtime_call(p, "class_addMethod", args); - - } // End methods - - // Add ivar if we have one - if (ivar_type != nullptr) { - // Register a single ivar for this class - Type *ivar_base = ivar_type->Named.base; - - const i64 size = type_size_of(ivar_base); - const i64 alignment = type_align_of(ivar_base); - // TODO(harold): Checker: Alignment must be compatible with ivar rules. Or we should increase the alignment if needed. - - // TODO(harold): Should we pass the actual type encoding? Might not be ideal for obfuscation. - String ivar_name = str_lit("__$ivar"); - String ivar_types = str_lit("{= }"); //lb_get_objc_type_encoding(ivar_type, temporary_allocator());// str_lit("{= }"); - args.count = 5; - args[0] = class_value; - args[1] = lb_const_value(m, t_cstring, exact_value_string(ivar_name)); - args[2] = lb_const_value(m, t_uint, exact_value_u64((u64)size)); - args[3] = lb_const_value(m, t_u8, exact_value_u64((u64)alignment)); - args[4] = lb_const_value(m, t_cstring, exact_value_string(ivar_types)); - lb_emit_runtime_call(p, "class_addIvar", args); - } - - // Complete the class registration - args.count = 1; - args[0] = class_value; - lb_emit_runtime_call(p, "objc_registerClassPair", args); - } + if (is_context_provider_ivar) { + // The context provider takes the ivar's type. + // Emit an obj_ivar_get call and use that pointer for 'self' instead. + lbValue real_self { + wrapper_proc->raw_input_parameters[0], + class_ptr_type + }; + 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); + } + + + auto method_call_args = array_make(temporary_allocator(), method_param_count + (isize)method_param_offset); + + if (!md.ac.objc_is_class_method) { + method_call_args[0] = lbValue { + wrapper_proc->raw_input_parameters[0], + class_ptr_type, + }; + } + + 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, + }; + } + 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); + + 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); + } + } + 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, temporary_allocator()); + } + + if (!md.ac.objc_is_class_method) { + method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("@:")); + } else { + method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("#:")); + } + + for (i32 i = method_param_offset; i < method_param_count; i++) { + Type *param_type = method_type->Proc.params->Tuple.variables[i]->type; + String param_encoding = lb_get_objc_type_encoding(param_type, temporary_allocator()); + + method_encoding = concatenate_strings(temporary_allocator(), method_encoding, param_encoding); + } + + // Emit method registration + lbAddr* sel_address = string_map_get(&m->objc_selectors, md.ac.objc_selector); + GB_ASSERT(sel_address); + lbValue selector_value = lb_addr_load(p, *sel_address); + + args.count = 4; + args[0] = class_value; // Class + args[1] = selector_value; // SEL + 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. + lb_emit_runtime_call(p, "class_addMethod", args); + + } // End methods + + // Add ivar if we have one + if (ivar_type != nullptr) { + // Register a single ivar for this class + Type *ivar_base = ivar_type->Named.base; + + const i64 size = type_size_of(ivar_base); + const i64 alignment = type_align_of(ivar_base); + // TODO(harold): Checker: Alignment must be compatible with ivar rules. Or we should increase the alignment if needed. + + // TODO(harold): Should we pass the actual type encoding? Might not be ideal for obfuscation. + String ivar_name = str_lit("__$ivar"); + String ivar_types = str_lit("{= }"); //lb_get_objc_type_encoding(ivar_type, temporary_allocator());// str_lit("{= }"); + args.count = 5; + args[0] = class_value; + args[1] = lb_const_value(m, t_cstring, exact_value_string(ivar_name)); + args[2] = lb_const_value(m, t_uint, exact_value_u64((u64)size)); + args[3] = lb_const_value(m, t_u8, exact_value_u64((u64)alignment)); + args[4] = lb_const_value(m, t_cstring, exact_value_string(ivar_types)); + lb_emit_runtime_call(p, "class_addIvar", args); + } + + // Complete the class registration + args.count = 1; + args[0] = class_value; + lb_emit_runtime_call(p, "objc_registerClassPair", args); + } // Register ivar offsets for any `objc_ivar_get` expressions emitted. Type *ptr_u32 = alloc_type_pointer(t_u32); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 7694c65c3..99ee2b2ff 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -196,7 +196,7 @@ struct lbModule { StringMap objc_classes; StringMap objc_selectors; - StringMap objc_ivars; + StringMap objc_ivars; PtrMap map_cell_info_map; // address of runtime.Map_Info PtrMap map_info_map; // address of runtime.Map_Cell_Info @@ -220,7 +220,7 @@ struct lbObjCGlobal { gbString global_name; String name; Type * type; - Type * class_impl_type; // This is set when the class has the objc_implement attribute set to true. + Type * class_impl_type; // This is set when the class has the objc_implement attribute set to true. }; struct lbGenerator : LinkerData { @@ -242,7 +242,7 @@ struct lbGenerator : LinkerData { MPSCQueue entities_to_correct_linkage; MPSCQueue objc_selectors; MPSCQueue objc_classes; - MPSCQueue objc_ivars; + MPSCQueue objc_ivars; }; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 7f012e006..bb683465b 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -101,7 +101,7 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) { string_map_init(&m->objc_classes); string_map_init(&m->objc_selectors); - string_map_init(&m->objc_ivars); + string_map_init(&m->objc_ivars); map_init(&m->map_info_map, 0); map_init(&m->map_cell_info_map, 0); @@ -174,7 +174,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { mpsc_init(&gen->entities_to_correct_linkage, heap_allocator()); mpsc_init(&gen->objc_selectors, heap_allocator()); mpsc_init(&gen->objc_classes, heap_allocator()); - mpsc_init(&gen->objc_ivars, heap_allocator()); + mpsc_init(&gen->objc_ivars, heap_allocator()); return true; } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index bf4ebf377..ba375283e 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3290,7 +3290,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu case BuiltinProc_objc_find_class: return lb_handle_objc_find_class(p, expr); case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr); 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_ivar_get: return lb_handle_objc_ivar_get(p, expr); case BuiltinProc_constant_utf16_cstring: diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 33211395a..264364162 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2157,8 +2157,8 @@ gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String gb_internal lbAddr lb_handle_objc_find_or_register_ivar(lbModule *m, Type *self_type) { - String name = self_type->Named.type_name->TypeName.objc_class_name; - GB_ASSERT(name != ""); + String name = self_type->Named.type_name->TypeName.objc_class_name; + GB_ASSERT(name != ""); lbAddr *found = string_map_get(&m->objc_ivars, name); if (found) { @@ -2170,7 +2170,7 @@ gb_internal lbAddr lb_handle_objc_find_or_register_ivar(lbModule *m, Type *self_ gbString global_name = gb_string_make(permanent_allocator(), "__$objc_ivar::"); global_name = gb_string_append_length(global_name, name.text, name.len); - // Create a global variable to store offset of the ivar in an instance of an object + // Create a global variable to store offset of the ivar in an instance of an object LLVMTypeRef t = lb_type(m, t_u32); lbValue g = {}; @@ -2209,10 +2209,10 @@ gb_internal lbValue lb_handle_objc_ivar_for_objc_object_pointer(lbProcedure *p, } gb_internal lbValue lb_handle_objc_ivar_get(lbProcedure *p, Ast *expr) { - ast_node(ce, CallExpr, expr); + ast_node(ce, CallExpr, expr); - GB_ASSERT(ce->args[0]->tav.type->kind == Type_Pointer); - lbValue self = lb_build_expr(p, ce->args[0]); + GB_ASSERT(ce->args[0]->tav.type->kind == Type_Pointer); + lbValue self = lb_build_expr(p, ce->args[0]); return lb_handle_objc_ivar_for_objc_object_pointer(p, self); } @@ -2282,7 +2282,7 @@ gb_internal lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) { GB_ASSERT(e->kind == Entity_TypeName); String name = e->TypeName.objc_class_name; - Type *class_impl_type = e->TypeName.objc_is_implementation ? type : nullptr; + Type *class_impl_type = e->TypeName.objc_is_implementation ? type : nullptr; return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name, class_impl_type)); } -- cgit v1.2.3 From ee8aeea38163c18a9b3513717bd09d3765c0d6d8 Mon Sep 17 00:00:00 2001 From: bogwi Date: Mon, 5 May 2025 14:18:11 +0900 Subject: CHECK 1 done Fix panic in LLVM backend when using generic procedure with default arguments - Fixed panic in `llvm_backend_proc.cpp` when using unspecialized polymorphic procedures as defaults. - Ensured correct type inference when generic procedures are used as default parameters. --- src/llvm_backend_const.cpp | 5 ++++- src/llvm_backend_general.cpp | 12 ++++++++++-- src/llvm_backend_proc.cpp | 12 ++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index dada2cff5..51c8a4449 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -533,7 +533,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb Entity *e = entity_from_expr(expr); res = lb_find_procedure_value_from_entity(m, e); } - GB_ASSERT(res.value != nullptr); + if (res.value == nullptr) { + // This is an unspecialized polymorphic procedure, return nil or dummy value + return lb_const_nil(m, original_type); + } GB_ASSERT(LLVMGetValueKind(res.value) == LLVMFunctionValueKind); if (LLVMGetIntrinsicID(res.value) == 0) { diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 421720c4c..41a6fb34a 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -885,8 +885,8 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) { Type *t = base_type(type_deref(addr.addr.type)); GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None); lbValue len = lb_soa_struct_len(p, addr.addr); - if (addr.soa.index_expr != nullptr) { - lb_emit_bounds_check(p, ast_token(addr.soa.index_expr), index, len); + if (addr.soa.index_expr != nullptr && (!lb_is_const(addr.soa.index) || t->Struct.soa_kind != StructSoa_Fixed)) { + lb_emit_bounds_check(p, ast_token(addr.soa.index_expr), addr.soa.index, len); } } @@ -2728,6 +2728,14 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) ignore_body = other_module != m; lbProcedure *missing_proc = lb_create_procedure(m, e, ignore_body); + if (missing_proc == nullptr) { + // This is an unspecialized polymorphic procedure, which should not be codegen'd + lbValue dummy = {}; + dummy.value = nullptr; + dummy.type = nullptr; + return dummy; + } + if (ignore_body) { mutex_lock(&gen->anonymous_proc_lits_mutex); defer (mutex_unlock(&gen->anonymous_proc_lits_mutex)); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index be51f529d..519ab3e9d 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -67,6 +67,14 @@ gb_internal void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValu gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) { GB_ASSERT(entity != nullptr); GB_ASSERT(entity->kind == Entity_Procedure); + // Skip codegen for unspecialized polymorphic procedures + if (is_type_polymorphic(entity->type) && !entity->Procedure.is_foreign) { + Type *bt = base_type(entity->type); + if (bt->kind == Type_Proc && bt->Proc.is_polymorphic && !bt->Proc.is_poly_specialized) { + // Do not generate code for unspecialized polymorphic procedures + return nullptr; + } + } if (!entity->Procedure.is_foreign) { if ((entity->flags & EntityFlag_ProcBodyChecked) == 0) { GB_PANIC("%.*s :: %s (was parapoly: %d %d)", LIT(entity->token.string), type_to_string(entity->type), is_type_polymorphic(entity->type, true), is_type_polymorphic(entity->type, false)); @@ -815,6 +823,10 @@ gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e) e->Procedure.link_name = name; lbProcedure *nested_proc = lb_create_procedure(p->module, e); + if (nested_proc == nullptr) { + // This is an unspecialized polymorphic procedure, skip codegen + return; + } e->code_gen_procedure = nested_proc; lbValue value = {}; -- cgit v1.2.3 From af0e067a12079cc16020e264c6157bb5581c9cf4 Mon Sep 17 00:00:00 2001 From: bogwi Date: Mon, 5 May 2025 15:14:06 +0900 Subject: CHECK 2 done Add support for handling generic types in LLVM backend - Updated `lb_type_internal` to return a pointer type for unspecialized generics. - Modified `write_type_to_canonical_string` to handle specialized generics without panicking. - Enhanced `default_type` to return the default type of specialized generics when applicable. --- src/llvm_backend_general.cpp | 8 ++++++++ src/name_canonicalization.cpp | 6 +++++- src/types.cpp | 4 ++++ 3 files changed, 17 insertions(+), 1 deletion(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 41a6fb34a..4b9b8d45f 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2212,6 +2212,14 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { case Type_BitField: return lb_type_internal(m, type->BitField.backing_type); + + case Type_Generic: + if (type->Generic.specialized) { + return lb_type_internal(m, type->Generic.specialized); + } else { + // For unspecialized generics, use a pointer type as a placeholder + return LLVMPointerType(LLVMInt8TypeInContext(m->ctx), 0); + } } GB_PANIC("Invalid type %s", type_to_string(type)); diff --git a/src/name_canonicalization.cpp b/src/name_canonicalization.cpp index 6aa933e86..0372f5039 100644 --- a/src/name_canonicalization.cpp +++ b/src/name_canonicalization.cpp @@ -756,8 +756,12 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) { type_writer_appendc(w, "/"); write_type_to_canonical_string(w, type->Generic.specialized); } + } else if (type->Generic.specialized) { + // If we have a specialized type, use that instead of panicking + write_type_to_canonical_string(w, type->Generic.specialized); } else { - GB_PANIC("Type_Generic should never be hit"); + // For unspecialized generics, use a generic placeholder string + type_writer_appendc(w, "rawptr"); } return; diff --git a/src/types.cpp b/src/types.cpp index 9c9472a28..cd33f1a0f 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2932,6 +2932,10 @@ gb_internal Type *default_type(Type *type) { case Basic_UntypedString: return t_string; case Basic_UntypedRune: return t_rune; } + } else if (type->kind == Type_Generic) { + if (type->Generic.specialized) { + return default_type(type->Generic.specialized); + } } return type; } -- cgit v1.2.3 From 83bc2d3c4a186d6a8c362eed901acd6bc6363a8d Mon Sep 17 00:00:00 2001 From: Lucas Perlind Date: Wed, 30 Apr 2025 19:21:00 +1000 Subject: Add asan support for various allocators --- base/runtime/default_temp_allocator_arena.odin | 8 +++ base/runtime/heap_allocator_windows.odin | 12 +++- base/runtime/internal.odin | 9 +++ base/sanitizer/address.odin | 84 +++++++++++++++++++++- base/sanitizer/doc.odin | 4 +- core/mem/rollback_stack_allocator.odin | 50 +++++++++----- core/mem/tlsf/tlsf.odin | 2 +- core/mem/tlsf/tlsf_internal.odin | 96 ++++++++++++++------------ core/mem/tracking_allocator.odin | 11 ++- core/mem/virtual/arena.odin | 43 +++++++++--- core/mem/virtual/virtual.odin | 25 ++++--- core/mem/virtual/virtual_platform.odin | 3 + core/mem/virtual/virtual_windows.odin | 12 +++- src/llvm_backend.hpp | 2 + src/llvm_backend_general.cpp | 7 ++ src/llvm_backend_proc.cpp | 24 ++++--- src/llvm_backend_stmt.cpp | 16 +++++ 17 files changed, 308 insertions(+), 100 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/base/runtime/default_temp_allocator_arena.odin b/base/runtime/default_temp_allocator_arena.odin index 5f25dac95..74994344a 100644 --- a/base/runtime/default_temp_allocator_arena.odin +++ b/base/runtime/default_temp_allocator_arena.odin @@ -1,6 +1,7 @@ package runtime import "base:intrinsics" +import "base:sanitizer" DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: uint(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE) @@ -43,6 +44,8 @@ memory_block_alloc :: proc(allocator: Allocator, capacity: uint, alignment: uint block.base = ([^]byte)(uintptr(block) + base_offset) block.capacity = uint(end - uintptr(block.base)) + sanitizer.address_poison(block.base, block.capacity) + // Should be zeroed assert(block.used == 0) assert(block.prev == nil) @@ -52,6 +55,7 @@ memory_block_alloc :: proc(allocator: Allocator, capacity: uint, alignment: uint memory_block_dealloc :: proc(block_to_free: ^Memory_Block, loc := #caller_location) { if block_to_free != nil { allocator := block_to_free.allocator + sanitizer.address_unpoison(block_to_free.base, block_to_free.capacity) mem_free(block_to_free, allocator, loc) } } @@ -83,6 +87,7 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint) return } data = block.base[block.used+alignment_offset:][:min_size] + sanitizer.address_unpoison(block.base[block.used:block.used+size]) block.used += size return } @@ -162,6 +167,7 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) { if arena.curr_block != nil { intrinsics.mem_zero(arena.curr_block.base, arena.curr_block.used) arena.curr_block.used = 0 + sanitizer.address_poison(arena.curr_block.base, arena.curr_block.capacity) } arena.total_used = 0 } @@ -226,6 +232,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, // grow data in-place, adjusting next allocation block.used = uint(new_end) data = block.base[start:new_end] + sanitizer.address_unpoison(data) return } } @@ -299,6 +306,7 @@ arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) { assert(block.used >= temp.used, "out of order use of arena_temp_end", loc) amount_to_zero := block.used-temp.used intrinsics.mem_zero(block.base[temp.used:], amount_to_zero) + sanitizer.address_poison(block.base[temp.used:block.capacity]) block.used = temp.used arena.total_used -= amount_to_zero } diff --git a/base/runtime/heap_allocator_windows.odin b/base/runtime/heap_allocator_windows.odin index e07df7559..04a6f149b 100644 --- a/base/runtime/heap_allocator_windows.odin +++ b/base/runtime/heap_allocator_windows.odin @@ -1,5 +1,7 @@ package runtime +import "../sanitizer" + foreign import kernel32 "system:Kernel32.lib" @(private="file") @@ -16,7 +18,10 @@ foreign kernel32 { _heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr { HEAP_ZERO_MEMORY :: 0x00000008 - return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY if zero_memory else 0, uint(size)) + ptr := HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY if zero_memory else 0, uint(size)) + // NOTE(lucas): asan not guarunteed to unpoison win32 heap out of the box, do it ourselves + sanitizer.address_unpoison(ptr, size) + return ptr } _heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr { if new_size == 0 { @@ -28,7 +33,10 @@ _heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr { } HEAP_ZERO_MEMORY :: 0x00000008 - return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptr, uint(new_size)) + new_ptr := HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptr, uint(new_size)) + // NOTE(lucas): asan not guarunteed to unpoison win32 heap out of the box, do it ourselves + sanitizer.address_unpoison(new_ptr, new_size) + return new_ptr } _heap_free :: proc "contextless" (ptr: rawptr) { if ptr == nil { diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index 59811a525..7c8a8294b 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -1106,3 +1106,12 @@ __read_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uin dst[j>>3] |= the_bit<<(j&7) } } + +@(no_sanitize_address) +__asan_unpoison_memory_region :: #force_inline proc "contextless" (address: rawptr, size: uint) { + foreign { + __asan_unpoison_memory_region :: proc(address: rawptr, size: uint) --- + } + __asan_unpoison_memory_region(address, size) +} + diff --git a/base/sanitizer/address.odin b/base/sanitizer/address.odin index 3924e02bf..edfdfc172 100644 --- a/base/sanitizer/address.odin +++ b/base/sanitizer/address.odin @@ -60,6 +60,7 @@ poison or unpoison memory in the same memory region region simultaneously. When asan is not enabled this procedure does nothing. */ +@(no_sanitize_address) address_poison_slice :: proc "contextless" (region: $T/[]$E) { when ASAN_ENABLED { __asan_poison_memory_region(raw_data(region), size_of(E) * len(region)) @@ -75,6 +76,7 @@ can poison or unpoison memory in the same memory region region simultaneously. When asan is not enabled this procedure does nothing. */ +@(no_sanitize_address) address_unpoison_slice :: proc "contextless" (region: $T/[]$E) { when ASAN_ENABLED { __asan_unpoison_memory_region(raw_data(region), size_of(E) * len(region)) @@ -90,6 +92,7 @@ two threads can poison or unpoison memory in the same memory region region simul When asan is not enabled this procedure does nothing. */ +@(no_sanitize_address) address_poison_ptr :: proc "contextless" (ptr: ^$T) { when ASAN_ENABLED { __asan_poison_memory_region(ptr, size_of(T)) @@ -106,6 +109,7 @@ region simultaneously. When asan is not enabled this procedure does nothing. */ +@(no_sanitize_address) address_unpoison_ptr :: proc "contextless" (ptr: ^$T) { when ASAN_ENABLED { __asan_unpoison_memory_region(ptr, size_of(T)) @@ -121,6 +125,7 @@ poison or unpoison memory in the same memory region region simultaneously. When asan is not enabled this procedure does nothing. */ +@(no_sanitize_address) address_poison_rawptr :: proc "contextless" (ptr: rawptr, len: int) { when ASAN_ENABLED { assert_contextless(len >= 0) @@ -128,6 +133,22 @@ address_poison_rawptr :: proc "contextless" (ptr: rawptr, len: int) { } } +/* +Marks the region covering `[ptr, ptr+len)` as unaddressable + +Code instrumented with `-sanitize:address` is forbidden from accessing any address +within the region. This procedure is not thread-safe because no two threads can +poison or unpoison memory in the same memory region region simultaneously. + +When asan is not enabled this procedure does nothing. +*/ +@(no_sanitize_address) +address_poison_rawptr_uint :: proc "contextless" (ptr: rawptr, len: uint) { + when ASAN_ENABLED { + __asan_poison_memory_region(ptr, len) + } +} + /* Marks the region covering `[ptr, ptr+len)` as addressable @@ -137,6 +158,7 @@ threads can poison or unpoison memory in the same memory region region simultane When asan is not enabled this procedure does nothing. */ +@(no_sanitize_address) address_unpoison_rawptr :: proc "contextless" (ptr: rawptr, len: int) { when ASAN_ENABLED { assert_contextless(len >= 0) @@ -144,16 +166,34 @@ address_unpoison_rawptr :: proc "contextless" (ptr: rawptr, len: int) { } } +/* +Marks the region covering `[ptr, ptr+len)` as addressable + +Code instrumented with `-sanitize:address` is allowed to access any address +within the region again. This procedure is not thread-safe because no two +threads can poison or unpoison memory in the same memory region region simultaneously. + +When asan is not enabled this procedure does nothing. +*/ +@(no_sanitize_address) +address_unpoison_rawptr_uint :: proc "contextless" (ptr: rawptr, len: uint) { + when ASAN_ENABLED { + __asan_unpoison_memory_region(ptr, len) + } +} + address_poison :: proc { address_poison_slice, address_poison_ptr, address_poison_rawptr, + address_poison_rawptr_uint, } address_unpoison :: proc { address_unpoison_slice, address_unpoison_ptr, address_unpoison_rawptr, + address_unpoison_rawptr_uint, } /* @@ -164,6 +204,7 @@ This can be used for logging and/or debugging purposes. When asan is not enabled this procedure does nothing. */ +@(no_sanitize_address) address_set_death_callback :: proc "contextless" (callback: Address_Death_Callback) { when ASAN_ENABLED { __sanitizer_set_death_callback(callback) @@ -178,7 +219,8 @@ in an asan error. When asan is not enabled this procedure returns `nil`. */ -address_region_is_poisoned_slice :: proc "contextless" (region: []$T/$E) -> rawptr { +@(no_sanitize_address) +address_region_is_poisoned_slice :: proc "contextless" (region: $T/[]$E) -> rawptr { when ASAN_ENABLED { return __asan_region_is_poisoned(raw_data(region), size_of(E) * len(region)) } else { @@ -194,6 +236,7 @@ in an asan error. When asan is not enabled this procedure returns `nil`. */ +@(no_sanitize_address) address_region_is_poisoned_ptr :: proc "contextless" (ptr: ^$T) -> rawptr { when ASAN_ENABLED { return __asan_region_is_poisoned(ptr, size_of(T)) @@ -210,6 +253,7 @@ in an asan error. When asan is not enabled this procedure returns `nil`. */ +@(no_sanitize_address) address_region_is_poisoned_rawptr :: proc "contextless" (region: rawptr, len: int) -> rawptr { when ASAN_ENABLED { assert_contextless(len >= 0) @@ -219,10 +263,29 @@ address_region_is_poisoned_rawptr :: proc "contextless" (region: rawptr, len: in } } +/* +Checks if the memory region covered by `[ptr, ptr+len)` is poisoned. + +If it is poisoned this procedure returns the address which would result +in an asan error. + +When asan is not enabled this procedure returns `nil`. +*/ +@(no_sanitize_address) +address_region_is_poisoned_rawptr_uint :: proc "contextless" (region: rawptr, len: uint) -> rawptr { + when ASAN_ENABLED { + return __asan_region_is_poisoned(region, len) + } else { + return nil + } +} + + address_region_is_poisoned :: proc { address_region_is_poisoned_slice, address_region_is_poisoned_ptr, address_region_is_poisoned_rawptr, + address_region_is_poisoned_rawptr_uint, } /* @@ -233,6 +296,7 @@ If it is poisoned this procedure returns `true`, otherwise it returns When asan is not enabled this procedure returns `false`. */ +@(no_sanitize_address) address_is_poisoned :: proc "contextless" (address: rawptr) -> bool { when ASAN_ENABLED { return __asan_address_is_poisoned(address) != 0 @@ -248,6 +312,7 @@ This procedure prints the description out to `stdout`. When asan is not enabled this procedure does nothing. */ +@(no_sanitize_address) address_describe_address :: proc "contextless" (address: rawptr) { when ASAN_ENABLED { __asan_describe_address(address) @@ -260,6 +325,7 @@ Returns `true` if an asan error has occured, otherwise it returns When asan is not enabled this procedure returns `false`. */ +@(no_sanitize_address) address_report_present :: proc "contextless" () -> bool { when ASAN_ENABLED { return __asan_report_present() != 0 @@ -275,6 +341,7 @@ If no asan error has occurd `nil` is returned. When asan is not enabled this procedure returns `nil`. */ +@(no_sanitize_address) address_get_report_pc :: proc "contextless" () -> rawptr { when ASAN_ENABLED { return __asan_get_report_pc() @@ -290,6 +357,7 @@ If no asan error has occurd `nil` is returned. When asan is not enabled this procedure returns `nil`. */ +@(no_sanitize_address) address_get_report_bp :: proc "contextless" () -> rawptr { when ASAN_ENABLED { return __asan_get_report_bp() @@ -305,6 +373,7 @@ If no asan error has occurd `nil` is returned. When asan is not enabled this procedure returns `nil`. */ +@(no_sanitize_address) address_get_report_sp :: proc "contextless" () -> rawptr { when ASAN_ENABLED { return __asan_get_report_sp() @@ -320,6 +389,7 @@ If no asan error has occurd `nil` is returned. When asan is not enabled this procedure returns `nil`. */ +@(no_sanitize_address) address_get_report_address :: proc "contextless" () -> rawptr { when ASAN_ENABLED { return __asan_get_report_address() @@ -335,6 +405,7 @@ If no asan error has occurd `.none` is returned. When asan is not enabled this procedure returns `.none`. */ +@(no_sanitize_address) address_get_report_access_type :: proc "contextless" () -> Address_Access_Type { when ASAN_ENABLED { if ! address_report_present() { @@ -353,6 +424,7 @@ If no asan error has occurd `0` is returned. When asan is not enabled this procedure returns `0`. */ +@(no_sanitize_address) address_get_report_access_size :: proc "contextless" () -> uint { when ASAN_ENABLED { return __asan_get_report_access_size() @@ -368,6 +440,7 @@ If no asan error has occurd an empty string is returned. When asan is not enabled this procedure returns an empty string. */ +@(no_sanitize_address) address_get_report_description :: proc "contextless" () -> string { when ASAN_ENABLED { return string(__asan_get_report_description()) @@ -386,6 +459,7 @@ The information provided include: When asan is not enabled this procedure returns zero initialised values. */ +@(no_sanitize_address) address_locate_address :: proc "contextless" (addr: rawptr, data: []byte) -> Address_Located_Address { when ASAN_ENABLED { out_addr: rawptr @@ -404,6 +478,7 @@ The stack trace is filled into the `data` slice. When asan is not enabled this procedure returns a zero initialised value. */ +@(no_sanitize_address) address_get_alloc_stack_trace :: proc "contextless" (addr: rawptr, data: []rawptr) -> ([]rawptr, int) { when ASAN_ENABLED { out_thread: i32 @@ -421,6 +496,7 @@ The stack trace is filled into the `data` slice. When asan is not enabled this procedure returns zero initialised values. */ +@(no_sanitize_address) address_get_free_stack_trace :: proc "contextless" (addr: rawptr, data: []rawptr) -> ([]rawptr, int) { when ASAN_ENABLED { out_thread: i32 @@ -436,6 +512,7 @@ Returns the current asan shadow memory mapping. When asan is not enabled this procedure returns a zero initialised value. */ +@(no_sanitize_address) address_get_shadow_mapping :: proc "contextless" () -> Address_Shadow_Mapping { when ASAN_ENABLED { result: Address_Shadow_Mapping @@ -451,6 +528,7 @@ Prints asan statistics to `stderr` When asan is not enabled this procedure does nothing. */ +@(no_sanitize_address) address_print_accumulated_stats :: proc "contextless" () { when ASAN_ENABLED { __asan_print_accumulated_stats() @@ -464,6 +542,7 @@ This pointer can be then used for `address_is_in_fake_stack`. When asan is not enabled this procedure returns `nil`. */ +@(no_sanitize_address) address_get_current_fake_stack :: proc "contextless" () -> rawptr { when ASAN_ENABLED { return __asan_get_current_fake_stack() @@ -477,6 +556,7 @@ Returns if an address belongs to a given fake stack and if so the region of the When asan is not enabled this procedure returns zero initialised values. */ +@(no_sanitize_address) address_is_in_fake_stack :: proc "contextless" (fake_stack: rawptr, addr: rawptr) -> ([]byte, bool) { when ASAN_ENABLED { begin: rawptr @@ -496,6 +576,7 @@ i.e. a procedure such as `panic` and `os.exit`. When asan is not enabled this procedure does nothing. */ +@(no_sanitize_address) address_handle_no_return :: proc "contextless" () { when ASAN_ENABLED { __asan_handle_no_return() @@ -509,6 +590,7 @@ Returns `true` if successful, otherwise it returns `false`. When asan is not enabled this procedure returns `false`. */ +@(no_sanitize_address) address_update_allocation_context :: proc "contextless" (addr: rawptr) -> bool { when ASAN_ENABLED { return __asan_update_allocation_context(addr) != 0 diff --git a/base/sanitizer/doc.odin b/base/sanitizer/doc.odin index e389842b1..707f41ce0 100644 --- a/base/sanitizer/doc.odin +++ b/base/sanitizer/doc.odin @@ -14,12 +14,14 @@ related bugs. Typically asan interacts with libc but Odin code can be marked up with the asan runtime to extend the memory error detection outside of libc using this package. For more information about asan see: https://clang.llvm.org/docs/AddressSanitizer.html +Procedures can be made exempt from asan when marked up with @(no_sanitize_address) + ## Memory Enabled with `-sanitize:memory` when building an odin project. The memory sanitizer is another runtime memory error detector with the sole purpose to catch the -use of uninitialized memory. This is not a very common bug in Odin as be default everything is +use of uninitialized memory. This is not a very common bug in Odin as by default everything is set to zero when initialised (ZII). For more information about the memory sanitizer see: https://clang.llvm.org/docs/MemorySanitizer.html diff --git a/core/mem/rollback_stack_allocator.odin b/core/mem/rollback_stack_allocator.odin index 43ef10fe9..a00131b7f 100644 --- a/core/mem/rollback_stack_allocator.odin +++ b/core/mem/rollback_stack_allocator.odin @@ -1,6 +1,7 @@ package mem import "base:runtime" +import "base:sanitizer" /* Rollback stack default block size. @@ -47,14 +48,14 @@ Rollback_Stack :: struct { block_allocator: Allocator, } -@(private="file", require_results) +@(private="file", require_results, no_sanitize_address) rb_ptr_in_bounds :: proc(block: ^Rollback_Stack_Block, ptr: rawptr) -> bool { start := raw_data(block.buffer) end := start[block.offset:] return start < ptr && ptr <= end } -@(private="file", require_results) +@(private="file", require_results, no_sanitize_address) rb_find_ptr :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> ( parent: ^Rollback_Stack_Block, block: ^Rollback_Stack_Block, @@ -71,7 +72,7 @@ rb_find_ptr :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> ( return nil, nil, nil, .Invalid_Pointer } -@(private="file", require_results) +@(private="file", require_results, no_sanitize_address) rb_find_last_alloc :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> ( block: ^Rollback_Stack_Block, header: ^Rollback_Stack_Header, @@ -86,9 +87,10 @@ rb_find_last_alloc :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> ( return nil, nil, false } -@(private="file") +@(private="file", no_sanitize_address) rb_rollback_block :: proc(block: ^Rollback_Stack_Block, header: ^Rollback_Stack_Header) { header := header + for block.offset > 0 && header.is_free { block.offset = header.prev_offset block.last_alloc = raw_data(block.buffer)[header.prev_ptr:] @@ -99,9 +101,10 @@ rb_rollback_block :: proc(block: ^Rollback_Stack_Block, header: ^Rollback_Stack_ /* Free memory to a rollback stack allocator. */ -@(private="file", require_results) +@(private="file", require_results, no_sanitize_address) rb_free :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> Allocator_Error { parent, block, header := rb_find_ptr(stack, ptr) or_return + if header.is_free { return .Invalid_Pointer } @@ -120,7 +123,7 @@ rb_free :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> Allocator_Error { /* Free all memory owned by the rollback stack allocator. */ -@(private="file") +@(private="file", no_sanitize_address) rb_free_all :: proc(stack: ^Rollback_Stack) { for block := stack.head.next_block; block != nil; /**/ { next_block := block.next_block @@ -131,12 +134,13 @@ rb_free_all :: proc(stack: ^Rollback_Stack) { stack.head.next_block = nil stack.head.last_alloc = nil stack.head.offset = 0 + sanitizer.address_poison(stack.head.buffer) } /* Allocate memory using the rollback stack allocator. */ -@(require_results) +@(require_results, no_sanitize_address) rb_alloc :: proc( stack: ^Rollback_Stack, size: int, @@ -153,7 +157,7 @@ rb_alloc :: proc( /* Allocate memory using the rollback stack allocator. */ -@(require_results) +@(require_results, no_sanitize_address) rb_alloc_bytes :: proc( stack: ^Rollback_Stack, size: int, @@ -170,7 +174,7 @@ rb_alloc_bytes :: proc( /* Allocate non-initialized memory using the rollback stack allocator. */ -@(require_results) +@(require_results, no_sanitize_address) rb_alloc_non_zeroed :: proc( stack: ^Rollback_Stack, size: int, @@ -184,7 +188,7 @@ rb_alloc_non_zeroed :: proc( /* Allocate non-initialized memory using the rollback stack allocator. */ -@(require_results) +@(require_results, no_sanitize_address) rb_alloc_bytes_non_zeroed :: proc( stack: ^Rollback_Stack, size: int, @@ -194,6 +198,7 @@ rb_alloc_bytes_non_zeroed :: proc( assert(size >= 0, "Size must be positive or zero.", loc) assert(is_power_of_two(cast(uintptr)alignment), "Alignment must be a power of two.", loc) parent: ^Rollback_Stack_Block + for block := stack.head; /**/; block = block.next_block { when !ODIN_DISABLE_ASSERT { allocated_new_block: bool @@ -235,7 +240,9 @@ rb_alloc_bytes_non_zeroed :: proc( // Prevent any further allocations on it. block.offset = cast(uintptr)len(block.buffer) } - #no_bounds_check return ptr[:size], nil + res := ptr[:size] + sanitizer.address_unpoison(res) + return res, nil } return nil, .Out_Of_Memory } @@ -243,7 +250,7 @@ rb_alloc_bytes_non_zeroed :: proc( /* Resize an allocation owned by rollback stack allocator. */ -@(require_results) +@(require_results, no_sanitize_address) rb_resize :: proc( stack: ^Rollback_Stack, old_ptr: rawptr, @@ -266,7 +273,7 @@ rb_resize :: proc( /* Resize an allocation owned by rollback stack allocator. */ -@(require_results) +@(require_results, no_sanitize_address) rb_resize_bytes :: proc( stack: ^Rollback_Stack, old_memory: []byte, @@ -289,7 +296,7 @@ rb_resize_bytes :: proc( Resize an allocation owned by rollback stack allocator without explicit zero-initialization. */ -@(require_results) +@(require_results, no_sanitize_address) rb_resize_non_zeroed :: proc( stack: ^Rollback_Stack, old_ptr: rawptr, @@ -306,7 +313,7 @@ rb_resize_non_zeroed :: proc( Resize an allocation owned by rollback stack allocator without explicit zero-initialization. */ -@(require_results) +@(require_results, no_sanitize_address) rb_resize_bytes_non_zeroed :: proc( stack: ^Rollback_Stack, old_memory: []byte, @@ -330,7 +337,9 @@ rb_resize_bytes_non_zeroed :: proc( if len(block.buffer) <= stack.block_size { block.offset += cast(uintptr)size - cast(uintptr)old_size } - #no_bounds_check return (ptr)[:size], nil + res := (ptr)[:size] + sanitizer.address_unpoison(res) + #no_bounds_check return res, nil } } } @@ -340,7 +349,7 @@ rb_resize_bytes_non_zeroed :: proc( return } -@(private="file", require_results) +@(private="file", require_results, no_sanitize_address) rb_make_block :: proc(size: int, allocator: Allocator) -> (block: ^Rollback_Stack_Block, err: Allocator_Error) { buffer := runtime.mem_alloc(size_of(Rollback_Stack_Block) + size, align_of(Rollback_Stack_Block), allocator) or_return block = cast(^Rollback_Stack_Block)raw_data(buffer) @@ -351,6 +360,7 @@ rb_make_block :: proc(size: int, allocator: Allocator) -> (block: ^Rollback_Stac /* Initialize the rollback stack allocator using a fixed backing buffer. */ +@(no_sanitize_address) rollback_stack_init_buffered :: proc(stack: ^Rollback_Stack, buffer: []byte, location := #caller_location) { MIN_SIZE :: size_of(Rollback_Stack_Block) + size_of(Rollback_Stack_Header) + size_of(rawptr) assert(len(buffer) >= MIN_SIZE, "User-provided buffer to Rollback Stack Allocator is too small.", location) @@ -365,6 +375,7 @@ rollback_stack_init_buffered :: proc(stack: ^Rollback_Stack, buffer: []byte, loc /* Initialize the rollback stack alocator using a backing block allocator. */ +@(no_sanitize_address) rollback_stack_init_dynamic :: proc( stack: ^Rollback_Stack, block_size : int = ROLLBACK_STACK_DEFAULT_BLOCK_SIZE, @@ -396,6 +407,7 @@ rollback_stack_init :: proc { /* Destroy a rollback stack. */ +@(no_sanitize_address) rollback_stack_destroy :: proc(stack: ^Rollback_Stack) { if stack.block_allocator.procedure != nil { rb_free_all(stack) @@ -435,7 +447,7 @@ from the last allocation backwards. Each allocation has an overhead of 8 bytes and any extra bytes to satisfy the requested alignment. */ -@(require_results) +@(require_results, no_sanitize_address) rollback_stack_allocator :: proc(stack: ^Rollback_Stack) -> Allocator { return Allocator { data = stack, @@ -443,7 +455,7 @@ rollback_stack_allocator :: proc(stack: ^Rollback_Stack) -> Allocator { } } -@(require_results) +@(require_results, no_sanitize_address) rollback_stack_allocator_proc :: proc( allocator_data: rawptr, mode: Allocator_Mode, diff --git a/core/mem/tlsf/tlsf.odin b/core/mem/tlsf/tlsf.odin index 4ce6e54d9..0ae8c28e0 100644 --- a/core/mem/tlsf/tlsf.odin +++ b/core/mem/tlsf/tlsf.odin @@ -198,4 +198,4 @@ fls :: proc "contextless" (word: u32) -> (bit: i32) { fls_uint :: proc "contextless" (size: uint) -> (bit: i32) { N :: (size_of(uint) * 8) - 1 return i32(N - intrinsics.count_leading_zeros(size)) -} \ No newline at end of file +} diff --git a/core/mem/tlsf/tlsf_internal.odin b/core/mem/tlsf/tlsf_internal.odin index f8a9bf60c..89b875679 100644 --- a/core/mem/tlsf/tlsf_internal.odin +++ b/core/mem/tlsf/tlsf_internal.odin @@ -10,6 +10,7 @@ package mem_tlsf import "base:intrinsics" +import "base:sanitizer" import "base:runtime" // log2 of number of linear subdivisions of block sizes. @@ -209,6 +210,8 @@ alloc_bytes_non_zeroed :: proc(control: ^Allocator, size: uint, align: uint) -> return nil, .Out_Of_Memory } + sanitizer.address_poison(new_pool_buf) + // Allocate a new link in the `control.pool` tracking structure. new_pool := new_clone(Pool{ data = new_pool_buf, @@ -254,7 +257,7 @@ alloc_bytes_non_zeroed :: proc(control: ^Allocator, size: uint, align: uint) -> return block_prepare_used(control, block, adjust) } -@(private, require_results) +@(private, require_results, no_sanitize_address) alloc_bytes :: proc(control: ^Allocator, size: uint, align: uint) -> (res: []byte, err: runtime.Allocator_Error) { res, err = alloc_bytes_non_zeroed(control, size, align) if err == nil { @@ -273,6 +276,7 @@ free_with_size :: proc(control: ^Allocator, ptr: rawptr, size: uint) { block := block_from_ptr(ptr) assert(!block_is_free(block), "block already marked as free") // double free + sanitizer.address_poison(ptr, block.size) block_mark_as_free(block) block = block_merge_prev(control, block) block = block_merge_next(control, block) @@ -316,6 +320,7 @@ resize :: proc(control: ^Allocator, ptr: rawptr, old_size, new_size: uint, align block_trim_used(control, block, adjust) res = ([^]byte)(ptr)[:new_size] + sanitizer.address_unpoison(res) if min_size < new_size { to_zero := ([^]byte)(ptr)[min_size:new_size] @@ -374,95 +379,96 @@ resize_non_zeroed :: proc(control: ^Allocator, ptr: rawptr, old_size, new_size: NOTE: TLSF spec relies on ffs/fls returning a value in the range 0..31. */ -@(private, require_results) +@(private, require_results, no_sanitize_address) block_size :: proc "contextless" (block: ^Block_Header) -> (size: uint) { return block.size &~ (BLOCK_HEADER_FREE | BLOCK_HEADER_PREV_FREE) } -@(private) +@(private, no_sanitize_address) block_set_size :: proc "contextless" (block: ^Block_Header, size: uint) { old_size := block.size block.size = size | (old_size & (BLOCK_HEADER_FREE | BLOCK_HEADER_PREV_FREE)) } -@(private, require_results) +@(private, require_results, no_sanitize_address) block_is_last :: proc "contextless" (block: ^Block_Header) -> (is_last: bool) { return block_size(block) == 0 } -@(private, require_results) +@(private, require_results, no_sanitize_address) block_is_free :: proc "contextless" (block: ^Block_Header) -> (is_free: bool) { return (block.size & BLOCK_HEADER_FREE) == BLOCK_HEADER_FREE } -@(private) +@(private, no_sanitize_address) block_set_free :: proc "contextless" (block: ^Block_Header) { block.size |= BLOCK_HEADER_FREE } -@(private) +@(private, no_sanitize_address) block_set_used :: proc "contextless" (block: ^Block_Header) { block.size &~= BLOCK_HEADER_FREE } -@(private, require_results) +@(private, require_results, no_sanitize_address) block_is_prev_free :: proc "contextless" (block: ^Block_Header) -> (is_prev_free: bool) { return (block.size & BLOCK_HEADER_PREV_FREE) == BLOCK_HEADER_PREV_FREE } -@(private) +@(private, no_sanitize_address) block_set_prev_free :: proc "contextless" (block: ^Block_Header) { block.size |= BLOCK_HEADER_PREV_FREE } -@(private) +@(private, no_sanitize_address) block_set_prev_used :: proc "contextless" (block: ^Block_Header) { block.size &~= BLOCK_HEADER_PREV_FREE } -@(private, require_results) +@(private, require_results, no_sanitize_address) block_from_ptr :: proc(ptr: rawptr) -> (block_ptr: ^Block_Header) { return (^Block_Header)(uintptr(ptr) - BLOCK_START_OFFSET) } -@(private, require_results) +@(private, require_results, no_sanitize_address) block_to_ptr :: proc(block: ^Block_Header) -> (ptr: rawptr) { return rawptr(uintptr(block) + BLOCK_START_OFFSET) } // Return location of next block after block of given size. -@(private, require_results) +@(private, require_results, no_sanitize_address) offset_to_block :: proc(ptr: rawptr, size: uint) -> (block: ^Block_Header) { return (^Block_Header)(uintptr(ptr) + uintptr(size)) } -@(private, require_results) +@(private, require_results, no_sanitize_address) offset_to_block_backwards :: proc(ptr: rawptr, size: uint) -> (block: ^Block_Header) { return (^Block_Header)(uintptr(ptr) - uintptr(size)) } // Return location of previous block. -@(private, require_results) +@(private, require_results, no_sanitize_address) block_prev :: proc(block: ^Block_Header) -> (prev: ^Block_Header) { assert(block_is_prev_free(block), "previous block must be free") + return block.prev_phys_block } // Return location of next existing block. -@(private, require_results) +@(private, require_results, no_sanitize_address) block_next :: proc(block: ^Block_Header) -> (next: ^Block_Header) { return offset_to_block(block_to_ptr(block), block_size(block) - BLOCK_HEADER_OVERHEAD) } // Link a new block with its physical neighbor, return the neighbor. -@(private, require_results) +@(private, require_results, no_sanitize_address) block_link_next :: proc(block: ^Block_Header) -> (next: ^Block_Header) { next = block_next(block) next.prev_phys_block = block return } -@(private) +@(private, no_sanitize_address) block_mark_as_free :: proc(block: ^Block_Header) { // Link the block to the next block, first. next := block_link_next(block) @@ -470,26 +476,26 @@ block_mark_as_free :: proc(block: ^Block_Header) { block_set_free(block) } -@(private) -block_mark_as_used :: proc(block: ^Block_Header) { +@(private, no_sanitize_address) +block_mark_as_used :: proc(block: ^Block_Header, ) { next := block_next(block) block_set_prev_used(next) block_set_used(block) } -@(private, require_results) +@(private, require_results, no_sanitize_address) align_up :: proc(x, align: uint) -> (aligned: uint) { assert(0 == (align & (align - 1)), "must align to a power of two") return (x + (align - 1)) &~ (align - 1) } -@(private, require_results) +@(private, require_results, no_sanitize_address) align_down :: proc(x, align: uint) -> (aligned: uint) { assert(0 == (align & (align - 1)), "must align to a power of two") return x - (x & (align - 1)) } -@(private, require_results) +@(private, require_results, no_sanitize_address) align_ptr :: proc(ptr: rawptr, align: uint) -> (aligned: rawptr) { assert(0 == (align & (align - 1)), "must align to a power of two") align_mask := uintptr(align) - 1 @@ -499,7 +505,7 @@ align_ptr :: proc(ptr: rawptr, align: uint) -> (aligned: rawptr) { } // Adjust an allocation size to be aligned to word size, and no smaller than internal minimum. -@(private, require_results) +@(private, require_results, no_sanitize_address) adjust_request_size :: proc(size, align: uint) -> (adjusted: uint) { if size == 0 { return 0 @@ -513,7 +519,7 @@ adjust_request_size :: proc(size, align: uint) -> (adjusted: uint) { } // Adjust an allocation size to be aligned to word size, and no smaller than internal minimum. -@(private, require_results) +@(private, require_results, no_sanitize_address) adjust_request_size_with_err :: proc(size, align: uint) -> (adjusted: uint, err: runtime.Allocator_Error) { if size == 0 { return 0, nil @@ -531,7 +537,7 @@ adjust_request_size_with_err :: proc(size, align: uint) -> (adjusted: uint, err: // TLSF utility functions. In most cases these are direct translations of // the documentation in the research paper. -@(optimization_mode="favor_size", private, require_results) +@(optimization_mode="favor_size", private, require_results, no_sanitize_address) mapping_insert :: proc(size: uint) -> (fl, sl: i32) { if size < SMALL_BLOCK_SIZE { // Store small blocks in first list. @@ -544,7 +550,7 @@ mapping_insert :: proc(size: uint) -> (fl, sl: i32) { return } -@(optimization_mode="favor_size", private, require_results) +@(optimization_mode="favor_size", private, require_results, no_sanitize_address) mapping_round :: #force_inline proc(size: uint) -> (rounded: uint) { rounded = size if size >= SMALL_BLOCK_SIZE { @@ -555,12 +561,12 @@ mapping_round :: #force_inline proc(size: uint) -> (rounded: uint) { } // This version rounds up to the next block size (for allocations) -@(optimization_mode="favor_size", private, require_results) +@(optimization_mode="favor_size", private, require_results, no_sanitize_address) mapping_search :: proc(size: uint) -> (fl, sl: i32) { return mapping_insert(mapping_round(size)) } -@(private, require_results) +@(private, require_results, no_sanitize_address) search_suitable_block :: proc(control: ^Allocator, fli, sli: ^i32) -> (block: ^Block_Header) { // First, search for a block in the list associated with the given fl/sl index. fl := fli^; sl := sli^ @@ -587,7 +593,7 @@ search_suitable_block :: proc(control: ^Allocator, fli, sli: ^i32) -> (block: ^B } // Remove a free block from the free list. -@(private) +@(private, no_sanitize_address) remove_free_block :: proc(control: ^Allocator, block: ^Block_Header, fl: i32, sl: i32) { prev := block.prev_free next := block.next_free @@ -613,7 +619,7 @@ remove_free_block :: proc(control: ^Allocator, block: ^Block_Header, fl: i32, sl } // Insert a free block into the free block list. -@(private) +@(private, no_sanitize_address) insert_free_block :: proc(control: ^Allocator, block: ^Block_Header, fl: i32, sl: i32) { current := control.blocks[fl][sl] assert(current != nil, "free lists cannot have a nil entry") @@ -631,26 +637,26 @@ insert_free_block :: proc(control: ^Allocator, block: ^Block_Header, fl: i32, sl } // Remove a given block from the free list. -@(private) +@(private, no_sanitize_address) block_remove :: proc(control: ^Allocator, block: ^Block_Header) { fl, sl := mapping_insert(block_size(block)) remove_free_block(control, block, fl, sl) } // Insert a given block into the free list. -@(private) +@(private, no_sanitize_address) block_insert :: proc(control: ^Allocator, block: ^Block_Header) { fl, sl := mapping_insert(block_size(block)) insert_free_block(control, block, fl, sl) } -@(private, require_results) +@(private, require_results, no_sanitize_address) block_can_split :: proc(block: ^Block_Header, size: uint) -> (can_split: bool) { return block_size(block) >= size_of(Block_Header) + size } // Split a block into two, the second of which is free. -@(private, require_results) +@(private, require_results, no_sanitize_address) block_split :: proc(block: ^Block_Header, size: uint) -> (remaining: ^Block_Header) { // Calculate the amount of space left in the remaining block. remaining = offset_to_block(block_to_ptr(block), size - BLOCK_HEADER_OVERHEAD) @@ -671,9 +677,10 @@ block_split :: proc(block: ^Block_Header, size: uint) -> (remaining: ^Block_Head } // Absorb a free block's storage into an adjacent previous free block. -@(private, require_results) +@(private, require_results, no_sanitize_address) block_absorb :: proc(prev: ^Block_Header, block: ^Block_Header) -> (absorbed: ^Block_Header) { assert(!block_is_last(prev), "previous block can't be last") + // Note: Leaves flags untouched. prev.size += block_size(block) + BLOCK_HEADER_OVERHEAD _ = block_link_next(prev) @@ -681,7 +688,7 @@ block_absorb :: proc(prev: ^Block_Header, block: ^Block_Header) -> (absorbed: ^B } // Merge a just-freed block with an adjacent previous free block. -@(private, require_results) +@(private, require_results, no_sanitize_address) block_merge_prev :: proc(control: ^Allocator, block: ^Block_Header) -> (merged: ^Block_Header) { merged = block if (block_is_prev_free(block)) { @@ -695,7 +702,7 @@ block_merge_prev :: proc(control: ^Allocator, block: ^Block_Header) -> (merged: } // Merge a just-freed block with an adjacent free block. -@(private, require_results) +@(private, require_results, no_sanitize_address) block_merge_next :: proc(control: ^Allocator, block: ^Block_Header) -> (merged: ^Block_Header) { merged = block next := block_next(block) @@ -710,7 +717,7 @@ block_merge_next :: proc(control: ^Allocator, block: ^Block_Header) -> (merged: } // Trim any trailing block space off the end of a free block, return to pool. -@(private) +@(private, no_sanitize_address) block_trim_free :: proc(control: ^Allocator, block: ^Block_Header, size: uint) { assert(block_is_free(block), "block must be free") if (block_can_split(block, size)) { @@ -722,7 +729,7 @@ block_trim_free :: proc(control: ^Allocator, block: ^Block_Header, size: uint) { } // Trim any trailing block space off the end of a used block, return to pool. -@(private) +@(private, no_sanitize_address) block_trim_used :: proc(control: ^Allocator, block: ^Block_Header, size: uint) { assert(!block_is_free(block), "Block must be used") if (block_can_split(block, size)) { @@ -736,7 +743,7 @@ block_trim_used :: proc(control: ^Allocator, block: ^Block_Header, size: uint) { } // Trim leading block space, return to pool. -@(private, require_results) +@(private, require_results, no_sanitize_address) block_trim_free_leading :: proc(control: ^Allocator, block: ^Block_Header, size: uint) -> (remaining: ^Block_Header) { remaining = block if block_can_split(block, size) { @@ -750,7 +757,7 @@ block_trim_free_leading :: proc(control: ^Allocator, block: ^Block_Header, size: return remaining } -@(private, require_results) +@(private, require_results, no_sanitize_address) block_locate_free :: proc(control: ^Allocator, size: uint) -> (block: ^Block_Header) { fl, sl: i32 if size != 0 { @@ -774,13 +781,14 @@ block_locate_free :: proc(control: ^Allocator, size: uint) -> (block: ^Block_Hea return block } -@(private, require_results) +@(private, require_results, no_sanitize_address) block_prepare_used :: proc(control: ^Allocator, block: ^Block_Header, size: uint) -> (res: []byte, err: runtime.Allocator_Error) { if block != nil { assert(size != 0, "Size must be non-zero") block_trim_free(control, block, size) block_mark_as_used(block) res = ([^]byte)(block_to_ptr(block))[:size] + sanitizer.address_unpoison(res) } return -} \ No newline at end of file +} diff --git a/core/mem/tracking_allocator.odin b/core/mem/tracking_allocator.odin index 25c547471..01080075e 100644 --- a/core/mem/tracking_allocator.odin +++ b/core/mem/tracking_allocator.odin @@ -64,6 +64,7 @@ This procedure initializes the tracking allocator `t` with a backing allocator specified with `backing_allocator`. The `internals_allocator` will used to allocate the tracked data. */ +@(no_sanitize_address) tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) { t.backing = backing_allocator t.allocation_map.allocator = internals_allocator @@ -77,6 +78,7 @@ tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Alloc /* Destroy the tracking allocator. */ +@(no_sanitize_address) tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) { delete(t.allocation_map) delete(t.bad_free_array) @@ -90,6 +92,7 @@ This procedure clears the tracked data from a tracking allocator. **Note**: This procedure clears only the current allocation data while keeping the totals intact. */ +@(no_sanitize_address) tracking_allocator_clear :: proc(t: ^Tracking_Allocator) { sync.mutex_lock(&t.mutex) clear(&t.allocation_map) @@ -103,6 +106,7 @@ Reset the tracking allocator. Reset all of a Tracking Allocator's allocation data back to zero. */ +@(no_sanitize_address) tracking_allocator_reset :: proc(t: ^Tracking_Allocator) { sync.mutex_lock(&t.mutex) clear(&t.allocation_map) @@ -124,6 +128,7 @@ Override Tracking_Allocator.bad_free_callback to have something else happen. For example, you can use tracking_allocator_bad_free_callback_add_to_array to return the tracking allocator to the old behavior, where the bad_free_array was used. */ +@(no_sanitize_address) tracking_allocator_bad_free_callback_panic :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location) { runtime.print_caller_location(location) runtime.print_string(" Tracking allocator error: Bad free of pointer ") @@ -136,6 +141,7 @@ tracking_allocator_bad_free_callback_panic :: proc(t: ^Tracking_Allocator, memor Alternative behavior for a bad free: Store in `bad_free_array`. If you use this, then you must make sure to check Tracking_Allocator.bad_free_array at some point. */ +@(no_sanitize_address) tracking_allocator_bad_free_callback_add_to_array :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location) { append(&t.bad_free_array, Tracking_Allocator_Bad_Free_Entry { memory = memory, @@ -175,7 +181,7 @@ Example: } } */ -@(require_results) +@(require_results, no_sanitize_address) tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator { return Allocator{ data = data, @@ -183,6 +189,7 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator { } } +@(no_sanitize_address) tracking_allocator_proc :: proc( allocator_data: rawptr, mode: Allocator_Mode, @@ -191,6 +198,7 @@ tracking_allocator_proc :: proc( old_size: int, loc := #caller_location, ) -> (result: []byte, err: Allocator_Error) { + @(no_sanitize_address) track_alloc :: proc(data: ^Tracking_Allocator, entry: ^Tracking_Allocator_Entry) { data.total_memory_allocated += i64(entry.size) data.total_allocation_count += 1 @@ -200,6 +208,7 @@ tracking_allocator_proc :: proc( } } + @(no_sanitize_address) track_free :: proc(data: ^Tracking_Allocator, entry: ^Tracking_Allocator_Entry) { data.total_memory_freed += i64(entry.size) data.total_free_count += 1 diff --git a/core/mem/virtual/arena.odin b/core/mem/virtual/arena.odin index 4fc2e0e35..4e1cc2466 100644 --- a/core/mem/virtual/arena.odin +++ b/core/mem/virtual/arena.odin @@ -3,6 +3,8 @@ package mem_virtual import "core:mem" import "core:sync" +import "base:sanitizer" + Arena_Kind :: enum uint { Growing = 0, // Chained memory blocks (singly linked list). Static = 1, // Fixed reservation sized. @@ -43,7 +45,7 @@ DEFAULT_ARENA_STATIC_RESERVE_SIZE :: mem.Gigabyte when size_of(uintptr) == 8 els // Initialization of an `Arena` to be a `.Growing` variant. // A growing arena is a linked list of `Memory_Block`s allocated with virtual memory. -@(require_results) +@(require_results, no_sanitize_address) arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) { arena.kind = .Growing arena.curr_block = memory_block_alloc(0, reserved, {}) or_return @@ -53,24 +55,26 @@ arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING if arena.minimum_block_size == 0 { arena.minimum_block_size = reserved } + sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed]) return } // Initialization of an `Arena` to be a `.Static` variant. // A static arena contains a single `Memory_Block` allocated with virtual memory. -@(require_results) +@(require_results, no_sanitize_address) arena_init_static :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_STATIC_RESERVE_SIZE, commit_size: uint = DEFAULT_ARENA_STATIC_COMMIT_SIZE) -> (err: Allocator_Error) { arena.kind = .Static arena.curr_block = memory_block_alloc(commit_size, reserved, {}) or_return arena.total_used = 0 arena.total_reserved = arena.curr_block.reserved + sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed]) return } // Initialization of an `Arena` to be a `.Buffer` variant. // A buffer arena contains single `Memory_Block` created from a user provided []byte. -@(require_results) +@(require_results, no_sanitize_address) arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Error) { if len(buffer) < size_of(Memory_Block) { return .Out_Of_Memory @@ -78,7 +82,7 @@ arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Erro arena.kind = .Buffer - mem.zero_slice(buffer) + sanitizer.address_poison(buffer[:]) block_base := raw_data(buffer) block := (^Memory_Block)(block_base) @@ -94,7 +98,7 @@ arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Erro } // Allocates memory from the provided arena. -@(require_results) +@(require_results, no_sanitize_address) arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_location) -> (data: []byte, err: Allocator_Error) { assert(alignment & (alignment-1) == 0, "non-power of two alignment", loc) @@ -158,10 +162,13 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l data, err = alloc_from_memory_block(arena.curr_block, size, alignment, default_commit_size=0) arena.total_used = arena.curr_block.used } + + sanitizer.address_unpoison(data) return } // Resets the memory of a Static or Buffer arena to a specific `position` (offset) and zeroes the previously used memory. +@(no_sanitize_address) arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) -> bool { sync.mutex_guard(&arena.mutex) @@ -175,6 +182,7 @@ arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) mem.zero_slice(arena.curr_block.base[arena.curr_block.used:][:prev_pos-pos]) } arena.total_used = arena.curr_block.used + sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed]) return true } else if pos == 0 { arena.total_used = 0 @@ -184,6 +192,7 @@ arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) } // Frees the last memory block of a Growing Arena +@(no_sanitize_address) arena_growing_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_location) { if free_block := arena.curr_block; free_block != nil { assert(arena.kind == .Growing, "expected a .Growing arena", loc) @@ -191,11 +200,13 @@ arena_growing_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_locat arena.total_reserved -= free_block.reserved arena.curr_block = free_block.prev + sanitizer.address_poison(free_block.base[:free_block.committed]) memory_block_dealloc(free_block) } } // Deallocates all but the first memory block of the arena and resets the allocator's usage to 0. +@(no_sanitize_address) arena_free_all :: proc(arena: ^Arena, loc := #caller_location) { switch arena.kind { case .Growing: @@ -208,7 +219,9 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) { if arena.curr_block != nil { curr_block_used := int(arena.curr_block.used) arena.curr_block.used = 0 + sanitizer.address_unpoison(arena.curr_block.base[:curr_block_used]) mem.zero(arena.curr_block.base, curr_block_used) + sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed]) } arena.total_used = 0 case .Static, .Buffer: @@ -219,6 +232,7 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) { // Frees all of the memory allocated by the arena and zeros all of the values of an arena. // A buffer based arena does not `delete` the provided `[]byte` bufffer. +@(no_sanitize_address) arena_destroy :: proc(arena: ^Arena, loc := #caller_location) { sync.mutex_guard(&arena.mutex) switch arena.kind { @@ -250,7 +264,7 @@ arena_static_bootstrap_new :: proc{ } // Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy. -@(require_results) +@(require_results, no_sanitize_address) arena_growing_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) { bootstrap: Arena bootstrap.kind = .Growing @@ -266,13 +280,13 @@ arena_growing_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintp } // Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy. -@(require_results) +@(require_results, no_sanitize_address) arena_growing_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) { return arena_growing_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size) } // Ability to bootstrap allocate a struct with an arena within the struct itself using the static variant strategy. -@(require_results) +@(require_results, no_sanitize_address) arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) { bootstrap: Arena bootstrap.kind = .Static @@ -288,19 +302,20 @@ arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintpt } // Ability to bootstrap allocate a struct with an arena within the struct itself using the static variant strategy. -@(require_results) +@(require_results, no_sanitize_address) arena_static_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) { return arena_static_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved) } // Create an `Allocator` from the provided `Arena` -@(require_results) +@(require_results, no_sanitize_address) arena_allocator :: proc(arena: ^Arena) -> mem.Allocator { return mem.Allocator{arena_allocator_proc, arena} } // The allocator procedure used by an `Allocator` produced by `arena_allocator` +@(no_sanitize_address) arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, @@ -334,6 +349,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, if size < old_size { // shrink data in-place data = old_data[:size] + sanitizer.address_poison(old_data[size:old_size]) return } @@ -347,6 +363,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, _ = alloc_from_memory_block(block, new_end - old_end, 1, default_commit_size=arena.default_commit_size) or_return arena.total_used += block.used - prev_used data = block.base[start:new_end] + sanitizer.address_unpoison(data) return } } @@ -357,6 +374,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, return } copy(new_memory, old_data[:old_size]) + sanitizer.address_poison(old_data[:old_size]) return new_memory, nil case .Query_Features: set := (^mem.Allocator_Mode_Set)(old_memory) @@ -382,7 +400,7 @@ Arena_Temp :: struct { } // Begins the section of temporary arena memory. -@(require_results) +@(require_results, no_sanitize_address) arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena_Temp) { assert(arena != nil, "nil arena", loc) sync.mutex_guard(&arena.mutex) @@ -397,6 +415,7 @@ arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena } // Ends the section of temporary arena memory by resetting the memory to the stored position. +@(no_sanitize_address) arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) { assert(temp.arena != nil, "nil arena", loc) arena := temp.arena @@ -432,6 +451,7 @@ arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) { } // Ignore the use of a `arena_temp_begin` entirely by __not__ resetting to the stored position. +@(no_sanitize_address) arena_temp_ignore :: proc(temp: Arena_Temp, loc := #caller_location) { assert(temp.arena != nil, "nil arena", loc) arena := temp.arena @@ -442,6 +462,7 @@ arena_temp_ignore :: proc(temp: Arena_Temp, loc := #caller_location) { } // Asserts that all uses of `Arena_Temp` has been used by an `Arena` +@(no_sanitize_address) arena_check_temp :: proc(arena: ^Arena, loc := #caller_location) { assert(arena.temp_count == 0, "Arena_Temp not been ended", loc) } diff --git a/core/mem/virtual/virtual.odin b/core/mem/virtual/virtual.odin index 4afc33813..031fb721a 100644 --- a/core/mem/virtual/virtual.odin +++ b/core/mem/virtual/virtual.odin @@ -2,6 +2,7 @@ package mem_virtual import "core:mem" import "base:intrinsics" +import "base:sanitizer" import "base:runtime" _ :: runtime @@ -14,27 +15,33 @@ platform_memory_init :: proc() { Allocator_Error :: mem.Allocator_Error -@(require_results) +@(require_results, no_sanitize_address) reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { return _reserve(size) } +@(no_sanitize_address) commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error { + sanitizer.address_unpoison(data, size) return _commit(data, size) } -@(require_results) +@(require_results, no_sanitize_address) reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { data = reserve(size) or_return commit(raw_data(data), size) or_return return } +@(no_sanitize_address) decommit :: proc "contextless" (data: rawptr, size: uint) { + sanitizer.address_poison(data, size) _decommit(data, size) } +@(no_sanitize_address) release :: proc "contextless" (data: rawptr, size: uint) { + sanitizer.address_unpoison(data, size) _release(data, size) } @@ -46,13 +53,11 @@ Protect_Flag :: enum u32 { Protect_Flags :: distinct bit_set[Protect_Flag; u32] Protect_No_Access :: Protect_Flags{} +@(no_sanitize_address) protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool { return _protect(data, size, flags) } - - - Memory_Block :: struct { prev: ^Memory_Block, base: [^]byte, @@ -66,13 +71,13 @@ Memory_Block_Flag :: enum u32 { Memory_Block_Flags :: distinct bit_set[Memory_Block_Flag; u32] -@(private="file", require_results) +@(private="file", require_results, no_sanitize_address) align_formula :: #force_inline proc "contextless" (size, align: uint) -> uint { result := size + align-1 return result - result%align } -@(require_results) +@(require_results, no_sanitize_address) memory_block_alloc :: proc(committed, reserved: uint, alignment: uint = 0, flags: Memory_Block_Flags = {}) -> (block: ^Memory_Block, err: Allocator_Error) { page_size := DEFAULT_PAGE_SIZE assert(mem.is_power_of_two(uintptr(page_size))) @@ -116,8 +121,9 @@ memory_block_alloc :: proc(committed, reserved: uint, alignment: uint = 0, flags return &pmblock.block, nil } -@(require_results) +@(require_results, no_sanitize_address) alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint, default_commit_size: uint = 0) -> (data: []byte, err: Allocator_Error) { + @(no_sanitize_address) calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint { alignment_offset := uint(0) ptr := uintptr(block.base[block.used:]) @@ -128,6 +134,7 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint, return alignment_offset } + @(no_sanitize_address) do_commit_if_necessary :: proc(block: ^Memory_Block, size: uint, default_commit_size: uint) -> (err: Allocator_Error) { if block.committed - block.used < size { pmblock := (^Platform_Memory_Block)(block) @@ -172,10 +179,12 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint, data = block.base[block.used+alignment_offset:][:min_size] block.used += size + sanitizer.address_unpoison(data) return } +@(no_sanitize_address) memory_block_dealloc :: proc(block_to_free: ^Memory_Block) { if block := (^Platform_Memory_Block)(block_to_free); block != nil { platform_memory_free(block) diff --git a/core/mem/virtual/virtual_platform.odin b/core/mem/virtual/virtual_platform.odin index 31e9cfca8..c9dde4e9d 100644 --- a/core/mem/virtual/virtual_platform.odin +++ b/core/mem/virtual/virtual_platform.odin @@ -7,6 +7,7 @@ Platform_Memory_Block :: struct { reserved: uint, } +@(no_sanitize_address) platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) { to_commit, to_reserve := to_commit, to_reserve to_reserve = max(to_commit, to_reserve) @@ -26,12 +27,14 @@ platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (bl } +@(no_sanitize_address) platform_memory_free :: proc "contextless" (block: ^Platform_Memory_Block) { if block != nil { release(block, block.reserved) } } +@(no_sanitize_address) platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) { if to_commit < block.committed { return nil diff --git a/core/mem/virtual/virtual_windows.odin b/core/mem/virtual/virtual_windows.odin index acd30ae33..0da8498d5 100644 --- a/core/mem/virtual/virtual_windows.odin +++ b/core/mem/virtual/virtual_windows.odin @@ -83,6 +83,8 @@ foreign Kernel32 { dwNumberOfBytesToMap: uint, ) -> rawptr --- } + +@(no_sanitize_address) _reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { result := VirtualAlloc(nil, size, MEM_RESERVE, PAGE_READWRITE) if result == nil { @@ -93,6 +95,7 @@ _reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Err return } +@(no_sanitize_address) _commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error { result := VirtualAlloc(data, size, MEM_COMMIT, PAGE_READWRITE) if result == nil { @@ -107,12 +110,18 @@ _commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error { } return nil } + +@(no_sanitize_address) _decommit :: proc "contextless" (data: rawptr, size: uint) { VirtualFree(data, size, MEM_DECOMMIT) } + +@(no_sanitize_address) _release :: proc "contextless" (data: rawptr, size: uint) { VirtualFree(data, 0, MEM_RELEASE) } + +@(no_sanitize_address) _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool { pflags: u32 pflags = PAGE_NOACCESS @@ -136,7 +145,7 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) } - +@(no_sanitize_address) _platform_memory_init :: proc() { sys_info: SYSTEM_INFO GetSystemInfo(&sys_info) @@ -147,6 +156,7 @@ _platform_memory_init :: proc() { } +@(no_sanitize_address) _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { page_flags: u32 if flags == {.Read} { diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 6177fcf6e..de6841ed8 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -383,6 +383,8 @@ struct lbProcedure { PtrMap selector_values; PtrMap selector_addr; PtrMap tuple_fix_map; + + Array asan_stack_locals; }; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 421720c4c..dad5d4dd5 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -3070,6 +3070,13 @@ gb_internal lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero if (e != nullptr) { lb_add_entity(p->module, e, val); lb_add_debug_local_variable(p, ptr, type, e->token); + + // NOTE(lucas): In LLVM 20 and below we do not have the option to have asan cleanup poisoned stack + // locals ourselves. So we need to manually track and unpoison these locals on proc return. + // LLVM 21 adds the 'use-after-scope' asan option which does this for us. + if (build_context.sanitizer_flags & SanitizerFlag_Address && !p->entity->Procedure.no_sanitize_address) { + array_add(&p->asan_stack_locals, val); + } } if (zero_init) { diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 7bd8dea59..8f1619006 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -115,12 +115,13 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i p->is_entry_point = false; gbAllocator a = heap_allocator(); - p->children.allocator = a; - p->defer_stmts.allocator = a; - p->blocks.allocator = a; - p->branch_blocks.allocator = a; - p->context_stack.allocator = a; - p->scope_stack.allocator = a; + p->children.allocator = a; + p->defer_stmts.allocator = a; + p->blocks.allocator = a; + p->branch_blocks.allocator = a; + p->context_stack.allocator = a; + p->scope_stack.allocator = a; + p->asan_stack_locals.allocator = a; // map_init(&p->selector_values, 0); // map_init(&p->selector_addr, 0); // map_init(&p->tuple_fix_map, 0); @@ -385,11 +386,12 @@ gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name p->is_entry_point = false; gbAllocator a = permanent_allocator(); - p->children.allocator = a; - p->defer_stmts.allocator = a; - p->blocks.allocator = a; - p->branch_blocks.allocator = a; - p->context_stack.allocator = a; + p->children.allocator = a; + p->defer_stmts.allocator = a; + p->blocks.allocator = a; + p->branch_blocks.allocator = a; + p->context_stack.allocator = a; + p->asan_stack_locals.allocator = a; map_init(&p->tuple_fix_map, 0); diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 96a5d0db1..d5e3e4c75 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -2917,6 +2917,22 @@ gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlo } defer (p->branch_location_pos = prev_token_pos); + // TODO(lucas): In LLVM 21 use the 'use-after-scope' asan option which does this for us. + #if LLVM_VERSION_MAJOR < 21 + if (kind == lbDeferExit_Return) { + for_array(i, p->asan_stack_locals) { + lbValue local = p->asan_stack_locals[i]; + + auto args = array_make(temporary_allocator(), 2); + args[0] = lb_emit_conv(p, local, t_rawptr); + args[1] = lb_const_int(p->module, t_int, type_size_of(local.type->Pointer.elem)); + lb_emit_runtime_call(p, "__asan_unpoison_memory_region", args); + } + } + #else + #error "Need to implement LLVM 21 'use-after-scope' asan option" + #endif + isize count = p->defer_stmts.count; isize i = count; while (i --> 0) { -- cgit v1.2.3 From ea65a7b870736311747c517970df3921d227e024 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 7 May 2025 14:26:10 +0100 Subject: Move raddbg string stuff to a thread-safe queue --- src/llvm_backend.cpp | 172 ++++++++++++++++++++++--------------------- src/llvm_backend.hpp | 1 + src/llvm_backend_debug.cpp | 19 +++++ src/llvm_backend_general.cpp | 1 + 4 files changed, 108 insertions(+), 85 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index f0c1000c5..b69cbc3a5 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2678,106 +2678,42 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { } } - TIME_SECTION("LLVM Runtime Objective-C Names Creation"); - gen->objc_names = lb_create_objc_names(default_module); - - TIME_SECTION("LLVM Runtime Startup Creation (Global Variables & @(init))"); - gen->startup_runtime = lb_create_startup_runtime(default_module, gen->objc_names, global_variables); - - TIME_SECTION("LLVM Runtime Cleanup Creation & @(fini)"); - gen->cleanup_runtime = lb_create_cleanup_runtime(default_module); - - if (build_context.ODIN_DEBUG) { - for (auto const &entry : builtin_pkg->scope->elements) { - Entity *e = entry.value; - lb_add_debug_info_for_global_constant_from_entity(gen, e); - } - // Custom `.raddbg` section for its debugger if (build_context.metrics.os == TargetOs_windows) { - LLVMModuleRef m = default_module->mod; - LLVMContextRef c = default_module->ctx; + lbModule *m = default_module; + LLVMModuleRef mod = m->mod; + LLVMContextRef ctx = m->ctx; { - LLVMTypeRef type = LLVMArrayType(LLVMInt8TypeInContext(c), 1); - LLVMValueRef global = LLVMAddGlobal(m, type, "raddbg_is_attached_byte_marker"); + LLVMTypeRef type = LLVMArrayType(LLVMInt8TypeInContext(ctx), 1); + LLVMValueRef global = LLVMAddGlobal(mod, type, "raddbg_is_attached_byte_marker"); LLVMSetInitializer(global, LLVMConstNull(type)); LLVMSetSection(global, ".raddbg"); } - TEMPORARY_ALLOCATOR_GUARD(); - - u32 index = 0; - auto const add_string = [m, c, &index](String const &str) { - LLVMValueRef data = LLVMConstStringInContext(c, cast(char const *)str.text, cast(unsigned)str.len, false); - LLVMTypeRef type = LLVMTypeOf(data); - - gbString global_name = gb_string_make(temporary_allocator(), "raddbg_data__"); - global_name = gb_string_append_fmt(global_name, "%u", index); - index += 1; - - LLVMValueRef global = LLVMAddGlobal(m, type, global_name); - - LLVMSetInitializer(global, data); - LLVMSetAlignment(global, 1); - - LLVMSetSection(global, ".raddbg"); - }; - - auto const add_string1 = [add_string](char const *a) { - add_string(make_string_c(a)); - }; - auto const add_string3 = [add_string](char const *a, char const *b, char const *c) { - add_string(concatenate3_strings(temporary_allocator(), make_string_c(a), make_string_c(b), make_string_c(c))); - }; - - - if (gen->info->entry_point) { - String mangled_name = lb_get_entity_name(default_module, gen->info->entry_point); + String mangled_name = lb_get_entity_name(m, gen->info->entry_point); char const *str = alloc_cstring(temporary_allocator(), mangled_name); - add_string3("entry_point: \"", str, "\""); + lb_add_raddbg_string(m, "entry_point: \"", str, "\""); } + } + } + + TIME_SECTION("LLVM Runtime Objective-C Names Creation"); + gen->objc_names = lb_create_objc_names(default_module); - add_string1("type_view: {type: \"[]?\", expr: \"array(data, len)\"}"); - add_string1("type_view: {type: \"string\", expr: \"array(data, len)\"}"); + TIME_SECTION("LLVM Runtime Startup Creation (Global Variables & @(init))"); + gen->startup_runtime = lb_create_startup_runtime(default_module, gen->objc_names, global_variables); - // column major matrices - add_string1("type_view: {type: \"matrix[1, ?]?\", expr: \"table($.data, $[0])\"}"); - add_string1("type_view: {type: \"matrix[2, ?]?\", expr: \"table($.data, $[0], $[1])\"}"); - add_string1("type_view: {type: \"matrix[3, ?]?\", expr: \"table($.data, $[0], $[1], $[2])\"}"); - add_string1("type_view: {type: \"matrix[4, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3])\"}"); - add_string1("type_view: {type: \"matrix[5, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4])\"}"); - add_string1("type_view: {type: \"matrix[6, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}"); - add_string1("type_view: {type: \"matrix[7, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}"); - add_string1("type_view: {type: \"matrix[8, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}"); - add_string1("type_view: {type: \"matrix[9, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}"); - add_string1("type_view: {type: \"matrix[10, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}"); - add_string1("type_view: {type: \"matrix[11, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}"); - add_string1("type_view: {type: \"matrix[12, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}"); - add_string1("type_view: {type: \"matrix[13, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}"); - add_string1("type_view: {type: \"matrix[14, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}"); - add_string1("type_view: {type: \"matrix[15, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}"); - add_string1("type_view: {type: \"matrix[16, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}"); + TIME_SECTION("LLVM Runtime Cleanup Creation & @(fini)"); + gen->cleanup_runtime = lb_create_cleanup_runtime(default_module); - // row major matrices - add_string1("type_view: {type: \"#row_major matrix[?, 1]?\", expr: \"table($.data, $[0])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 2]?\", expr: \"table($.data, $[0], $[1])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 3]?\", expr: \"table($.data, $[0], $[1], $[2])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 4]?\", expr: \"table($.data, $[0], $[1], $[2], $[3])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 5]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 6]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 7]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 8]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 9]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 10]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 11]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 12]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 13]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 14]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 15]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 16]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}"); + + if (build_context.ODIN_DEBUG) { + for (auto const &entry : builtin_pkg->scope->elements) { + Entity *e = entry.value; + lb_add_debug_info_for_global_constant_from_entity(gen, e); } } @@ -2807,6 +2743,72 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { if (build_context.ODIN_DEBUG) { TIME_SECTION("LLVM Debug Info Complete Types and Finalize"); lb_debug_info_complete_types_and_finalize(gen); + + // Custom `.raddbg` section for its debugger + if (build_context.metrics.os == TargetOs_windows) { + lbModule *m = default_module; + LLVMModuleRef mod = m->mod; + LLVMContextRef ctx = m->ctx; + + lb_add_raddbg_string(m, "type_view: {type: \"[]?\", expr: \"array(data, len)\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"string\", expr: \"array(data, len)\"}"); + + // column major matrices + lb_add_raddbg_string(m, "type_view: {type: \"matrix[1, ?]?\", expr: \"table($.data, $[0])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[2, ?]?\", expr: \"table($.data, $[0], $[1])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[3, ?]?\", expr: \"table($.data, $[0], $[1], $[2])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[4, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[5, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[6, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[7, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[8, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[9, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[10, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[11, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[12, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[13, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[14, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[15, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[16, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}"); + + // row major matrices + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 1]?\", expr: \"table($.data, $[0])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 2]?\", expr: \"table($.data, $[0], $[1])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 3]?\", expr: \"table($.data, $[0], $[1], $[2])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 4]?\", expr: \"table($.data, $[0], $[1], $[2], $[3])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 5]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 6]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 7]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 8]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 9]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 10]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 11]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 12]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 13]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 14]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 15]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 16]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}"); + + + TEMPORARY_ALLOCATOR_GUARD(); + + u32 global_name_index = 0; + for (String str = {}; mpsc_dequeue(&gen->raddebug_section_strings, &str); /**/) { + LLVMValueRef data = LLVMConstStringInContext(ctx, cast(char const *)str.text, cast(unsigned)str.len, false); + LLVMTypeRef type = LLVMTypeOf(data); + + gbString global_name = gb_string_make(temporary_allocator(), "raddbg_data__"); + global_name = gb_string_append_fmt(global_name, "%u", global_name_index); + global_name_index += 1; + + LLVMValueRef global = LLVMAddGlobal(mod, type, global_name); + + LLVMSetInitializer(global, data); + LLVMSetAlignment(global, 1); + + LLVMSetSection(global, ".raddbg"); + } + } } if (do_threading) { diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index de6841ed8..a0764494a 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -240,6 +240,7 @@ struct lbGenerator : LinkerData { MPSCQueue entities_to_correct_linkage; MPSCQueue objc_selectors; MPSCQueue objc_classes; + MPSCQueue raddebug_section_strings; }; diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 0358031d1..464d50cac 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -18,6 +18,25 @@ gb_internal void lb_set_llvm_metadata(lbModule *m, void *key, LLVMMetadataRef va } } +gb_internal void lb_add_raddbg_string(lbModule *m, String const &str) { + mpsc_enqueue(&m->gen->raddebug_section_strings, copy_string(permanent_allocator(), str)); +} + +gb_internal void lb_add_raddbg_string(lbModule *m, char const *cstr) { + mpsc_enqueue(&m->gen->raddebug_section_strings, copy_string(permanent_allocator(), make_string_c(cstr))); +} + +gb_internal void lb_add_raddbg_string(lbModule *m, char const *a, char const *b) { + String str = concatenate_strings(permanent_allocator(), make_string_c(a), make_string_c(b)); + mpsc_enqueue(&m->gen->raddebug_section_strings, str); +} + +gb_internal void lb_add_raddbg_string(lbModule *m, char const *a, char const *b, char const *c) { + String str = concatenate3_strings(permanent_allocator(), make_string_c(a), make_string_c(b), make_string_c(c)); + mpsc_enqueue(&m->gen->raddebug_section_strings, str); +} + + gb_internal LLVMMetadataRef lb_get_current_debug_scope(lbProcedure *p) { GB_ASSERT_MSG(p->debug_info != nullptr, "missing debug information for %.*s", LIT(p->name)); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index dad5d4dd5..2eaecd8a7 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -173,6 +173,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { mpsc_init(&gen->entities_to_correct_linkage, heap_allocator()); mpsc_init(&gen->objc_selectors, heap_allocator()); mpsc_init(&gen->objc_classes, heap_allocator()); + mpsc_init(&gen->raddebug_section_strings, heap_allocator()); return true; } -- cgit v1.2.3 From f9b9e9e7dcbb605bc64bc5af1331855375f58494 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Fri, 9 May 2025 22:27:35 +0200 Subject: some ABI fixups and improvements Started with trying to enable asan in the CI for MacOS, noticed it wasn't enabled on the `tests/internal` folder, it came up with a couple of issues with the abi/OdinLLVMBuildTransmute that this also solves. - Looking at clang output for arm64, we should be promoting `{ i64, i32 }` to `{ i64, i64 }` - after doing the previous point, I noticed this is not handled well in OdinLLVMBuildTransmute which was emitting loads and stores into the space of a value that was alignment, asan does not want this, looking at clang output again, a memcpy is the appropriate way of handling this. - Having done this we don't need the hacky "return is packed" set anymore in the amd64 sysv ABI anymore either --- src/llvm_abi.cpp | 42 +++++++++++--------------------- src/llvm_backend_general.cpp | 58 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 31 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index c8e1ca764..baad3f873 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -977,7 +977,7 @@ namespace lbAbiAmd64SysV { return types[0]; } - return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, sz == 0); + return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, false); } gb_internal void classify_with(LLVMTypeRef t, Array *cls, i64 ix, i64 off) { @@ -1231,38 +1231,24 @@ namespace lbAbiArm64 { } } else { i64 size = lb_sizeof(return_type); - if (size <= 16) { - LLVMTypeRef cast_type = nullptr; - - if (size == 0) { - cast_type = LLVMStructTypeInContext(c, nullptr, 0, false); - } else if (size <= 8) { - cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8)); - } else { - unsigned count = cast(unsigned)((size+7)/8); - - LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64); - LLVMTypeRef *types = gb_alloc_array(temporary_allocator(), LLVMTypeRef, count); - - i64 size_copy = size; - for (unsigned i = 0; i < count; i++) { - if (size_copy >= 8) { - types[i] = llvm_i64; - } else { - types[i] = LLVMIntTypeInContext(c, 8*cast(unsigned)size_copy); - } - size_copy -= 8; - } - GB_ASSERT(size_copy <= 0); - cast_type = LLVMStructTypeInContext(c, types, count, true); - } - return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr); - } else { + if (size > 16) { LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO(); LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type); return lb_arg_type_indirect(return_type, attr); } + + GB_ASSERT(size <= 16); + LLVMTypeRef cast_type = nullptr; + if (size == 0) { + cast_type = LLVMStructTypeInContext(c, nullptr, 0, false); + } else if (size <= 8) { + cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8)); + } else { + LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64); + cast_type = LLVMArrayType2(llvm_i64, 2); + } + return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr); } } diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index c52551b36..504c8234e 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2525,10 +2525,13 @@ general_end:; } } - src_size = align_formula(src_size, src_align); - dst_size = align_formula(dst_size, dst_align); + // NOTE(laytan): even though this logic seems sound, the Address Sanitizer does not + // want you to load/store the space of a value that is there for alignment. +#if 0 + i64 aligned_src_size = align_formula(src_size, src_align); + i64 aligned_dst_size = align_formula(dst_size, dst_align); - if (LLVMIsALoadInst(val) && (src_size >= dst_size && src_align >= dst_align)) { + if (LLVMIsALoadInst(val) && (aligned_src_size >= aligned_dst_size && src_align >= dst_align)) { LLVMValueRef val_ptr = LLVMGetOperand(val, 0); val_ptr = LLVMBuildPointerCast(p->builder, val_ptr, LLVMPointerType(dst_type, 0), ""); LLVMValueRef loaded_val = OdinLLVMBuildLoad(p, dst_type, val_ptr); @@ -2536,8 +2539,57 @@ general_end:; // LLVMSetAlignment(loaded_val, gb_min(src_align, dst_align)); return loaded_val; + } +#endif + + if (src_size > dst_size) { + GB_ASSERT(p->decl_block != p->curr_block); + // NOTE(laytan): src is bigger than dst, need to memcpy the part of src we want. + + LLVMValueRef val_ptr; + if (LLVMIsALoadInst(val)) { + val_ptr = LLVMGetOperand(val, 0); + } else if (LLVMIsAAllocaInst(val)) { + val_ptr = LLVMBuildPointerCast(p->builder, val, LLVMPointerType(src_type, 0), ""); + } else { + // NOTE(laytan): we need a pointer to memcpy from. + LLVMValueRef val_copy = llvm_alloca(p, src_type, src_align); + val_ptr = LLVMBuildPointerCast(p->builder, val_copy, LLVMPointerType(src_type, 0), ""); + LLVMBuildStore(p->builder, val, val_ptr); + } + + i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type)); + max_align = gb_max(max_align, 16); + + LLVMValueRef ptr = llvm_alloca(p, dst_type, max_align); + LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(dst_type, 0), ""); + + LLVMTypeRef types[3] = { + lb_type(p->module, t_rawptr), + lb_type(p->module, t_rawptr), + lb_type(p->module, t_int) + }; + + LLVMValueRef args[4] = { + nptr, + val_ptr, + LLVMConstInt(LLVMIntTypeInContext(p->module->ctx, 8*cast(unsigned)build_context.int_size), dst_size, 0), + LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, 0), + }; + + lb_call_intrinsic( + p, + "llvm.memcpy.inline", + args, + gb_count_of(args), + types, + gb_count_of(types) + ); + + return OdinLLVMBuildLoad(p, dst_type, ptr); } else { GB_ASSERT(p->decl_block != p->curr_block); + GB_ASSERT(dst_size >= src_size); i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type)); max_align = gb_max(max_align, 16); -- cgit v1.2.3 From c35a45e823401a1d7a15f11c6fb07e4fe9e6007a Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sat, 17 May 2025 16:28:34 +0200 Subject: fix global and static any Fixes #4627 --- src/llvm_backend.cpp | 25 ++++-------------- src/llvm_backend_general.cpp | 43 ++++++++++++++++-------------- src/llvm_backend_stmt.cpp | 52 ++++++++++++++++++++++--------------- tests/internal/test_global_any.odin | 40 ++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 60 deletions(-) create mode 100644 tests/internal/test_global_any.odin (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 7de147058..361a0c46b 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1973,14 +1973,14 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc gbString var_name = gb_string_make(permanent_allocator(), "__$global_any::"); gbString e_str = string_canonical_entity_name(temporary_allocator(), e); var_name = gb_string_append_length(var_name, e_str, gb_strlen(e_str)); - lbAddr g = lb_add_global_generated_with_name(main_module, var_type, var.init, make_string_c(var_name)); + lbAddr g = lb_add_global_generated_with_name(main_module, var_type, {}, make_string_c(var_name)); lb_addr_store(p, g, var.init); lbValue gp = lb_addr_get_ptr(p, g); lbValue data = lb_emit_struct_ep(p, var.var, 0); lbValue ti = lb_emit_struct_ep(p, var.var, 1); lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr)); - lb_emit_store(p, ti, lb_type_info(p, var_type)); + lb_emit_store(p, ti, lb_typeid(p->module, var_type)); } else { LLVMTypeRef vt = llvm_addr_type(p->module, var.var); lbValue src0 = lb_emit_conv(p, var.init, t); @@ -3194,24 +3194,9 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { lbValue g = {}; g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name)); g.type = alloc_type_pointer(e->type); - if (e->Variable.thread_local_model != "") { - LLVMSetThreadLocal(g.value, true); - - String m = e->Variable.thread_local_model; - LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel; - if (m == "default") { - mode = LLVMGeneralDynamicTLSModel; - } else if (m == "localdynamic") { - mode = LLVMLocalDynamicTLSModel; - } else if (m == "initialexec") { - mode = LLVMInitialExecTLSModel; - } else if (m == "localexec") { - mode = LLVMLocalExecTLSModel; - } else { - GB_PANIC("Unhandled thread local mode %.*s", LIT(m)); - } - LLVMSetThreadLocalMode(g.value, mode); - } + + lb_apply_thread_local_model(g.value, e->Variable.thread_local_model); + if (is_foreign) { LLVMSetLinkage(g.value, LLVMExternalLinkage); LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 504c8234e..85a165de4 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2387,6 +2387,29 @@ gb_internal void lb_add_attribute_to_proc_with_string(lbModule *m, LLVMValueRef } +gb_internal bool lb_apply_thread_local_model(LLVMValueRef value, String model) { + if (model != "") { + LLVMSetThreadLocal(value, true); + + LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel; + if (model == "default") { + mode = LLVMGeneralDynamicTLSModel; + } else if (model == "localdynamic") { + mode = LLVMLocalDynamicTLSModel; + } else if (model == "initialexec") { + mode = LLVMInitialExecTLSModel; + } else if (model == "localexec") { + mode = LLVMLocalExecTLSModel; + } else { + GB_PANIC("Unhandled thread local mode %.*s", LIT(model)); + } + LLVMSetThreadLocalMode(value, mode); + return true; + } + + return false; +} + gb_internal void lb_add_edge(lbBlock *from, lbBlock *to) { LLVMValueRef instr = LLVMGetLastInstruction(from->block); @@ -2990,25 +3013,7 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) { lb_set_entity_from_other_modules_linkage_correctly(other_module, e, name); - if (e->Variable.thread_local_model != "") { - LLVMSetThreadLocal(g.value, true); - - String m = e->Variable.thread_local_model; - LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel; - if (m == "default") { - mode = LLVMGeneralDynamicTLSModel; - } else if (m == "localdynamic") { - mode = LLVMLocalDynamicTLSModel; - } else if (m == "initialexec") { - mode = LLVMInitialExecTLSModel; - } else if (m == "localexec") { - mode = LLVMLocalExecTLSModel; - } else { - GB_PANIC("Unhandled thread local mode %.*s", LIT(m)); - } - LLVMSetThreadLocalMode(g.value, mode); - } - + lb_apply_thread_local_model(g.value, e->Variable.thread_local_model); return g; } diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 44a78b036..9b5b14626 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -2022,33 +2022,43 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) { LLVMValueRef global = LLVMAddGlobal(p->module->mod, lb_type(p->module, e->type), c_name); LLVMSetAlignment(global, cast(u32)type_align_of(e->type)); LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type))); - if (value.value != nullptr) { - LLVMSetInitializer(global, value.value); - } + if (e->Variable.is_rodata) { LLVMSetGlobalConstant(global, true); } - if (e->Variable.thread_local_model != "") { - LLVMSetThreadLocal(global, true); - - String m = e->Variable.thread_local_model; - LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel; - if (m == "default") { - mode = LLVMGeneralDynamicTLSModel; - } else if (m == "localdynamic") { - mode = LLVMLocalDynamicTLSModel; - } else if (m == "initialexec") { - mode = LLVMInitialExecTLSModel; - } else if (m == "localexec") { - mode = LLVMLocalExecTLSModel; - } else { - GB_PANIC("Unhandled thread local mode %.*s", LIT(m)); - } - LLVMSetThreadLocalMode(global, mode); - } else { + + if (!lb_apply_thread_local_model(global, e->Variable.thread_local_model)) { LLVMSetLinkage(global, LLVMInternalLinkage); } + if (value.value != nullptr) { + if (is_type_any(e->type)) { + Type *var_type = default_type(value.type); + + gbString var_name = gb_string_make(temporary_allocator(), "__$static_any::"); + var_name = gb_string_append_length(var_name, mangled_name.text, mangled_name.len); + + lbAddr var_global = lb_add_global_generated_with_name(p->module, var_type, value, make_string_c(var_name), nullptr); + LLVMValueRef var_global_ref = var_global.addr.value; + + if (e->Variable.is_rodata) { + LLVMSetGlobalConstant(var_global_ref, true); + } + + if (!lb_apply_thread_local_model(var_global_ref, e->Variable.thread_local_model)) { + LLVMSetLinkage(var_global_ref, LLVMInternalLinkage); + } + + LLVMValueRef vals[2] = { + lb_emit_conv(p, var_global.addr, t_rawptr).value, + lb_typeid(p->module, var_type).value, + }; + LLVMValueRef init = llvm_const_named_struct(p->module, e->type, vals, gb_count_of(vals)); + LLVMSetInitializer(global, init); + } else { + LLVMSetInitializer(global, value.value); + } + } lbValue global_val = {global, alloc_type_pointer(e->type)}; lb_add_entity(p->module, e, global_val); diff --git a/tests/internal/test_global_any.odin b/tests/internal/test_global_any.odin new file mode 100644 index 000000000..73b70e0a4 --- /dev/null +++ b/tests/internal/test_global_any.odin @@ -0,0 +1,40 @@ +package test_internal + +@(private="file") +global_any_from_proc: any = from_proc() + +from_proc :: proc() -> f32 { + return 1.1 +} + +@(private="file") +global_any: any = 1 + +import "core:testing" + +@(test) +test_global_any :: proc(t: ^testing.T) { + as_f32, is_f32 := global_any_from_proc.(f32) + testing.expect(t, is_f32 == true) + testing.expect(t, as_f32 == 1.1) + + as_int, is_int := global_any.(int) + testing.expect(t, is_int == true) + testing.expect(t, as_int == 1) +} + +@(test) +test_static_any :: proc(t: ^testing.T) { + @(static) + var: any = 3 + + as_int, is_int := var.(int) + testing.expect(t, is_int == true) + testing.expect(t, as_int == 3) + + var = f32(1.1) + + as_f32, is_f32 := var.(f32) + testing.expect(t, is_f32 == true) + testing.expect(t, as_f32 == 1.1) +} -- cgit v1.2.3 From f94fc992d788696a4bc903a3f179307a28accbb9 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Mon, 2 Jun 2025 16:53:18 +0200 Subject: fix swizzle in for in statement Fixes #1730 --- src/llvm_backend_general.cpp | 9 ++++++--- src/llvm_backend_stmt.cpp | 18 +++++++++++++++--- tests/issues/run.bat | 1 + tests/issues/run.sh | 1 + tests/issues/test_issue_1730.odin | 21 +++++++++++++++++++++ 5 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 tests/issues/test_issue_1730.odin (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 3a099ec55..5aaa7f63a 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -546,8 +546,11 @@ gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) { break; case lbAddr_Swizzle: + GB_PANIC("lbAddr_Swizzle should be handled elsewhere"); + break; + case lbAddr_SwizzleLarge: - // TOOD(bill): is this good enough logic? + GB_PANIC("lbAddr_SwizzleLarge should be handled elsewhere"); break; } @@ -922,7 +925,7 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) { GB_ASSERT(value.value != nullptr); value = lb_emit_conv(p, value, lb_addr_type(addr)); - lbValue dst = lb_addr_get_ptr(p, addr); + lbValue dst = addr.addr; lbValue src = lb_address_from_load_or_generate_local(p, value); { lbValue src_ptrs[4] = {}; @@ -948,7 +951,7 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) { GB_ASSERT(value.value != nullptr); value = lb_emit_conv(p, value, lb_addr_type(addr)); - lbValue dst = lb_addr_get_ptr(p, addr); + lbValue dst = addr.addr; lbValue src = lb_address_from_load_or_generate_local(p, value); for_array(i, addr.swizzle_large.indices) { lbValue src_ptr = lb_emit_array_epi(p, src, i); diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 9b5b14626..027837f3f 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1072,10 +1072,22 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc break; } case Type_Array: { - lbValue array = lb_build_addr_ptr(p, expr); - if (is_type_pointer(type_deref(array.type))) { - array = lb_emit_load(p, array); + lbValue array; + lbAddr addr = lb_build_addr(p, expr); + switch (addr.kind) { + case lbAddr_Swizzle: + case lbAddr_SwizzleLarge: + // NOTE(laytan): apply the swizzle. + array = lb_address_from_load(p, lb_addr_load(p, addr)); + break; + default: + array = lb_addr_get_ptr(p, addr); + if (is_type_pointer(type_deref(array.type))) { + array = lb_emit_load(p, array); + } + break; } + lbAddr count_ptr = lb_add_local_generated(p, t_int, false); lb_addr_store(p, count_ptr, lb_const_int(p->module, t_int, et->Array.count)); lb_build_range_indexed(p, array, val0_type, count_ptr.addr, &val, &key, &loop, &done, rs->reverse); diff --git a/tests/issues/run.bat b/tests/issues/run.bat index db941b55a..156b1754e 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -9,6 +9,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style ..\..\..\odin test ..\test_issue_829.odin %COMMON% || exit /b ..\..\..\odin test ..\test_issue_1592.odin %COMMON% || exit /b +..\..\..\odin test ..\test_issue_1730.odin %COMMON% || exit /b ..\..\..\odin test ..\test_issue_2056.odin %COMMON% || exit /b ..\..\..\odin build ..\test_issue_2113.odin %COMMON% -debug || exit /b ..\..\..\odin test ..\test_issue_2466.odin %COMMON% || exit /b diff --git a/tests/issues/run.sh b/tests/issues/run.sh index db0864c3e..cd70f6401 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -10,6 +10,7 @@ set -x $ODIN test ../test_issue_829.odin $COMMON $ODIN test ../test_issue_1592.odin $COMMON +$ODIN test ../test_issue_1730.odin $COMMON $ODIN test ../test_issue_2056.odin $COMMON $ODIN build ../test_issue_2113.odin $COMMON -debug $ODIN test ../test_issue_2466.odin $COMMON diff --git a/tests/issues/test_issue_1730.odin b/tests/issues/test_issue_1730.odin new file mode 100644 index 000000000..c1c5c4cae --- /dev/null +++ b/tests/issues/test_issue_1730.odin @@ -0,0 +1,21 @@ +package test_issues + +import "core:testing" + +// Tests issue #1730 https://github.com/odin-lang/Odin/issues/1730 +@(test) +test_issue_1730 :: proc(t: ^testing.T) { + ll := [4]int{1, 2, 3, 4} + for l, i in ll.yz { + testing.expect(t, i <= 1) + if i == 0 { + testing.expect_value(t, l, 2) + } else if i == 1 { + testing.expect_value(t, l, 3) + } + } + + out: [4]int + out.yz = ll.yz + testing.expect_value(t, out, [4]int{0, 2, 3, 0}) +} -- cgit v1.2.3 From 3a86bc9c6d3ca51ce02397f73605a7fbd7f3a899 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Fri, 20 Jun 2025 21:56:50 +0200 Subject: Fix WASM C ABI for raw unions --- src/llvm_abi.cpp | 70 ++++++++++++++++++++++++++++++++-------- src/llvm_backend_general.cpp | 2 +- src/types.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 15 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index af08722c3..e1cbe7558 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1313,7 +1313,7 @@ namespace lbAbiWasm { registers/arguments if possible rather than by pointer. */ gb_internal Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention, Type *original_type); - gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type); + gb_internal lbArgType compute_return_type(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, Type* original_type); enum {MAX_DIRECT_STRUCT_SIZE = 32}; @@ -1323,7 +1323,9 @@ namespace lbAbiWasm { ft->ctx = c; ft->calling_convention = calling_convention; ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention, original_type); - ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple); + + GB_ASSERT(original_type->kind == Type_Proc); + ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple, original_type->Proc.results); return ft; } @@ -1359,7 +1361,7 @@ namespace lbAbiWasm { return false; } - gb_internal bool type_can_be_direct(LLVMTypeRef type, ProcCallingConvention calling_convention) { + gb_internal bool type_can_be_direct(LLVMTypeRef type, Type *original_type, ProcCallingConvention calling_convention) { LLVMTypeKind kind = LLVMGetTypeKind(type); i64 sz = lb_sizeof(type); if (sz == 0) { @@ -1372,9 +1374,21 @@ namespace lbAbiWasm { return false; } else if (kind == LLVMStructTypeKind) { unsigned count = LLVMCountStructElementTypes(type); + + // NOTE(laytan): raw unions are always structs with 1 field in LLVM, need to check our own def. + Type *bt = base_type(original_type); + if (bt->kind == Type_Struct && bt->Struct.is_raw_union) { + count = cast(unsigned)bt->Struct.fields.count; + } + if (count == 1) { - return type_can_be_direct(LLVMStructGetTypeAtIndex(type, 0), calling_convention); + return type_can_be_direct( + LLVMStructGetTypeAtIndex(type, 0), + type_internal_index(original_type, 0), + calling_convention + ); } + } else if (is_basic_register_type(type)) { return true; } @@ -1398,7 +1412,7 @@ namespace lbAbiWasm { return false; } - gb_internal lbArgType is_struct(LLVMContextRef c, LLVMTypeRef type, ProcCallingConvention calling_convention) { + gb_internal lbArgType is_struct(LLVMContextRef c, LLVMTypeRef type, Type *original_type, ProcCallingConvention calling_convention) { LLVMTypeKind kind = LLVMGetTypeKind(type); GB_ASSERT(kind == LLVMArrayTypeKind || kind == LLVMStructTypeKind); @@ -1406,15 +1420,15 @@ namespace lbAbiWasm { if (sz == 0) { return lb_arg_type_ignore(type); } - if (type_can_be_direct(type, calling_convention)) { + if (type_can_be_direct(type, original_type, calling_convention)) { return lb_arg_type_direct(type); } return lb_arg_type_indirect(type, nullptr); } - gb_internal lbArgType pseudo_slice(LLVMContextRef c, LLVMTypeRef type, ProcCallingConvention calling_convention) { + gb_internal lbArgType pseudo_slice(LLVMContextRef c, LLVMTypeRef type, Type *original_type, ProcCallingConvention calling_convention) { if (build_context.metrics.ptr_size < build_context.metrics.int_size && - type_can_be_direct(type, calling_convention)) { + type_can_be_direct(type, original_type, calling_convention)) { LLVMTypeRef types[2] = { LLVMStructGetTypeAtIndex(type, 0), // ignore padding @@ -1423,7 +1437,7 @@ namespace lbAbiWasm { LLVMTypeRef new_type = LLVMStructTypeInContext(c, types, gb_count_of(types), false); return lb_arg_type_direct(type, new_type, nullptr, nullptr); } else { - return is_struct(c, type, calling_convention); + return is_struct(c, type, original_type, calling_convention); } } @@ -1444,9 +1458,9 @@ namespace lbAbiWasm { LLVMTypeKind kind = LLVMGetTypeKind(t); if (kind == LLVMStructTypeKind || kind == LLVMArrayTypeKind) { if (is_type_slice(ptype) || is_type_string(ptype)) { - args[i] = pseudo_slice(c, t, calling_convention); + args[i] = pseudo_slice(c, t, ptype, calling_convention); } else { - args[i] = is_struct(c, t, calling_convention); + args[i] = is_struct(c, t, ptype, calling_convention); } } else { args[i] = non_struct(c, t, false); @@ -1455,11 +1469,11 @@ namespace lbAbiWasm { return args; } - gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type) { + gb_internal lbArgType compute_return_type(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, Type* original_type) { if (!return_is_defined) { return lb_arg_type_direct(LLVMVoidTypeInContext(c)); } else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) { - if (type_can_be_direct(return_type, ft->calling_convention)) { + if (type_can_be_direct(return_type, original_type, ft->calling_convention)) { return lb_arg_type_direct(return_type); } else if (ft->calling_convention != ProcCC_CDecl) { i64 sz = lb_sizeof(return_type); @@ -1471,7 +1485,35 @@ namespace lbAbiWasm { } } - LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO(); + // Multiple returns. + if (return_is_tuple) { \ + lbArgType return_arg = {}; + if (lb_is_type_kind(return_type, LLVMStructTypeKind)) { + unsigned field_count = LLVMCountStructElementTypes(return_type); + if (field_count > 1) { + ft->original_arg_count = ft->args.count; + ft->multiple_return_original_type = return_type; + + for (unsigned i = 0; i < field_count-1; i++) { + LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(return_type, i); + LLVMTypeRef field_pointer_type = LLVMPointerType(field_type, 0); + lbArgType ret_partial = lb_arg_type_direct(field_pointer_type); + array_add(&ft->args, ret_partial); + } + + return_arg = compute_return_type( + ft, + c, + LLVMStructGetTypeAtIndex(return_type, field_count-1), + true, false, + type_internal_index(original_type, field_count-1) + ); + } + } + if (return_arg.type != nullptr) { + return return_arg; + } + } LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type); return lb_arg_type_indirect(return_type, attr); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 5aaa7f63a..5d6a55973 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2206,7 +2206,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { field_count = 3; } LLVMTypeRef *fields = gb_alloc_array(permanent_allocator(), LLVMTypeRef, field_count); - fields[0] = LLVMPointerType(lb_type(m, type->Pointer.elem), 0); + fields[0] = LLVMPointerType(lb_type(m, type->SoaPointer.elem), 0); if (bigger_int) { fields[1] = lb_type_padding_filler(m, build_context.ptr_size, build_context.ptr_size); fields[2] = LLVMIntTypeInContext(ctx, 8*cast(unsigned)build_context.int_size); diff --git a/src/types.cpp b/src/types.cpp index c7573173c..861aa5bf8 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -4614,6 +4614,82 @@ gb_internal Type *alloc_type_proc_from_types(Type **param_types, unsigned param_ // return type; // } +// Index a type that is internally a struct or array. +gb_internal Type *type_internal_index(Type *t, isize index) { + Type *bt = base_type(t); + if (bt == nullptr) { + return nullptr; + } + + switch (bt->kind) { + case Type_Basic: + { + switch (bt->Basic.kind) { + case Basic_complex32: return t_f16; + case Basic_complex64: return t_f32; + case Basic_complex128: return t_f64; + case Basic_quaternion64: return t_f16; + case Basic_quaternion128: return t_f32; + case Basic_quaternion256: return t_f64; + case Basic_string: + { + GB_ASSERT(index == 0 || index == 1); + return index == 0 ? t_u8_ptr : t_int; + } + case Basic_any: + { + GB_ASSERT(index == 0 || index == 1); + return index == 0 ? t_rawptr : t_typeid; + } + } + } + break; + + case Type_Array: return bt->Array.elem; + case Type_EnumeratedArray: return bt->EnumeratedArray.elem; + case Type_SimdVector: return bt->SimdVector.elem; + case Type_Slice: + { + GB_ASSERT(index == 0 || index == 1); + return index == 0 ? t_rawptr : t_typeid; + } + case Type_DynamicArray: + { + switch (index) { + case 0: return t_rawptr; + case 1: return t_int; + case 2: return t_int; + case 3: return t_allocator; + default: GB_PANIC("invalid raw dynamic array index"); + }; + } + case Type_Struct: + return get_struct_field_type(bt, index); + case Type_Union: + if (index < bt->Union.variants.count) { + return bt->Union.variants[index]; + } + return union_tag_type(bt); + case Type_Tuple: + return bt->Tuple.variables[index]->type; + case Type_Matrix: + return bt->Matrix.elem; + case Type_SoaPointer: + { + GB_ASSERT(index == 0 || index == 1); + return index == 0 ? t_rawptr : t_int; + } + case Type_Map: + return type_internal_index(bt->Map.debug_metadata_type, index); + case Type_BitField: + return type_internal_index(bt->BitField.backing_type, index); + case Type_Generic: + return type_internal_index(bt->Generic.specialized, index); + }; + + GB_PANIC("Unhandled type %s", type_to_string(bt)); +}; + gb_internal gbString write_type_to_string(gbString str, Type *type, bool shorthand=false, bool allow_polymorphic=false) { if (type == nullptr) { return gb_string_appendc(str, ""); -- cgit v1.2.3 From f72b2b153057e1d629c85af2ea7c54f7928198d5 Mon Sep 17 00:00:00 2001 From: Hayden Gray <35206453+A1029384756@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:43:44 -0400 Subject: [source-code-locations] - added options to show, obfuscate, and hide source code locations (#5412) --- src/build_settings.cpp | 9 ++++++++- src/check_expr.cpp | 46 ++++++++++++++++++++++++++++++++++++++++---- src/llvm_backend_const.cpp | 20 ++++++++++++++++--- src/llvm_backend_general.cpp | 14 +++++++++++++- src/main.cpp | 30 ++++++++++++++++++++++++++--- 5 files changed, 107 insertions(+), 12 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 05709902a..ebe57bf1e 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -385,6 +385,13 @@ enum LinkerChoice : i32 { Linker_COUNT, }; +enum SourceCodeLocationInfo : u8 { + SourceCodeLocationInfo_Normal = 0, + SourceCodeLocationInfo_Obfuscated = 1, + SourceCodeLocationInfo_Filename = 2, + SourceCodeLocationInfo_None = 3, +}; + String linker_choices[Linker_COUNT] = { str_lit("default"), str_lit("lld"), @@ -512,7 +519,7 @@ struct BuildContext { bool dynamic_map_calls; - bool obfuscate_source_code_locations; + SourceCodeLocationInfo source_code_location_info; bool min_link_libs; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 14d54af68..aa9c8837d 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8760,23 +8760,52 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A String name = bd->name.string; if (name == "file") { String file = get_file_path_string(bd->token.pos.file_id); - if (build_context.obfuscate_source_code_locations) { + switch (build_context.source_code_location_info) { + case SourceCodeLocationInfo_Normal: + break; + case SourceCodeLocationInfo_Obfuscated: file = obfuscate_string(file, "F"); + break; + case SourceCodeLocationInfo_Filename: + file = last_path_element(file); + break; + case SourceCodeLocationInfo_None: + file = str_lit(""); + break; } o->type = t_untyped_string; o->value = exact_value_string(file); } else if (name == "directory") { String file = get_file_path_string(bd->token.pos.file_id); String path = dir_from_path(file); - if (build_context.obfuscate_source_code_locations) { + switch (build_context.source_code_location_info) { + case SourceCodeLocationInfo_Normal: + break; + case SourceCodeLocationInfo_Obfuscated: path = obfuscate_string(path, "D"); + break; + case SourceCodeLocationInfo_Filename: + path = last_path_element(path); + break; + case SourceCodeLocationInfo_None: + path = str_lit(""); + break; } o->type = t_untyped_string; o->value = exact_value_string(path); } else if (name == "line") { i32 line = bd->token.pos.line; - if (build_context.obfuscate_source_code_locations) { + switch (build_context.source_code_location_info) { + case SourceCodeLocationInfo_Normal: + break; + case SourceCodeLocationInfo_Obfuscated: line = obfuscate_i32(line); + break; + case SourceCodeLocationInfo_Filename: + break; + case SourceCodeLocationInfo_None: + line = 0; + break; } o->type = t_untyped_integer; o->value = exact_value_i64(line); @@ -8787,8 +8816,17 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A o->value = exact_value_string(str_lit("")); } else { String p = c->proc_name; - if (build_context.obfuscate_source_code_locations) { + switch (build_context.source_code_location_info) { + case SourceCodeLocationInfo_Normal: + break; + case SourceCodeLocationInfo_Obfuscated: p = obfuscate_string(p, "P"); + break; + case SourceCodeLocationInfo_Filename: + break; + case SourceCodeLocationInfo_None: + p = str_lit(""); + break; } o->type = t_untyped_string; o->value = exact_value_string(p); diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index e897ae282..c3112934e 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -292,12 +292,26 @@ gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String cons i32 line = pos.line; i32 column = pos.column; - if (build_context.obfuscate_source_code_locations) { + switch (build_context.source_code_location_info) { + case SourceCodeLocationInfo_Normal: + break; + case SourceCodeLocationInfo_Obfuscated: file = obfuscate_string(file, "F"); procedure = obfuscate_string(procedure, "P"); - line = obfuscate_i32(line); - column = obfuscate_i32(column); + line = obfuscate_i32(line); + column = obfuscate_i32(column); + break; + case SourceCodeLocationInfo_Filename: + file = last_path_element(file); + break; + case SourceCodeLocationInfo_None: + file = str_lit(""); + procedure = str_lit(""); + + line = 0; + column = 0; + break; } LLVMValueRef fields[4] = {}; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 5d6a55973..aaa9ffd4d 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -568,10 +568,22 @@ gb_internal void lb_set_file_line_col(lbProcedure *p, Array arr, TokenP i32 line = pos.line; i32 col = pos.column; - if (build_context.obfuscate_source_code_locations) { + switch (build_context.source_code_location_info) { + case SourceCodeLocationInfo_Normal: + break; + case SourceCodeLocationInfo_Obfuscated: file = obfuscate_string(file, "F"); line = obfuscate_i32(line); col = obfuscate_i32(col); + break; + case SourceCodeLocationInfo_Filename: + file = last_path_element(file); + break; + case SourceCodeLocationInfo_None: + file = str_lit(""); + line = 0; + col = 0; + break; } arr[0] = lb_find_or_add_entity_string(p->module, file, false); diff --git a/src/main.cpp b/src/main.cpp index d7fb66f99..f988aff6a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -370,6 +370,7 @@ enum BuildFlagKind { BuildFlag_NoRTTI, BuildFlag_DynamicMapCalls, BuildFlag_ObfuscateSourceCodeLocations, + BuildFlag_SourceCodeLocations, BuildFlag_Compact, BuildFlag_GlobalDefinitions, @@ -594,6 +595,7 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_DynamicMapCalls, str_lit("dynamic-map-calls"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ObfuscateSourceCodeLocations, str_lit("obfuscate-source-code-locations"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_SourceCodeLocations, str_lit("source-code-locations"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_Short, str_lit("short"), BuildFlagParam_None, Command_doc); add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc | Command_test | Command_build); @@ -1422,7 +1424,23 @@ gb_internal bool parse_build_flags(Array args) { break; case BuildFlag_ObfuscateSourceCodeLocations: - build_context.obfuscate_source_code_locations = true; + gb_printf_err("'-obfuscate-source-code-locations' is now deprecated in favor of '-source-code-locations:obfuscated'\n"); + build_context.source_code_location_info = SourceCodeLocationInfo_Obfuscated; + break; + + case BuildFlag_SourceCodeLocations: + if (str_eq_ignore_case(value.value_string, str_lit("normal"))) { + build_context.source_code_location_info = SourceCodeLocationInfo_Normal; + } else if (str_eq_ignore_case(value.value_string, str_lit("obfuscated"))) { + build_context.source_code_location_info = SourceCodeLocationInfo_Obfuscated; + } else if (str_eq_ignore_case(value.value_string, str_lit("filename"))) { + build_context.source_code_location_info = SourceCodeLocationInfo_Filename; + } else if (str_eq_ignore_case(value.value_string, str_lit("none"))) { + build_context.source_code_location_info = SourceCodeLocationInfo_None; + } else { + gb_printf_err("-source-code-locations: options are 'normal', 'obfuscated', 'filename', and 'none'\n"); + bad_flags = true; + } break; case BuildFlag_DefaultToNilAllocator: @@ -2669,8 +2687,14 @@ gb_internal int print_show_help(String const arg0, String command, String option } - if (print_flag("-obfuscate-source-code-locations")) { - print_usage_line(2, "Obfuscate the file and procedure strings, and line and column numbers, stored with a 'runtime.Source_Code_Location' value."); + if (print_flag("-source-code-locations:")) { + print_usage_line(2, "Processes the file and procedure strings, and line and column numbers, stored with a 'runtime.Source_Code_Location' value."); + print_usage_line(2, "Available options:"); + print_usage_line(3, "-source-code-locations:normal"); + print_usage_line(3, "-source-code-locations:obfuscated"); + print_usage_line(3, "-source-code-locations:filename"); + print_usage_line(3, "-source-code-locations:none"); + print_usage_line(2, "The default is -source-code-locations:normal."); } -- cgit v1.2.3 From 9a87611609495f95429a8f4630243e62320e7f81 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Sun, 13 Jul 2025 15:39:41 -0400 Subject: Fix leftover poor indentation for objc_ivars MPSCQueue --- src/llvm_backend.hpp | 2 +- src/llvm_backend_general.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index fd6f50dcd..fef6e754d 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -242,7 +242,7 @@ struct lbGenerator : LinkerData { MPSCQueue entities_to_correct_linkage; MPSCQueue objc_selectors; MPSCQueue objc_classes; - MPSCQueue objc_ivars; + MPSCQueue objc_ivars; MPSCQueue raddebug_section_strings; }; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index aaa9ffd4d..64ea58578 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -174,9 +174,9 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { mpsc_init(&gen->entities_to_correct_linkage, heap_allocator()); mpsc_init(&gen->objc_selectors, heap_allocator()); mpsc_init(&gen->objc_classes, heap_allocator()); - mpsc_init(&gen->objc_ivars, heap_allocator()); + mpsc_init(&gen->objc_ivars, heap_allocator()); mpsc_init(&gen->raddebug_section_strings, heap_allocator()); - + return true; } -- cgit v1.2.3 From 20318e7a3b3ce4e836f70be8305048e0482ab94a Mon Sep 17 00:00:00 2001 From: Laytan Date: Fri, 1 Aug 2025 19:19:23 +0200 Subject: concrete types to make llvm 14 happy Fixes #5463 Fixes #5244 Fixes #5435 --- src/llvm_backend_general.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 64ea58578..3ce0c725f 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2584,16 +2584,19 @@ general_end:; if (src_size > dst_size) { GB_ASSERT(p->decl_block != p->curr_block); // NOTE(laytan): src is bigger than dst, need to memcpy the part of src we want. + + LLVMTypeRef llvm_src_type = LLVMPointerType(src_type, 0); + LLVMTypeRef llvm_dst_type = LLVMPointerType(dst_type, 0); LLVMValueRef val_ptr; if (LLVMIsALoadInst(val)) { val_ptr = LLVMGetOperand(val, 0); } else if (LLVMIsAAllocaInst(val)) { - val_ptr = LLVMBuildPointerCast(p->builder, val, LLVMPointerType(src_type, 0), ""); + val_ptr = LLVMBuildPointerCast(p->builder, val, llvm_src_type, ""); } else { // NOTE(laytan): we need a pointer to memcpy from. LLVMValueRef val_copy = llvm_alloca(p, src_type, src_align); - val_ptr = LLVMBuildPointerCast(p->builder, val_copy, LLVMPointerType(src_type, 0), ""); + val_ptr = LLVMBuildPointerCast(p->builder, val_copy, llvm_src_type, ""); LLVMBuildStore(p->builder, val, val_ptr); } @@ -2601,11 +2604,11 @@ general_end:; max_align = gb_max(max_align, 16); LLVMValueRef ptr = llvm_alloca(p, dst_type, max_align); - LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(dst_type, 0), ""); + LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, llvm_dst_type, ""); LLVMTypeRef types[3] = { - lb_type(p->module, t_rawptr), - lb_type(p->module, t_rawptr), + llvm_dst_type, + llvm_src_type, lb_type(p->module, t_int) }; -- cgit v1.2.3 From 2561427dd396a69cd49eb02c0814c4e8e8b3a08f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 11:00:15 +0100 Subject: Add `string16` and `cstring16` (UTF-16 based strings) --- base/runtime/core.odin | 13 +++- base/runtime/core_builtin.odin | 28 ++++++- base/runtime/internal.odin | 87 +++++++++++++++++++++ core/fmt/fmt.odin | 76 ++++++++++++++++++ core/io/io.odin | 24 ++++++ core/io/util.odin | 27 +++++++ core/unicode/utf16/utf16.odin | 20 +++++ src/build_settings.cpp | 8 +- src/cached.cpp | 2 +- src/check_builtin.cpp | 9 ++- src/check_decl.cpp | 6 ++ src/check_expr.cpp | 88 +++++++++++++++++++++ src/checker.cpp | 16 ++-- src/common.cpp | 2 +- src/exact_value.cpp | 52 ++++++++++++- src/llvm_backend_expr.cpp | 72 +++++++++++++++++ src/llvm_backend_general.cpp | 31 ++++++++ src/llvm_backend_proc.cpp | 9 +++ src/llvm_backend_type.cpp | 28 ++++++- src/llvm_backend_utility.cpp | 12 +++ src/main.cpp | 8 +- src/microsoft_craziness.h | 4 +- src/path.cpp | 8 +- src/string.cpp | 172 +++++++++++++++++++++++++++++++++++++---- src/types.cpp | 133 +++++++++++++++++++++++++++---- 25 files changed, 873 insertions(+), 62 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/base/runtime/core.odin b/base/runtime/core.odin index baecb4146..fe40427ff 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -73,7 +73,7 @@ Type_Info_Rune :: struct {} Type_Info_Float :: struct {endianness: Platform_Endianness} Type_Info_Complex :: struct {} Type_Info_Quaternion :: struct {} -Type_Info_String :: struct {is_cstring: bool} +Type_Info_String :: struct {is_cstring: bool, is_utf16: bool} Type_Info_Boolean :: struct {} Type_Info_Any :: struct {} Type_Info_Type_Id :: struct {} @@ -397,6 +397,11 @@ Raw_String :: struct { len: int, } +Raw_String16 :: struct { + data: [^]u16, + len: int, +} + Raw_Slice :: struct { data: rawptr, len: int, @@ -450,6 +455,12 @@ Raw_Cstring :: struct { } #assert(size_of(Raw_Cstring) == size_of(cstring)) +Raw_Cstring16 :: struct { + data: [^]u16, +} +#assert(size_of(Raw_Cstring16) == size_of(cstring16)) + + Raw_Soa_Pointer :: struct { data: rawptr, index: int, diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index e2ba14f3a..09118998c 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -86,11 +86,26 @@ copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int } return n } + +// `copy_from_string16` is a built-in procedure that copies elements from a source string `src` to a destination slice `dst`. +// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum +// of len(src) and len(dst). +// +// Prefer the procedure group `copy`. +@builtin +copy_from_string16 :: proc "contextless" (dst: $T/[]$E/u16, src: $S/string16) -> int { + n := min(len(dst), len(src)) + if n > 0 { + intrinsics.mem_copy(raw_data(dst), raw_data(src), n*size_of(u16)) + } + return n +} + // `copy` is a built-in procedure that copies elements from a source slice/string `src` to a destination slice `dst`. // The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum // of len(src) and len(dst). @builtin -copy :: proc{copy_slice, copy_from_string} +copy :: proc{copy_slice, copy_from_string, copy_from_string16} @@ -285,6 +300,15 @@ delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error } +@builtin +delete_string16 :: proc(str: string16, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { + return mem_free_with_size(raw_data(str), len(str)*size_of(u16), allocator, loc) +} +@builtin +delete_cstring16 :: proc(str: cstring16, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { + return mem_free((^u16)(str), allocator, loc) +} + // `delete` will try to free the underlying data of the passed built-in data structure (string, cstring, dynamic array, slice, or map), with the given `allocator` if the allocator supports this operation. // // Note: Prefer `delete` over the specific `delete_*` procedures where possible. @@ -297,6 +321,8 @@ delete :: proc{ delete_map, delete_soa_slice, delete_soa_dynamic_array, + delete_string16, + delete_cstring16, } diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index 907b187f1..660af58ab 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -493,12 +493,40 @@ string_cmp :: proc "contextless" (a, b: string) -> int { return ret } + +string16_eq :: proc "contextless" (lhs, rhs: string16) -> bool { + x := transmute(Raw_String16)lhs + y := transmute(Raw_String16)rhs + if x.len != y.len { + return false + } + return #force_inline memory_equal(x.data, y.data, x.len*size_of(u16)) +} + +string16_cmp :: proc "contextless" (a, b: string16) -> int { + x := transmute(Raw_String16)a + y := transmute(Raw_String16)b + + ret := memory_compare(x.data, y.data, min(x.len, y.len)*size_of(u16)) + if ret == 0 && x.len != y.len { + return -1 if x.len < y.len else +1 + } + return ret +} + string_ne :: #force_inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b) } string_lt :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) < 0 } string_gt :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) > 0 } string_le :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) <= 0 } string_ge :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) >= 0 } +string16_ne :: #force_inline proc "contextless" (a, b: string16) -> bool { return !string16_eq(a, b) } +string16_lt :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) < 0 } +string16_gt :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) > 0 } +string16_le :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) <= 0 } +string16_ge :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) >= 0 } + + cstring_len :: proc "contextless" (s: cstring) -> int { p0 := uintptr((^byte)(s)) p := p0 @@ -508,6 +536,16 @@ cstring_len :: proc "contextless" (s: cstring) -> int { return int(p - p0) } +cstring16_len :: proc "contextless" (s: cstring16) -> int { + p := ([^]u16)(s) + n := 0 + for p != nil && p[0] != 0 { + p = p[1:] + n += 1 + } + return n +} + cstring_to_string :: proc "contextless" (s: cstring) -> string { if s == nil { return "" @@ -517,6 +555,15 @@ cstring_to_string :: proc "contextless" (s: cstring) -> string { return transmute(string)Raw_String{ptr, n} } +cstring16_to_string16 :: proc "contextless" (s: cstring16) -> string16 { + if s == nil { + return "" + } + ptr := (^u16)(s) + n := cstring16_len(s) + return transmute(string16)Raw_String16{ptr, n} +} + cstring_eq :: proc "contextless" (lhs, rhs: cstring) -> bool { x := ([^]byte)(lhs) @@ -559,6 +606,46 @@ cstring_gt :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_le :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) <= 0 } cstring_ge :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) >= 0 } +cstring16_eq :: proc "contextless" (lhs, rhs: cstring16) -> bool { + x := ([^]u16)(lhs) + y := ([^]u16)(rhs) + if x == y { + return true + } + if (x == nil) ~ (y == nil) { + return false + } + xn := cstring16_len(lhs) + yn := cstring16_len(rhs) + if xn != yn { + return false + } + return #force_inline memory_equal(x, y, xn*size_of(u16)) +} + +cstring16_cmp :: proc "contextless" (lhs, rhs: cstring16) -> int { + x := ([^]u16)(lhs) + y := ([^]u16)(rhs) + if x == y { + return 0 + } + if (x == nil) ~ (y == nil) { + return -1 if x == nil else +1 + } + xn := cstring16_len(lhs) + yn := cstring16_len(rhs) + ret := memory_compare(x, y, min(xn, yn)*size_of(u16)) + if ret == 0 && xn != yn { + return -1 if xn < yn else +1 + } + return ret +} + +cstring16_ne :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return !cstring16_eq(a, b) } +cstring16_lt :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) < 0 } +cstring16_gt :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) > 0 } +cstring16_le :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) <= 0 } +cstring16_ge :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) >= 0 } complex32_eq :: #force_inline proc "contextless" (a, b: complex32) -> bool { return real(a) == real(b) && imag(a) == imag(b) } complex32_ne :: #force_inline proc "contextless" (a, b: complex32) -> bool { return real(a) != real(b) || imag(a) != imag(b) } diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 0f6470cca..7fe6287d4 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1551,6 +1551,79 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) { fmt_cstring :: proc(fi: ^Info, s: cstring, verb: rune) { fmt_string(fi, string(s), verb) } + +// Formats a string UTF-16 with a specific format. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - s: The string to format. +// - verb: The format specifier character (e.g. 's', 'v', 'q', 'x', 'X'). +// +fmt_string16 :: proc(fi: ^Info, s: string16, verb: rune) { + s, verb := s, verb + if ol, ok := fi.optional_len.?; ok { + s = s[:clamp(ol, 0, len(s))] + } + if !fi.in_bad && fi.record_level > 0 && verb == 'v' { + verb = 'q' + } + + switch verb { + case 's', 'v': + if fi.width_set { + if fi.width > len(s) { + if fi.minus { + io.write_string16(fi.writer, s, &fi.n) + } + + for _ in 0.. 0 && space { + io.write_byte(fi.writer, ' ', &fi.n) + } + char_set := __DIGITS_UPPER + if verb == 'x' { + char_set = __DIGITS_LOWER + } + _fmt_int(fi, u64(s[i]), 16, false, bit_size=16, digits=char_set) + } + + case: + fmt_bad_verb(fi, verb) + } +} +// Formats a C-style UTF-16 string with a specific format. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - s: The C-style string to format. +// - verb: The format specifier character (Ref fmt_string). +// +fmt_cstring16 :: proc(fi: ^Info, s: cstring16, verb: rune) { + fmt_string16(fi, string16(s), verb) +} + // Formats a raw pointer with a specific format. // // Inputs: @@ -3210,6 +3283,9 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { case string: fmt_string(fi, a, verb) case cstring: fmt_cstring(fi, a, verb) + case string16: fmt_string16(fi, a, verb) + case cstring16: fmt_cstring16(fi, a, verb) + case typeid: reflect.write_typeid(fi.writer, a, &fi.n) case i16le: fmt_int(fi, u64(a), true, 16, verb) diff --git a/core/io/io.odin b/core/io/io.odin index c2b44cbdb..5431519bf 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -5,6 +5,7 @@ package io import "base:intrinsics" import "core:unicode/utf8" +import "core:unicode/utf16" // Seek whence values Seek_From :: enum { @@ -314,6 +315,29 @@ write_string :: proc(s: Writer, str: string, n_written: ^int = nil) -> (n: int, return write(s, transmute([]byte)str, n_written) } +// write_string16 writes the contents of the string16 s to w reencoded as utf-8 +write_string16 :: proc(s: Writer, str: string16, n_written: ^int = nil) -> (n: int, err: Error) { + for i := 0; i < len(str); i += 1 { + r := rune(utf16.REPLACEMENT_CHAR) + + switch c := str[i]; { + case c < utf16._surr1, utf16._surr3 <= c: + r = rune(c) + case utf16._surr1 <= c && c < utf16._surr2 && i+1 < len(str) && + utf16._surr2 <= str[i+1] && str[i+1] < utf16._surr3: + r = utf16.decode_surrogate_pair(rune(c), rune(str[i+1])) + i += 1 + } + + w, err := write_rune(s, r, n_written) + n += w + if err != nil { + return + } + } + return +} + // write_rune writes a UTF-8 encoded rune to w. write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err: Error) { defer if err == nil && n_written != nil { diff --git a/core/io/util.odin b/core/io/util.odin index fa98e007b..72983523a 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -264,6 +264,33 @@ write_quoted_string :: proc(w: Writer, str: string, quote: byte = '"', n_written return } +write_quoted_string16 :: proc(w: Writer, str: string16, quote: byte = '"', n_written: ^int = nil, for_json := false) -> (n: int, err: Error) { + defer if n_written != nil { + n_written^ += n + } + write_byte(w, quote, &n) or_return + for width, s := 0, str; len(s) > 0; s = s[width:] { + r := rune(s[0]) + width = 1 + if r >= utf8.RUNE_SELF { + r, width = utf16.decode_rune_in_string(s) + } + if width == 1 && r == utf8.RUNE_ERROR { + write_byte(w, '\\', &n) or_return + write_byte(w, 'x', &n) or_return + write_byte(w, DIGITS_LOWER[s[0]>>4], &n) or_return + write_byte(w, DIGITS_LOWER[s[0]&0xf], &n) or_return + continue + } + + n_wrapper(write_escaped_rune(w, r, quote, false, nil, for_json), &n) or_return + + } + write_byte(w, quote, &n) or_return + return +} + + // writer append a quoted rune into the byte buffer, return the written size write_quoted_rune :: proc(w: Writer, r: rune) -> (n: int) { _write_byte :: #force_inline proc(w: Writer, c: byte) -> int { diff --git a/core/unicode/utf16/utf16.odin b/core/unicode/utf16/utf16.odin index e2bcf7f68..9a8cfe438 100644 --- a/core/unicode/utf16/utf16.odin +++ b/core/unicode/utf16/utf16.odin @@ -106,6 +106,26 @@ decode :: proc(d: []rune, s: []u16) -> (n: int) { return } +decode_rune_in_string :: proc(s: string16) -> (r: rune, width: int) { + r = rune(REPLACEMENT_CHAR) + n := len(s) + if n < 1 { + return + } + width = 1 + + + switch c := s[0]; { + case c < _surr1, _surr3 <= c: + r = rune(c) + case _surr1 <= c && c < _surr2 && 1 < len(s) && + _surr2 <= s[1] && s[1] < _surr3: + r = decode_surrogate_pair(rune(c), rune(s[1])) + width += 1 + } + return +} + rune_count :: proc(s: []u16) -> (n: int) { for i := 0; i < len(s); i += 1 { c := s[i] diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 46a4f9ae5..40bbe41e5 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1089,7 +1089,7 @@ gb_internal String internal_odin_root_dir(void) { text = gb_alloc_array(permanent_allocator(), wchar_t, len+1); GetModuleFileNameW(nullptr, text, cast(int)len); - path = string16_to_string(heap_allocator(), make_string16(text, len)); + path = string16_to_string(heap_allocator(), make_string16(cast(u16 *)text, len)); for (i = path.len-1; i >= 0; i--) { u8 c = path[i]; @@ -1387,14 +1387,14 @@ gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_) { mutex_lock(&fullpath_mutex); - len = GetFullPathNameW(&string16[0], 0, nullptr, nullptr); + 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); - GetFullPathNameW(&string16[0], len, text, nullptr); + GetFullPathNameW(cast(wchar_t *)&string16[0], len, text, nullptr); mutex_unlock(&fullpath_mutex); text[len] = 0; - result = string16_to_string(a, make_string16(text, len)); + result = string16_to_string(a, make_string16(cast(u16 *)text, len)); result = string_trim_whitespace(result); // Replace Windows style separators diff --git a/src/cached.cpp b/src/cached.cpp index efdadce7b..61b5d01b4 100644 --- a/src/cached.cpp +++ b/src/cached.cpp @@ -231,7 +231,7 @@ Array cache_gather_envs() { wchar_t *curr_string = strings; while (curr_string && *curr_string) { - String16 wstr = make_string16_c(curr_string); + String16 wstr = make_string16_c(cast(u16 *)curr_string); curr_string += wstr.len+1; String str = string16_to_string(temporary_allocator(), wstr); if (string_starts_with(str, str_lit("CURR_DATE_TIME="))) { diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 974224ed2..d36cf4520 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2327,6 +2327,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (is_type_string(op_type) && id == BuiltinProc_len) { if (operand->mode == Addressing_Constant) { mode = Addressing_Constant; + + GB_ASSERT_MSG(!is_type_string16(op_type), "TODO(bill): constant utf-16 string len"); + String str = operand->value.value_string; value = exact_value_i64(str.len); type = t_untyped_integer; @@ -2334,6 +2337,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As mode = Addressing_Value; if (is_type_cstring(op_type)) { add_package_dependency(c, "runtime", "cstring_len"); + } else if (is_type_cstring16(op_type)) { + add_package_dependency(c, "runtime", "cstring16_len"); } } } else if (is_type_array(op_type)) { @@ -4683,7 +4688,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; case Type_Basic: if (t->Basic.kind == Basic_string) { - operand->type = alloc_type_multi_pointer(t_u8); + operand->type = t_u8_multi_ptr; + } else if (t->Basic.kind == Basic_string16) { + operand->type = t_u16_multi_ptr; } break; case Type_Pointer: diff --git a/src/check_decl.cpp b/src/check_decl.cpp index dd4c09e85..af46ee40e 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -815,6 +815,12 @@ gb_internal bool signature_parameter_similar_enough(Type *x, Type *y) { if (sig_compare(is_type_cstring, is_type_u8_multi_ptr, x, y)) { return true; } + if (sig_compare(is_type_cstring16, is_type_u16_ptr, x, y)) { + return true; + } + if (sig_compare(is_type_cstring16, is_type_u16_multi_ptr, x, y)) { + return true; + } if (sig_compare(is_type_uintptr, is_type_rawptr, x, y)) { return true; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6723a7580..57073e22f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2862,6 +2862,14 @@ gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t add_package_dependency(c, "runtime", "string_eq"); add_package_dependency(c, "runtime", "string_ne"); break; + case Basic_cstring16: + add_package_dependency(c, "runtime", "cstring16_eq"); + add_package_dependency(c, "runtime", "cstring16_ne"); + break; + case Basic_string16: + add_package_dependency(c, "runtime", "string16_eq"); + add_package_dependency(c, "runtime", "string16_ne"); + break; } break; case Type_Struct: @@ -3035,6 +3043,24 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper case Token_LtEq: add_package_dependency(c, "runtime", "cstring_le"); break; case Token_GtEq: add_package_dependency(c, "runtime", "cstring_gt"); break; } + } else if (is_type_cstring16(x->type) && is_type_cstring16(y->type)) { + switch (op) { + case Token_CmpEq: add_package_dependency(c, "runtime", "cstring16_eq"); break; + case Token_NotEq: add_package_dependency(c, "runtime", "cstring16_ne"); break; + case Token_Lt: add_package_dependency(c, "runtime", "cstring16_lt"); break; + case Token_Gt: add_package_dependency(c, "runtime", "cstring16_gt"); break; + case Token_LtEq: add_package_dependency(c, "runtime", "cstring16_le"); break; + case Token_GtEq: add_package_dependency(c, "runtime", "cstring16_gt"); break; + } + } else if (is_type_string16(x->type) || is_type_string16(y->type)) { + switch (op) { + case Token_CmpEq: add_package_dependency(c, "runtime", "string16_eq"); break; + case Token_NotEq: add_package_dependency(c, "runtime", "string16_ne"); break; + case Token_Lt: add_package_dependency(c, "runtime", "string16_lt"); break; + case Token_Gt: add_package_dependency(c, "runtime", "string16_gt"); break; + case Token_LtEq: add_package_dependency(c, "runtime", "string16_le"); break; + case Token_GtEq: add_package_dependency(c, "runtime", "string16_gt"); break; + } } else if (is_type_string(x->type) || is_type_string(y->type)) { switch (op) { case Token_CmpEq: add_package_dependency(c, "runtime", "string_eq"); break; @@ -3340,6 +3366,11 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type return true; } + // []u16 <-> string16 (not cstring16) + if (is_type_u16_slice(src) && (is_type_string16(dst) && !is_type_cstring16(dst))) { + return true; + } + // cstring -> string if (are_types_identical(src, t_cstring) && are_types_identical(dst, t_string)) { if (operand->mode != Addressing_Constant) { @@ -3347,6 +3378,14 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type } return true; } + // cstring16 -> string16 + if (are_types_identical(src, t_cstring16) && are_types_identical(dst, t_string16)) { + if (operand->mode != Addressing_Constant) { + add_package_dependency(c, "runtime", "cstring16_to_string16"); + } + return true; + } + // cstring -> ^u8 if (are_types_identical(src, t_cstring) && is_type_u8_ptr(dst)) { return !is_constant; @@ -3372,6 +3411,34 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type if (is_type_rawptr(src) && are_types_identical(dst, t_cstring)) { return !is_constant; } + + // cstring -> ^u16 + if (are_types_identical(src, t_cstring16) && is_type_u16_ptr(dst)) { + return !is_constant; + } + // cstring -> [^]u16 + if (are_types_identical(src, t_cstring16) && is_type_u16_multi_ptr(dst)) { + return !is_constant; + } + // cstring -> rawptr + if (are_types_identical(src, t_cstring16) && is_type_rawptr(dst)) { + return !is_constant; + } + + + // ^u16 -> cstring16 + if (is_type_u16_ptr(src) && are_types_identical(dst, t_cstring16)) { + return !is_constant; + } + // [^]u16 -> cstring + if (is_type_u16_multi_ptr(src) && are_types_identical(dst, t_cstring16)) { + return !is_constant; + } + // rawptr -> cstring16 + if (is_type_rawptr(src) && are_types_identical(dst, t_cstring16)) { + return !is_constant; + } + // proc <-> proc if (is_type_proc(src) && is_type_proc(dst)) { if (is_type_polymorphic(dst)) { @@ -4558,6 +4625,8 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar // target_type = t_untyped_nil; } else if (is_type_cstring(target_type)) { // target_type = t_untyped_nil; + } else if (is_type_cstring16(target_type)) { + // target_type = t_untyped_nil; } else if (!type_has_nil(target_type)) { operand->mode = Addressing_Invalid; convert_untyped_error(c, operand, target_type); @@ -8226,6 +8295,7 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 case Type_Basic: if (t->Basic.kind == Basic_string) { if (o->mode == Addressing_Constant) { + GB_ASSERT(o->value.kind == ExactValue_String); *max_count = o->value.value_string.len; } if (o->mode != Addressing_Constant) { @@ -8233,6 +8303,16 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 } o->type = t_u8; return true; + } else if (t->Basic.kind == Basic_string16) { + if (o->mode == Addressing_Constant) { + GB_ASSERT(o->value.kind == ExactValue_String16); + *max_count = o->value.value_string16.len; + } + if (o->mode != Addressing_Constant) { + o->mode = Addressing_Value; + } + o->type = t_u16; + return true; } else if (t->Basic.kind == Basic_UntypedString) { if (o->mode == Addressing_Constant) { *max_count = o->value.value_string.len; @@ -10879,9 +10959,17 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) { valid = true; if (o->mode == Addressing_Constant) { + GB_ASSERT(o->value.kind == ExactValue_String); max_count = o->value.value_string.len; } o->type = type_deref(o->type); + } else if (t->Basic.kind == Basic_string16) { + valid = true; + if (o->mode == Addressing_Constant) { + GB_ASSERT(o->value.kind == ExactValue_String16); + max_count = o->value.value_string16.len; + } + o->type = type_deref(o->type); } break; diff --git a/src/checker.cpp b/src/checker.cpp index a1d8f98d7..20da5b19b 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1363,13 +1363,15 @@ gb_internal void init_universal(void) { } - t_u8_ptr = alloc_type_pointer(t_u8); - t_u8_multi_ptr = alloc_type_multi_pointer(t_u8); - t_int_ptr = alloc_type_pointer(t_int); - t_i64_ptr = alloc_type_pointer(t_i64); - t_f64_ptr = alloc_type_pointer(t_f64); - t_u8_slice = alloc_type_slice(t_u8); - t_string_slice = alloc_type_slice(t_string); + t_u8_ptr = alloc_type_pointer(t_u8); + t_u8_multi_ptr = alloc_type_multi_pointer(t_u8); + t_u16_ptr = alloc_type_pointer(t_u16); + t_u16_multi_ptr = alloc_type_multi_pointer(t_u16); + t_int_ptr = alloc_type_pointer(t_int); + t_i64_ptr = alloc_type_pointer(t_i64); + t_f64_ptr = alloc_type_pointer(t_f64); + t_u8_slice = alloc_type_slice(t_u8); + t_string_slice = alloc_type_slice(t_string); // intrinsics types for objective-c stuff { diff --git a/src/common.cpp b/src/common.cpp index ad1e5a851..b3761fc36 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -669,7 +669,7 @@ gb_internal gb_inline f64 gb_sqrt(f64 x) { gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) { u32 i, j; - u32 len = cast(u32)string16_len(cmd_line); + u32 len = cast(u32)string16_len(cast(u16 *)cmd_line); i = ((len+2)/2)*gb_size_of(void *) + gb_size_of(void *); wchar_t **argv = cast(wchar_t **)GlobalAlloc(GMEM_FIXED, i + (len+2)*gb_size_of(wchar_t)); diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 37751c8f1..f2aed84c2 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -29,6 +29,7 @@ enum ExactValueKind { ExactValue_Compound = 8, ExactValue_Procedure = 9, ExactValue_Typeid = 10, + ExactValue_String16 = 11, ExactValue_Count, }; @@ -46,6 +47,7 @@ struct ExactValue { Ast * value_compound; Ast * value_procedure; Type * value_typeid; + String16 value_string16; }; }; @@ -66,6 +68,9 @@ gb_internal uintptr hash_exact_value(ExactValue v) { case ExactValue_String: res = gb_fnv32a(v.value_string.text, v.value_string.len); break; + case ExactValue_String16: + res = gb_fnv32a(v.value_string.text, v.value_string.len*gb_size_of(u16)); + break; case ExactValue_Integer: { u32 key = gb_fnv32a(v.value_integer.dp, gb_size_of(*v.value_integer.dp) * v.value_integer.used); @@ -118,6 +123,11 @@ gb_internal ExactValue exact_value_string(String string) { result.value_string = string; return result; } +gb_internal ExactValue exact_value_string16(String16 string) { + ExactValue result = {ExactValue_String16}; + result.value_string16 = string; + return result; +} gb_internal ExactValue exact_value_i64(i64 i) { ExactValue result = {ExactValue_Integer}; @@ -656,6 +666,7 @@ gb_internal i32 exact_value_order(ExactValue const &v) { return 0; case ExactValue_Bool: case ExactValue_String: + case ExactValue_String16: return 1; case ExactValue_Integer: return 2; @@ -689,6 +700,7 @@ gb_internal void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Bool: case ExactValue_String: + case ExactValue_String16: case ExactValue_Quaternion: case ExactValue_Pointer: case ExactValue_Compound: @@ -891,7 +903,18 @@ gb_internal ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, E gb_memmove(data, sx.text, sx.len); gb_memmove(data+sx.len, sy.text, sy.len); return exact_value_string(make_string(data, len)); - break; + } + case ExactValue_String16: { + if (op != Token_Add) goto error; + + // NOTE(bill): How do you minimize this over allocation? + String sx = x.value_string; + String sy = y.value_string; + isize len = sx.len+sy.len; + u16 *data = gb_alloc_array(permanent_allocator(), u16, len); + gb_memmove(data, sx.text, sx.len*gb_size_of(u16)); + gb_memmove(data+sx.len, sy.text, sy.len*gb_size_of(u16)); + return exact_value_string16(make_string16(data, len)); } } @@ -994,6 +1017,19 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) } break; } + case ExactValue_String16: { + String16 a = x.value_string16; + String16 b = y.value_string16; + switch (op) { + case Token_CmpEq: return a == b; + case Token_NotEq: return a != b; + case Token_Lt: return a < b; + case Token_LtEq: return a <= b; + case Token_Gt: return a > b; + case Token_GtEq: return a >= b; + } + break; + } case ExactValue_Pointer: { switch (op) { @@ -1050,6 +1086,20 @@ gb_internal gbString write_exact_value_to_string(gbString str, ExactValue const gb_free(heap_allocator(), s.text); return str; } + case ExactValue_String16: { + String s = quote_to_ascii(heap_allocator(), v.value_string16); + string_limit = gb_max(string_limit, 36); + if (s.len <= string_limit) { + str = gb_string_append_length(str, s.text, s.len); + } else { + isize n = string_limit/5; + str = gb_string_append_length(str, s.text, n); + str = gb_string_append_fmt(str, "\"..%lld chars..\"", s.len-(2*n)); + str = gb_string_append_length(str, s.text+s.len-n, n); + } + gb_free(heap_allocator(), s.text); + return str; + } case ExactValue_Integer: { String s = big_int_to_string(heap_allocator(), &v.value_integer); str = gb_string_append_length(str, s.text, s.len); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 74aea82f1..fbf0dea11 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1656,6 +1656,8 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { res.type = t; res.value = llvm_cstring(m, str); return res; + } else if (src->kind == Type_Basic && src->Basic.kind == Basic_string16 && dst->Basic.kind == Basic_cstring16) { + GB_PANIC("TODO(bill): UTF-16 string"); } // if (is_type_float(dst)) { // return value; @@ -1795,6 +1797,38 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { } + + if (is_type_cstring16(src) && is_type_u16_ptr(dst)) { + return lb_emit_transmute(p, value, dst); + } + if (is_type_u16_ptr(src) && is_type_cstring16(dst)) { + return lb_emit_transmute(p, value, dst); + } + if (is_type_cstring16(src) && is_type_u16_multi_ptr(dst)) { + return lb_emit_transmute(p, value, dst); + } + if (is_type_u8_multi_ptr(src) && is_type_cstring16(dst)) { + return lb_emit_transmute(p, value, dst); + } + if (is_type_cstring16(src) && is_type_rawptr(dst)) { + return lb_emit_transmute(p, value, dst); + } + if (is_type_rawptr(src) && is_type_cstring16(dst)) { + return lb_emit_transmute(p, value, dst); + } + + if (are_types_identical(src, t_cstring16) && are_types_identical(dst, t_string16)) { + TEMPORARY_ALLOCATOR_GUARD(); + + lbValue c = lb_emit_conv(p, value, t_cstring16); + auto args = array_make(temporary_allocator(), 1); + args[0] = c; + lbValue s = lb_emit_runtime_call(p, "cstring16_to_string16", args); + return lb_emit_conv(p, s, dst); + } + + + // integer -> boolean if (is_type_integer(src) && is_type_boolean(dst)) { lbValue res = {}; @@ -2296,6 +2330,14 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { return res; } + // []u16 <-> string16 + if (is_type_u16_slice(src) && is_type_string16(dst)) { + return lb_emit_transmute(p, value, t); + } + if (is_type_string16(src) && is_type_u16_slice(dst)) { + return lb_emit_transmute(p, value, t); + } + // []byte/[]u8 <-> string if (is_type_u8_slice(src) && is_type_string(dst)) { return lb_emit_transmute(p, value, t); @@ -2304,6 +2346,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { return lb_emit_transmute(p, value, t); } + if (is_type_array_like(dst)) { Type *elem = base_array_type(dst); isize index_count = cast(isize)get_array_type_count(dst); @@ -2483,6 +2526,12 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { if (is_type_untyped(src)) { + if (is_type_string(src) && is_type_string16(dst)) { + GB_PANIC("TODO(bill): UTF-16 string"); + lbAddr result = lb_add_local_generated(p, t, false); + lb_addr_store(p, result, value); + return lb_addr_load(p, result); + } if (is_type_string(src) && is_type_string(dst)) { lbAddr result = lb_add_local_generated(p, t, false); lb_addr_store(p, result, value); @@ -3056,6 +3105,13 @@ gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, res.value = LLVMBuildIsNotNull(p->builder, x.value, ""); } return res; + case Basic_cstring16: + if (op_kind == Token_CmpEq) { + res.value = LLVMBuildIsNull(p->builder, x.value, ""); + } else if (op_kind == Token_NotEq) { + res.value = LLVMBuildIsNotNull(p->builder, x.value, ""); + } + return res; case Basic_any: { // TODO(bill): is this correct behaviour for nil comparison for any? @@ -4432,6 +4488,22 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) { } case Type_Basic: { + if (is_type_string16(type)) { + GB_ASSERT_MSG(are_types_identical(type, t_string16), "got %s", type_to_string(type)); + lbValue len = lb_string_len(p, base); + if (high.value == nullptr) high = len; + + if (!no_indices) { + lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); + } + + lbValue elem = lb_emit_ptr_offset(p, lb_string_elem(p, base), low); + lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); + + lbAddr str = lb_add_local_generated(p, t_string16, false); + lb_fill_string(p, str, elem, new_len); + return str; + } GB_ASSERT_MSG(are_types_identical(type, t_string), "got %s", type_to_string(type)); lbValue len = lb_string_len(p, base); if (high.value == nullptr) high = len; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 3ce0c725f..d9771a75b 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1812,6 +1812,37 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { return type; } case Basic_cstring: return LLVMPointerType(LLVMInt8TypeInContext(ctx), 0); + + + case Basic_string16: + { + char const *name = "..string16"; + LLVMTypeRef type = LLVMGetTypeByName(m->mod, name); + if (type != nullptr) { + return type; + } + type = LLVMStructCreateNamed(ctx, name); + + if (build_context.metrics.ptr_size < build_context.metrics.int_size) { + GB_ASSERT(build_context.metrics.ptr_size == 4); + GB_ASSERT(build_context.metrics.int_size == 8); + LLVMTypeRef fields[3] = { + LLVMPointerType(lb_type(m, t_u16), 0), + lb_type(m, t_i32), + lb_type(m, t_int), + }; + LLVMStructSetBody(type, fields, 3, false); + } else { + LLVMTypeRef fields[2] = { + LLVMPointerType(lb_type(m, t_u16), 0), + lb_type(m, t_int), + }; + LLVMStructSetBody(type, fields, 2, false); + } + return type; + } + case Basic_cstring16: return LLVMPointerType(LLVMInt16TypeInContext(ctx), 0); + case Basic_any: { char const *name = "..any"; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index e63c92f6f..8f306b771 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2289,6 +2289,10 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu } if (is_type_cstring(t)) { return lb_cstring_len(p, v); + } else if (is_type_cstring16(t)) { + return lb_cstring16_len(p, v); + } else if (is_type_string16(t)) { + return lb_string_len(p, v); } else if (is_type_string(t)) { return lb_string_len(p, v); } else if (is_type_array(t)) { @@ -2728,6 +2732,11 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu res = lb_emit_conv(p, res, tv.type); } else if (t->Basic.kind == Basic_cstring) { res = lb_emit_conv(p, x, tv.type); + } else if (t->Basic.kind == Basic_string16) { + res = lb_string_elem(p, x); + res = lb_emit_conv(p, res, tv.type); + } else if (t->Basic.kind == Basic_cstring16) { + res = lb_emit_conv(p, x, tv.type); } break; case Type_Pointer: diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 43c5f0b40..a91d77fe5 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -531,7 +531,33 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ case Basic_cstring: { tag_type = t_type_info_string; - LLVMValueRef vals[1] = { + LLVMValueRef vals[2] = { + lb_const_bool(m, t_bool, true).value, + lb_const_bool(m, t_bool, false).value, + }; + + variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); + } + break; + + case Basic_string16: + { + tag_type = t_type_info_string; + LLVMValueRef vals[2] = { + lb_const_bool(m, t_bool, false).value, + lb_const_bool(m, t_bool, true).value, + }; + + variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); + } + break; + + + case Basic_cstring16: + { + tag_type = t_type_info_string; + LLVMValueRef vals[2] = { + lb_const_bool(m, t_bool, true).value, lb_const_bool(m, t_bool, true).value, }; diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 521553147..d4117b7ff 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1626,11 +1626,17 @@ gb_internal void lb_fill_string(lbProcedure *p, lbAddr const &string, lbValue ba gb_internal lbValue lb_string_elem(lbProcedure *p, lbValue string) { Type *t = base_type(string.type); + if (t->kind == Type_Basic && t->Basic.kind == Basic_string16) { + return lb_emit_struct_ev(p, string, 0); + } GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string); return lb_emit_struct_ev(p, string, 0); } gb_internal lbValue lb_string_len(lbProcedure *p, lbValue string) { Type *t = base_type(string.type); + if (t->kind == Type_Basic && t->Basic.kind == Basic_string16) { + return lb_emit_struct_ev(p, string, 1); + } GB_ASSERT_MSG(t->kind == Type_Basic && t->Basic.kind == Basic_string, "%s", type_to_string(t)); return lb_emit_struct_ev(p, string, 1); } @@ -1641,6 +1647,12 @@ gb_internal lbValue lb_cstring_len(lbProcedure *p, lbValue value) { args[0] = lb_emit_conv(p, value, t_cstring); return lb_emit_runtime_call(p, "cstring_len", args); } +gb_internal lbValue lb_cstring16_len(lbProcedure *p, lbValue value) { + GB_ASSERT(is_type_cstring16(value.type)); + auto args = array_make(permanent_allocator(), 1); + args[0] = lb_emit_conv(p, value, t_cstring16); + return lb_emit_runtime_call(p, "cstring16_len", args); +} gb_internal lbValue lb_array_elem(lbProcedure *p, lbValue array_ptr) { diff --git a/src/main.cpp b/src/main.cpp index 112d1208a..5a43e3c02 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -142,9 +142,9 @@ gb_internal i32 system_exec_command_line_app_internal(bool exit_on_err, char con } wcmd = string_to_string16(permanent_allocator(), make_string(cast(u8 *)cmd_line, cmd_len-1)); - if (CreateProcessW(nullptr, wcmd.text, - nullptr, nullptr, true, 0, nullptr, nullptr, - &start_info, &pi)) { + if (CreateProcessW(nullptr, cast(wchar_t *)wcmd.text, + nullptr, nullptr, true, 0, nullptr, nullptr, + &start_info, &pi)) { WaitForSingleObject(pi.hProcess, INFINITE); GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code); @@ -232,7 +232,7 @@ gb_internal Array setup_args(int argc, char const **argv) { wchar_t **wargv = command_line_to_wargv(GetCommandLineW(), &wargc); auto args = array_make(a, 0, wargc); for (isize i = 0; i < wargc; i++) { - wchar_t *warg = wargv[i]; + u16 *warg = cast(u16 *)wargv[i]; isize wlen = string16_len(warg); String16 wstr = make_string16(warg, wlen); String arg = string16_to_string(a, wstr); diff --git a/src/microsoft_craziness.h b/src/microsoft_craziness.h index b0fd22a23..933607a2a 100644 --- a/src/microsoft_craziness.h +++ b/src/microsoft_craziness.h @@ -59,7 +59,7 @@ struct Find_Result { }; gb_internal String mc_wstring_to_string(wchar_t const *str) { - return string16_to_string(mc_allocator, make_string16_c(str)); + return string16_to_string(mc_allocator, make_string16_c(cast(u16 *)str)); } gb_internal String16 mc_string_to_wstring(String str) { @@ -103,7 +103,7 @@ gb_internal HANDLE mc_find_first(String wildcard, MC_Find_Data *find_data) { String16 wildcard_wide = mc_string_to_wstring(wildcard); defer (mc_free(wildcard_wide)); - HANDLE handle = FindFirstFileW(wildcard_wide.text, &_find_data); + HANDLE handle = FindFirstFileW(cast(wchar_t *)wildcard_wide.text, &_find_data); if (handle == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE; find_data->file_attributes = _find_data.dwFileAttributes; diff --git a/src/path.cpp b/src/path.cpp index d5e982088..2b97a04df 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -130,7 +130,7 @@ gb_internal String directory_from_path(String const &s) { String16 wstr = string_to_string16(a, path); defer (gb_free(a, wstr.text)); - i32 attribs = GetFileAttributesW(wstr.text); + i32 attribs = GetFileAttributesW(cast(wchar_t *)wstr.text); if (attribs < 0) return false; return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0; @@ -360,7 +360,7 @@ gb_internal ReadDirectoryError read_directory(String path, Array *fi) defer (gb_free(a, wstr.text)); WIN32_FIND_DATAW file_data = {}; - HANDLE find_file = FindFirstFileW(wstr.text, &file_data); + HANDLE find_file = FindFirstFileW(cast(wchar_t *)wstr.text, &file_data); if (find_file == INVALID_HANDLE_VALUE) { return ReadDirectory_Unknown; } @@ -372,7 +372,7 @@ gb_internal ReadDirectoryError read_directory(String path, Array *fi) wchar_t *filename_w = file_data.cFileName; u64 size = cast(u64)file_data.nFileSizeLow; size |= (cast(u64)file_data.nFileSizeHigh) << 32; - String name = string16_to_string(a, make_string16_c(filename_w)); + String name = string16_to_string(a, make_string16_c(cast(u16 *)filename_w)); if (name == "." || name == "..") { gb_free(a, name.text); continue; @@ -494,7 +494,7 @@ gb_internal bool write_directory(String path) { #else gb_internal bool write_directory(String path) { String16 wstr = string_to_string16(heap_allocator(), path); - LPCWSTR wdirectory_name = wstr.text; + LPCWSTR wdirectory_name = cast(wchar_t *)wstr.text; HANDLE directory = CreateFileW(wdirectory_name, GENERIC_WRITE, diff --git a/src/string.cpp b/src/string.cpp index ae8d066b1..8405938f4 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -26,15 +26,14 @@ struct String_Iterator { // NOTE(bill): String16 is only used for Windows due to its file directories struct String16 { - wchar_t *text; - isize len; - wchar_t const &operator[](isize i) const { + u16 * text; + isize len; + u16 const &operator[](isize i) const { GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i); return text[i]; } }; - gb_internal gb_inline String make_string(u8 const *text, isize len) { String s; s.text = cast(u8 *)text; @@ -45,19 +44,19 @@ gb_internal gb_inline String make_string(u8 const *text, isize len) { return s; } - -gb_internal gb_inline String16 make_string16(wchar_t const *text, isize len) { +gb_internal gb_inline String16 make_string16(u16 const *text, isize len) { String16 s; - s.text = cast(wchar_t *)text; + s.text = cast(u16 *)text; s.len = len; return s; } -gb_internal isize string16_len(wchar_t const *s) { + +gb_internal isize string16_len(u16 const *s) { if (s == nullptr) { return 0; } - wchar_t const *p = s; + u16 const *p = s; while (*p) { p++; } @@ -69,7 +68,7 @@ gb_internal gb_inline String make_string_c(char const *text) { return make_string(cast(u8 *)cast(void *)text, gb_strlen(text)); } -gb_internal gb_inline String16 make_string16_c(wchar_t const *text) { +gb_internal gb_inline String16 make_string16_c(u16 const *text) { return make_string16(text, string16_len(text)); } @@ -145,6 +144,27 @@ gb_internal int string_compare(String const &a, String const &b) { return res; } + +gb_internal int string16_compare(String16 const &a, String16 const &b) { + if (a.text == b.text) { + return cast(int)(a.len - b.len); + } + if (a.text == nullptr) { + return -1; + } + if (b.text == nullptr) { + return +1; + } + + uintptr n = gb_min(a.len, b.len); + int res = memcmp(a.text, b.text, n*gb_size_of(u16)); + if (res == 0) { + res = cast(int)(a.len - b.len); + } + return res; +} + + gb_internal isize string_index_byte(String const &s, u8 x) { for (isize i = 0; i < s.len; i++) { if (s.text[i] == x) { @@ -182,6 +202,26 @@ template gb_internal bool operator >= (String const &a, char const (&b template <> bool operator == (String const &a, char const (&b)[1]) { return a.len == 0; } template <> bool operator != (String const &a, char const (&b)[1]) { return a.len != 0; } + +gb_internal gb_inline bool str_eq(String16 const &a, String16 const &b) { + if (a.len != b.len) return false; + if (a.len == 0) return true; + return memcmp(a.text, b.text, a.len) == 0; +} +gb_internal gb_inline bool str_ne(String16 const &a, String16 const &b) { return !str_eq(a, b); } +gb_internal gb_inline bool str_lt(String16 const &a, String16 const &b) { return string16_compare(a, b) < 0; } +gb_internal gb_inline bool str_gt(String16 const &a, String16 const &b) { return string16_compare(a, b) > 0; } +gb_internal gb_inline bool str_le(String16 const &a, String16 const &b) { return string16_compare(a, b) <= 0; } +gb_internal gb_inline bool str_ge(String16 const &a, String16 const &b) { return string16_compare(a, b) >= 0; } + +gb_internal gb_inline bool operator == (String16 const &a, String16 const &b) { return str_eq(a, b); } +gb_internal gb_inline bool operator != (String16 const &a, String16 const &b) { return str_ne(a, b); } +gb_internal gb_inline bool operator < (String16 const &a, String16 const &b) { return str_lt(a, b); } +gb_internal gb_inline bool operator > (String16 const &a, String16 const &b) { return str_gt(a, b); } +gb_internal gb_inline bool operator <= (String16 const &a, String16 const &b) { return str_le(a, b); } +gb_internal gb_inline bool operator >= (String16 const &a, String16 const &b) { return str_ge(a, b); } + + gb_internal gb_inline bool string_starts_with(String const &s, String const &prefix) { if (prefix.len > s.len) { return false; @@ -614,7 +654,7 @@ gb_internal String normalize_path(gbAllocator a, String const &path, String cons // TODO(bill): Make this non-windows specific gb_internal String16 string_to_string16(gbAllocator a, String s) { int len, len1; - wchar_t *text; + u16 *text; if (s.len < 1) { return make_string16(nullptr, 0); @@ -625,9 +665,9 @@ gb_internal String16 string_to_string16(gbAllocator a, String s) { return make_string16(nullptr, 0); } - text = gb_alloc_array(a, wchar_t, len+1); + text = gb_alloc_array(a, u16, len+1); - len1 = convert_multibyte_to_widechar(cast(char *)s.text, cast(int)s.len, text, cast(int)len); + len1 = convert_multibyte_to_widechar(cast(char *)s.text, cast(int)s.len, cast(wchar_t *)text, cast(int)len); if (len1 == 0) { gb_free(a, text); return make_string16(nullptr, 0); @@ -646,7 +686,7 @@ gb_internal String string16_to_string(gbAllocator a, String16 s) { return make_string(nullptr, 0); } - len = convert_widechar_to_multibyte(s.text, cast(int)s.len, nullptr, 0); + len = convert_widechar_to_multibyte(cast(wchar_t *)s.text, cast(int)s.len, nullptr, 0); if (len == 0) { return make_string(nullptr, 0); } @@ -654,7 +694,7 @@ gb_internal String string16_to_string(gbAllocator a, String16 s) { text = gb_alloc_array(a, u8, len+1); - len1 = convert_widechar_to_multibyte(s.text, cast(int)s.len, cast(char *)text, cast(int)len); + len1 = convert_widechar_to_multibyte(cast(wchar_t *)s.text, cast(int)s.len, cast(char *)text, cast(int)len); if (len1 == 0) { gb_free(a, text); return make_string(nullptr, 0); @@ -674,9 +714,9 @@ gb_internal String temporary_directory(gbAllocator allocator) { return String{0}; } DWORD len = gb_max(MAX_PATH, n); - wchar_t *b = gb_alloc_array(heap_allocator(), wchar_t, len+1); + u16 *b = gb_alloc_array(heap_allocator(), u16, len+1); defer (gb_free(heap_allocator(), b)); - n = GetTempPathW(len, b); + n = GetTempPathW(len, cast(wchar_t *)b); if (n == 3 && b[1] == ':' && b[2] == '\\') { } else if (n > 0 && b[n-1] == '\\') { @@ -791,6 +831,104 @@ gb_internal String quote_to_ascii(gbAllocator a, String str, u8 quote='"') { return res; } +gb_internal Rune decode_surrogate_pair(u16 r1, u16 r2) { + static Rune const _surr1 = 0xd800; + static Rune const _surr2 = 0xdc00; + static Rune const _surr3 = 0xe000; + static Rune const _surr_self = 0x10000; + + if (_surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3) { + return (((r1-_surr1)<<10) | (r2 - _surr2)) + _surr_self; + } + return GB_RUNE_INVALID; +} + +gb_internal String quote_to_ascii(gbAllocator a, String16 str, u8 quote='"') { + static Rune const _surr1 = 0xd800; + static Rune const _surr2 = 0xdc00; + static Rune const _surr3 = 0xe000; + static Rune const _surr_self = 0x10000; + + u16 *s = cast(u16 *)str.text; + isize n = str.len; + auto buf = array_make(a, 0, n*2); + array_add(&buf, quote); + for (isize width = 0; n > 0; s += width, n -= width) { + Rune r = cast(Rune)s[0]; + width = 1; + if (r < _surr1 || _surr3 <= r) { + r = cast(Rune)r; + } else if (_surr1 <= r && r < _surr2) { + if (n>1) { + r = decode_surrogate_pair(s[0], s[1]); + if (r != GB_RUNE_INVALID) { + width = 2; + } + } else { + r = GB_RUNE_INVALID; + } + } + if (width == 1 && r == GB_RUNE_INVALID) { + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'x'); + array_add(&buf, cast(u8)lower_hex[s[0]>>4]); + array_add(&buf, cast(u8)lower_hex[s[0]&0xf]); + continue; + } + + if (r == quote || r == '\\') { + array_add(&buf, cast(u8)'\\'); + array_add(&buf, u8(r)); + continue; + } + if (r < 0x80 && is_printable(r)) { + array_add(&buf, u8(r)); + continue; + } + switch (r) { + case '\a': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + default: + if (r < ' ') { + u8 b = cast(u8)r; + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'x'); + array_add(&buf, cast(u8)lower_hex[b>>4]); + array_add(&buf, cast(u8)lower_hex[b&0xf]); + } + if (r > GB_RUNE_MAX) { + r = 0XFFFD; + } + if (r < 0x10000) { + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'u'); + for (isize i = 12; i >= 0; i -= 4) { + array_add(&buf, cast(u8)lower_hex[(r>>i)&0xf]); + } + } else { + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'U'); + for (isize i = 28; i >= 0; i -= 4) { + array_add(&buf, cast(u8)lower_hex[(r>>i)&0xf]); + } + } + } + } + + + + array_add(&buf, quote); + String res = {}; + res.text = buf.data; + res.len = buf.count; + return res; +} + diff --git a/src/types.cpp b/src/types.cpp index 2e696810d..ad576c8af 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -41,8 +41,13 @@ enum BasicKind { Basic_uint, Basic_uintptr, Basic_rawptr, - Basic_string, // ^u8 + int - Basic_cstring, // ^u8 + + Basic_string, // [^]u8 + int + Basic_cstring, // [^]u8 + + Basic_string16, // [^]u16 + int + Basic_cstring16, // [^]u16 + int + Basic_any, // rawptr + ^Type_Info Basic_typeid, @@ -500,8 +505,14 @@ gb_global Type basic_types[] = { {Type_Basic, {Basic_uintptr, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uintptr")}}, {Type_Basic, {Basic_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}}, + {Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}}, {Type_Basic, {Basic_cstring, BasicFlag_String, -1, STR_LIT("cstring")}}, + + {Type_Basic, {Basic_string16, BasicFlag_String, -1, STR_LIT("string16")}}, + {Type_Basic, {Basic_cstring16, BasicFlag_String, -1, STR_LIT("cstring16")}}, + + {Type_Basic, {Basic_any, 0, 16, STR_LIT("any")}}, {Type_Basic, {Basic_typeid, 0, 8, STR_LIT("typeid")}}, @@ -591,8 +602,12 @@ gb_global Type *t_uint = &basic_types[Basic_uint]; gb_global Type *t_uintptr = &basic_types[Basic_uintptr]; gb_global Type *t_rawptr = &basic_types[Basic_rawptr]; + gb_global Type *t_string = &basic_types[Basic_string]; gb_global Type *t_cstring = &basic_types[Basic_cstring]; +gb_global Type *t_string16 = &basic_types[Basic_string16]; +gb_global Type *t_cstring16 = &basic_types[Basic_cstring16]; + gb_global Type *t_any = &basic_types[Basic_any]; gb_global Type *t_typeid = &basic_types[Basic_typeid]; @@ -630,6 +645,8 @@ gb_global Type *t_untyped_uninit = &basic_types[Basic_UntypedUninit]; gb_global Type *t_u8_ptr = nullptr; gb_global Type *t_u8_multi_ptr = nullptr; +gb_global Type *t_u16_ptr = nullptr; +gb_global Type *t_u16_multi_ptr = nullptr; gb_global Type *t_int_ptr = nullptr; gb_global Type *t_i64_ptr = nullptr; gb_global Type *t_f64_ptr = nullptr; @@ -1292,6 +1309,14 @@ gb_internal bool is_type_string(Type *t) { } return false; } +gb_internal bool is_type_string16(Type *t) { + t = base_type(t); + if (t == nullptr) { return false; } + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_string16; + } + return false; +} gb_internal bool is_type_cstring(Type *t) { t = base_type(t); if (t == nullptr) { return false; } @@ -1300,6 +1325,14 @@ gb_internal bool is_type_cstring(Type *t) { } return false; } +gb_internal bool is_type_cstring16(Type *t) { + t = base_type(t); + if (t == nullptr) { return false; } + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_cstring16; + } + return false; +} gb_internal bool is_type_typed(Type *t) { t = base_type(t); if (t == nullptr) { return false; } @@ -1429,6 +1462,12 @@ gb_internal bool is_type_u8(Type *t) { } return false; } +gb_internal bool is_type_u16(Type *t) { + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_u16; + } + return false; +} gb_internal bool is_type_array(Type *t) { t = base_type(t); if (t == nullptr) { return false; } @@ -1690,6 +1729,39 @@ gb_internal bool is_type_rune_array(Type *t) { return false; } +gb_internal bool is_type_u16_slice(Type *t) { + t = base_type(t); + if (t == nullptr) { return false; } + if (t->kind == Type_Slice) { + return is_type_u16(t->Slice.elem); + } + return false; +} +gb_internal bool is_type_u16_array(Type *t) { + t = base_type(t); + if (t == nullptr) { return false; } + if (t->kind == Type_Array) { + return is_type_u16(t->Array.elem); + } + return false; +} +gb_internal bool is_type_u16_ptr(Type *t) { + t = base_type(t); + if (t == nullptr) { return false; } + if (t->kind == Type_Pointer) { + return is_type_u16(t->Slice.elem); + } + return false; +} +gb_internal bool is_type_u16_multi_ptr(Type *t) { + t = base_type(t); + if (t == nullptr) { return false; } + if (t->kind == Type_MultiPointer) { + return is_type_u16(t->Slice.elem); + } + return false; +} + gb_internal bool is_type_array_like(Type *t) { return is_type_array(t) || is_type_enumerated_array(t); @@ -2109,7 +2181,7 @@ gb_internal bool is_type_indexable(Type *t) { Type *bt = base_type(t); switch (bt->kind) { case Type_Basic: - return bt->Basic.kind == Basic_string; + return bt->Basic.kind == Basic_string || bt->Basic.kind == Basic_string16; case Type_Array: case Type_Slice: case Type_DynamicArray: @@ -2129,7 +2201,7 @@ gb_internal bool is_type_sliceable(Type *t) { Type *bt = base_type(t); switch (bt->kind) { case Type_Basic: - return bt->Basic.kind == Basic_string; + return bt->Basic.kind == Basic_string || bt->Basic.kind == Basic_string16; case Type_Array: case Type_Slice: case Type_DynamicArray: @@ -2376,6 +2448,7 @@ gb_internal bool type_has_nil(Type *t) { case Basic_any: return true; case Basic_cstring: + case Basic_cstring16: return true; case Basic_typeid: return true; @@ -2443,8 +2516,9 @@ gb_internal bool is_type_comparable(Type *t) { case Basic_rune: return true; case Basic_string: - return true; case Basic_cstring: + case Basic_string16: + case Basic_cstring16: return true; case Basic_typeid: return true; @@ -3774,10 +3848,12 @@ gb_internal i64 type_size_of(Type *t) { if (t->kind == Type_Basic) { GB_ASSERT_MSG(is_type_typed(t), "%s", type_to_string(t)); switch (t->Basic.kind) { - case Basic_string: size = 2*build_context.int_size; break; - case Basic_cstring: size = build_context.ptr_size; break; - case Basic_any: size = 16; break; - case Basic_typeid: size = 8; break; + case Basic_string: size = 2*build_context.int_size; break; + case Basic_cstring: size = build_context.ptr_size; break; + case Basic_string16: size = 2*build_context.int_size; break; + case Basic_cstring16: size = build_context.ptr_size; break; + case Basic_any: size = 16; break; + case Basic_typeid: size = 8; break; case Basic_int: case Basic_uint: size = build_context.int_size; @@ -3837,10 +3913,12 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) { case Type_Basic: { GB_ASSERT(is_type_typed(t)); switch (t->Basic.kind) { - case Basic_string: return build_context.int_size; - case Basic_cstring: return build_context.ptr_size; - case Basic_any: return 8; - case Basic_typeid: return 8; + case Basic_string: return build_context.int_size; + case Basic_cstring: return build_context.ptr_size; + case Basic_string16: return build_context.int_size; + case Basic_cstring16: return build_context.ptr_size; + case Basic_any: return 8; + case Basic_typeid: return 8; case Basic_int: case Basic_uint: return build_context.int_size; @@ -4088,10 +4166,12 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) { return size; } switch (kind) { - case Basic_string: return 2*build_context.int_size; - case Basic_cstring: return build_context.ptr_size; - case Basic_any: return 16; - case Basic_typeid: return 8; + case Basic_string: return 2*build_context.int_size; + case Basic_cstring: return build_context.ptr_size; + case Basic_string16: return 2*build_context.int_size; + case Basic_cstring16: return build_context.ptr_size; + case Basic_any: return 16; + case Basic_typeid: return 8; case Basic_int: case Basic_uint: return build_context.int_size; @@ -4320,6 +4400,15 @@ gb_internal i64 type_offset_of(Type *t, i64 index, Type **field_type_) { if (field_type_) *field_type_ = t_int; return build_context.int_size; // len } + } else if (t->Basic.kind == Basic_string16) { + switch (index) { + case 0: + if (field_type_) *field_type_ = t_u16_ptr; + return 0; // data + case 1: + if (field_type_) *field_type_ = t_int; + return build_context.int_size; // len + } } else if (t->Basic.kind == Basic_any) { switch (index) { case 0: @@ -4396,6 +4485,11 @@ gb_internal i64 type_offset_of_from_selection(Type *type, Selection sel) { case 0: t = t_rawptr; break; case 1: t = t_int; break; } + } else if (t->Basic.kind == Basic_string16) { + switch (index) { + case 0: t = t_rawptr; break; + case 1: t = t_int; break; + } } else if (t->Basic.kind == Basic_any) { switch (index) { case 0: t = t_rawptr; break; @@ -4637,6 +4731,11 @@ gb_internal Type *type_internal_index(Type *t, isize index) { GB_ASSERT(index == 0 || index == 1); return index == 0 ? t_u8_ptr : t_int; } + case Basic_string16: + { + GB_ASSERT(index == 0 || index == 1); + return index == 0 ? t_u16_ptr : t_int; + } case Basic_any: { GB_ASSERT(index == 0 || index == 1); -- cgit v1.2.3 From ae02d3d02d2eb5132fa7c6573ed7db20d7e18f3e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 11:55:16 +0100 Subject: Begin supporting `string16` across the core library --- base/intrinsics/intrinsics.odin | 1 + base/runtime/print.odin | 6 +++ core/encoding/cbor/tags.odin | 2 +- core/encoding/cbor/unmarshal.odin | 2 + core/encoding/json/marshal.odin | 8 ++-- core/encoding/json/unmarshal.odin | 4 +- core/flags/internal_rtti.odin | 2 + core/fmt/fmt.odin | 16 ++++---- core/io/io.odin | 4 +- core/reflect/types.odin | 8 ++-- src/check_builtin.cpp | 2 + src/check_expr.cpp | 42 +++++++++++++++++---- src/checker_builtin_procs.hpp | 2 + src/llvm_backend.cpp | 6 +++ src/llvm_backend_const.cpp | 77 +++++++++++++++++++++++++++++++++++++-- src/llvm_backend_debug.cpp | 14 +++++++ src/llvm_backend_expr.cpp | 3 +- src/llvm_backend_general.cpp | 37 +++++++++++++++++++ src/llvm_backend_utility.cpp | 19 +++++++++- src/string.cpp | 7 ++++ 20 files changed, 230 insertions(+), 32 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index be75739fe..d45d24f48 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -141,6 +141,7 @@ type_is_quaternion :: proc($T: typeid) -> bool --- type_is_string :: proc($T: typeid) -> bool --- type_is_typeid :: proc($T: typeid) -> bool --- type_is_any :: proc($T: typeid) -> bool --- +type_is_string16 :: proc($T: typeid) -> bool --- type_is_endian_platform :: proc($T: typeid) -> bool --- type_is_endian_little :: proc($T: typeid) -> bool --- diff --git a/base/runtime/print.odin b/base/runtime/print.odin index 145f002d1..85ed49445 100644 --- a/base/runtime/print.odin +++ b/base/runtime/print.odin @@ -293,7 +293,13 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) { print_string("quaternion") print_u64(u64(8*ti.size)) case Type_Info_String: + if info.is_cstring { + print_byte('c') + } print_string("string") + if info.is_utf16 { + print_string("16") + } case Type_Info_Boolean: switch ti.id { case bool: print_string("bool") diff --git a/core/encoding/cbor/tags.odin b/core/encoding/cbor/tags.odin index 17420af46..e0e69cbf5 100644 --- a/core/encoding/cbor/tags.odin +++ b/core/encoding/cbor/tags.odin @@ -298,7 +298,7 @@ tag_base64_unmarshal :: proc(_: ^Tag_Implementation, d: Decoder, _: Tag_Number, #partial switch t in ti.variant { case reflect.Type_Info_String: - + assert(!t.is_utf16) if t.is_cstring { length := base64.decoded_len(bytes) builder := strings.builder_make(0, length+1) diff --git a/core/encoding/cbor/unmarshal.odin b/core/encoding/cbor/unmarshal.odin index 365ac5d6f..2840429f5 100644 --- a/core/encoding/cbor/unmarshal.odin +++ b/core/encoding/cbor/unmarshal.odin @@ -335,6 +335,8 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header, allocator := context.a _unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, loc := #caller_location) -> (err: Unmarshal_Error) { #partial switch t in ti.variant { case reflect.Type_Info_String: + assert(!t.is_utf16) + bytes := err_conv(_decode_bytes(d, add, allocator=allocator, loc=loc)) or_return if t.is_cstring { diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index ebb9a639c..cdb00a354 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -353,10 +353,10 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: #partial switch info in ti.variant { case runtime.Type_Info_String: switch x in v { - case string: - return x == "" - case cstring: - return x == nil || x == "" + case string: return x == "" + case cstring: return x == nil || x == "" + case string16: return x == "" + case cstring16: return x == nil || x == "" } case runtime.Type_Info_Any: return v.(any) == nil diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index b9ed1476f..51e7e3b81 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -570,7 +570,9 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm key_ptr: rawptr #partial switch tk in t.key.variant { - case runtime.Type_Info_String: + case runtime.Type_Info_String: + assert(!tk.is_utf16) + key_ptr = rawptr(&key) key_cstr: cstring if reflect.is_cstring(t.key) { diff --git a/core/flags/internal_rtti.odin b/core/flags/internal_rtti.odin index 1c559ca55..58224cc87 100644 --- a/core/flags/internal_rtti.odin +++ b/core/flags/internal_rtti.odin @@ -127,6 +127,8 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: } case runtime.Type_Info_String: + assert(!specific_type_info.is_utf16) + if specific_type_info.is_cstring { cstr_ptr := (^cstring)(ptr) if cstr_ptr != nil { diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 7fe6287d4..9c245de94 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -2346,14 +2346,14 @@ fmt_array :: proc(fi: ^Info, data: rawptr, n: int, elem_size: int, elem: ^reflec } switch reflect.type_info_base(elem).id { - case byte: fmt_string(fi, string(([^]byte)(data)[:n]), verb); return - case u16: print_utf16(fi, ([^]u16)(data)[:n]); return - case u16le: print_utf16(fi, ([^]u16le)(data)[:n]); return - case u16be: print_utf16(fi, ([^]u16be)(data)[:n]); return - case u32: print_utf32(fi, ([^]u32)(data)[:n]); return - case u32le: print_utf32(fi, ([^]u32le)(data)[:n]); return - case u32be: print_utf32(fi, ([^]u32be)(data)[:n]); return - case rune: print_utf32(fi, ([^]rune)(data)[:n]); return + case byte: fmt_string(fi, string (([^]byte)(data)[:n]), verb); return + case u16: fmt_string16(fi, string16(([^]u16) (data)[:n]), verb); return + case u16le: print_utf16(fi, ([^]u16le)(data)[:n]); return + case u16be: print_utf16(fi, ([^]u16be)(data)[:n]); return + case u32: print_utf32(fi, ([^]u32)(data)[:n]); return + case u32le: print_utf32(fi, ([^]u32le)(data)[:n]); return + case u32be: print_utf32(fi, ([^]u32be)(data)[:n]); return + case rune: print_utf32(fi, ([^]rune)(data)[:n]); return } } if verb == 'p' { diff --git a/core/io/io.odin b/core/io/io.odin index 5431519bf..c4eb6a073 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -319,7 +319,6 @@ write_string :: proc(s: Writer, str: string, n_written: ^int = nil) -> (n: int, write_string16 :: proc(s: Writer, str: string16, n_written: ^int = nil) -> (n: int, err: Error) { for i := 0; i < len(str); i += 1 { r := rune(utf16.REPLACEMENT_CHAR) - switch c := str[i]; { case c < utf16._surr1, utf16._surr3 <= c: r = rune(c) @@ -329,7 +328,8 @@ write_string16 :: proc(s: Writer, str: string16, n_written: ^int = nil) -> (n: i i += 1 } - w, err := write_rune(s, r, n_written) + w: int + w, err = write_rune(s, r, n_written) n += w if err != nil { return diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 511c5c9bd..2351408cc 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -511,9 +511,11 @@ write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_writt io.write_i64(w, i64(8*ti.size), 10, &n) or_return case Type_Info_String: if info.is_cstring { - io.write_string(w, "cstring", &n) or_return - } else { - io.write_string(w, "string", &n) or_return + io.write_byte(w, 'c', &n) or_return + } + io.write_string(w, "string", &n) or_return + if info.is_utf16 { + io.write_string(w, "16", &n) or_return } case Type_Info_Boolean: switch ti.id { diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index d36cf4520..4abace637 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -19,6 +19,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool is_type_complex, is_type_quaternion, is_type_string, + is_type_string16, is_type_typeid, is_type_any, is_type_endian_platform, @@ -6139,6 +6140,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_type_is_complex: case BuiltinProc_type_is_quaternion: case BuiltinProc_type_is_string: + case BuiltinProc_type_is_string16: case BuiltinProc_type_is_typeid: case BuiltinProc_type_is_any: case BuiltinProc_type_is_endian_platform: diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 57073e22f..8d2e4d637 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2106,6 +2106,9 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i } else if (is_type_boolean(type)) { return in_value.kind == ExactValue_Bool; } else if (is_type_string(type)) { + if (in_value.kind == ExactValue_String16) { + return is_type_string16(type) || is_type_cstring16(type); + } return in_value.kind == ExactValue_String; } else if (is_type_integer(type) || is_type_rune(type)) { if (in_value.kind == ExactValue_Bool) { @@ -2320,6 +2323,9 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i if (in_value.kind == ExactValue_String) { return false; } + if (in_value.kind == ExactValue_String16) { + return false; + } if (out_value) *out_value = in_value; } else if (is_type_bit_set(type)) { if (in_value.kind == ExactValue_Integer) { @@ -4654,6 +4660,13 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar break; } } + } else if (operand->value.kind == ExactValue_String16) { + String16 s = operand->value.value_string16; + if (is_type_u16_array(t)) { + if (s.len == t->Array.count) { + break; + } + } } operand->mode = Addressing_Invalid; convert_untyped_error(c, operand, target_type); @@ -4983,6 +4996,12 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v if (success_) *success_ = true; if (finish_) *finish_ = true; return exact_value_u64(val); + } else if (value.kind == ExactValue_String16) { + GB_ASSERT(0 <= index && index < value.value_string.len); + u16 val = value.value_string16[index]; + if (success_) *success_ = true; + if (finish_) *finish_ = true; + return exact_value_u64(val); } if (value.kind != ExactValue_Compound) { if (success_) *success_ = true; @@ -11124,15 +11143,21 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, o->expr = node; return kind; } - - String s = {}; - if (o->value.kind == ExactValue_String) { - s = o->value.value_string; - } - o->mode = Addressing_Constant; o->type = t; - o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1])); + + if (o->value.kind == ExactValue_String16) { + String16 s = o->value.value_string16; + + o->value = exact_value_string16(substring(s, cast(isize)indices[0], cast(isize)indices[1])); + } else { + String s = {}; + if (o->value.kind == ExactValue_String) { + s = o->value.value_string; + } + + o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1])); + } } return kind; } @@ -11221,6 +11246,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast Type *t = t_invalid; switch (node->tav.value.kind) { case ExactValue_String: t = t_untyped_string; break; + case ExactValue_String16: t = t_string16; break; // TODO(bill): determine this correctly case ExactValue_Float: t = t_untyped_float; break; case ExactValue_Complex: t = t_untyped_complex; break; case ExactValue_Quaternion: t = t_untyped_quaternion; break; @@ -11657,6 +11683,8 @@ gb_internal bool is_exact_value_zero(ExactValue const &v) { return !v.value_bool; case ExactValue_String: return v.value_string.len == 0; + case ExactValue_String16: + return v.value_string16.len == 0; case ExactValue_Integer: return big_int_is_zero(&v.value_integer); case ExactValue_Float: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 8e135ab10..bff887d9e 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -250,6 +250,7 @@ BuiltinProc__type_simple_boolean_begin, BuiltinProc_type_is_complex, BuiltinProc_type_is_quaternion, BuiltinProc_type_is_string, + BuiltinProc_type_is_string16, BuiltinProc_type_is_typeid, BuiltinProc_type_is_any, @@ -607,6 +608,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_is_complex"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_quaternion"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_string"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_string16"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_typeid"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_any"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 13a1d8cf3..f37415cc1 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1264,7 +1264,13 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) { case Basic_string: return build_context.metrics.int_size == 4 ? str_lit("{string=*i}") : str_lit("{string=*q}"); + case Basic_string16: + return build_context.metrics.int_size == 4 ? str_lit("{string16=*i}") : str_lit("{string16=*q}"); + case Basic_cstring: return str_lit("*"); + case Basic_cstring16: return str_lit("*"); + + case Basic_any: return str_lit("{any=^v^v}"); // rawptr + ^Type_Info case Basic_typeid: diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index c3112934e..8c05ed4a2 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -122,6 +122,25 @@ gb_internal lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) { gb_internal LLVMValueRef llvm_const_string_internal(lbModule *m, Type *t, LLVMValueRef data, LLVMValueRef len) { + GB_ASSERT(!is_type_string16(t)); + if (build_context.metrics.ptr_size < build_context.metrics.int_size) { + LLVMValueRef values[3] = { + data, + LLVMConstNull(lb_type(m, t_i32)), + len, + }; + return llvm_const_named_struct_internal(lb_type(m, t), values, 3); + } else { + LLVMValueRef values[2] = { + data, + len, + }; + return llvm_const_named_struct_internal(lb_type(m, t), values, 2); + } +} + +gb_internal LLVMValueRef llvm_const_string16_internal(lbModule *m, Type *t, LLVMValueRef data, LLVMValueRef len) { + GB_ASSERT(is_type_string16(t)); if (build_context.metrics.ptr_size < build_context.metrics.int_size) { LLVMValueRef values[3] = { data, @@ -238,6 +257,10 @@ gb_internal lbValue lb_const_string(lbModule *m, String const &value) { return lb_const_value(m, t_string, exact_value_string(value)); } +gb_internal lbValue lb_const_string(lbModule *m, String16 const &value) { + return lb_const_value(m, t_string16, exact_value_string16(value)); +} + gb_internal lbValue lb_const_bool(lbModule *m, Type *type, bool value) { lbValue res = {}; @@ -569,7 +592,11 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb GB_ASSERT(is_type_slice(type)); res.value = lb_find_or_add_entity_string_byte_slice_with_type(m, value.value_string, original_type).value; return res; - } else { + } else if (value.kind == ExactValue_String16) { + GB_ASSERT(is_type_slice(type)); + GB_PANIC("TODO(bill): UTF-16 String"); + return res; + }else { ast_node(cl, CompoundLit, value.value_compound); isize count = cl->elems.count; @@ -751,15 +778,23 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb { bool custom_link_section = cc.link_section.len > 0; - LLVMValueRef ptr = lb_find_or_add_entity_string_ptr(m, value.value_string, custom_link_section); + LLVMValueRef ptr = nullptr; lbValue res = {}; res.type = default_type(original_type); + if (is_type_string16(res.type) || is_type_cstring16(res.type)) { + TEMPORARY_ALLOCATOR_GUARD(); + String16 s16 = string_to_string16(temporary_allocator(), value.value_string); + ptr = lb_find_or_add_entity_string16_ptr(m, s16, custom_link_section); + } else { + ptr = lb_find_or_add_entity_string_ptr(m, value.value_string, custom_link_section); + } + if (custom_link_section) { LLVMSetSection(ptr, alloc_cstring(permanent_allocator(), cc.link_section)); } - if (is_type_cstring(res.type)) { + if (is_type_cstring(res.type) || is_type_cstring16(res.type)) { res.value = ptr; } else { if (value.value_string.len == 0) { @@ -768,12 +803,46 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), value.value_string.len, true); GB_ASSERT(is_type_string(original_type)); - res.value = llvm_const_string_internal(m, original_type, ptr, str_len); + if (is_type_string16(res.type)) { + res.value = llvm_const_string16_internal(m, original_type, ptr, str_len); + } else { + res.value = llvm_const_string_internal(m, original_type, ptr, str_len); + } + } + + return res; + } + + case ExactValue_String16: + { + GB_ASSERT(is_type_string16(res.type) || is_type_cstring16(res.type)); + + bool custom_link_section = cc.link_section.len > 0; + + LLVMValueRef ptr = lb_find_or_add_entity_string16_ptr(m, value.value_string16, custom_link_section); + lbValue res = {}; + res.type = default_type(original_type); + + if (custom_link_section) { + LLVMSetSection(ptr, alloc_cstring(permanent_allocator(), cc.link_section)); + } + + if (is_type_cstring16(res.type)) { + res.value = ptr; + } else { + if (value.value_string16.len == 0) { + ptr = LLVMConstNull(lb_type(m, t_u8_ptr)); + } + LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), value.value_string16.len, true); + GB_ASSERT(is_type_string(original_type)); + + res.value = llvm_const_string16_internal(m, original_type, ptr, str_len); } return res; } + case ExactValue_Integer: if (is_type_pointer(type) || is_type_multi_pointer(type) || is_type_proc(type)) { LLVMTypeRef t = lb_type(m, original_type); diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 024c5564e..182920fc7 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -802,6 +802,20 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { LLVMMetadataRef char_type = lb_debug_type_basic_type(m, str_lit("char"), 8, LLVMDWARFTypeEncoding_Unsigned); return LLVMDIBuilderCreatePointerType(m->debug_builder, char_type, ptr_bits, ptr_bits, 0, "cstring", 7); } + + case Basic_string16: + { + LLVMMetadataRef elements[2] = {}; + elements[0] = lb_debug_struct_field(m, str_lit("data"), t_u16_ptr, 0); + elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, int_bits); + return lb_debug_basic_struct(m, str_lit("string16"), 2*int_bits, int_bits, elements, gb_count_of(elements)); + } + case Basic_cstring16: + { + LLVMMetadataRef char_type = lb_debug_type_basic_type(m, str_lit("wchar_t"), 16, LLVMDWARFTypeEncoding_Unsigned); + return LLVMDIBuilderCreatePointerType(m->debug_builder, char_type, ptr_bits, ptr_bits, 0, "cstring16", 7); + } + case Basic_any: { LLVMMetadataRef elements[2] = {}; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index fbf0dea11..3463b6083 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4354,12 +4354,13 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) { } - case Type_Basic: { // Basic_string + case Type_Basic: { // Basic_string/Basic_string16 lbValue str; lbValue elem; lbValue len; lbValue index; + str = lb_build_expr(p, ie->expr); if (deref) { str = lb_emit_load(p, str); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index d9771a75b..9ef1c23c0 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2715,6 +2715,43 @@ gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String co } } +gb_internal LLVMValueRef lb_find_or_add_entity_string16_ptr(lbModule *m, String16 const &str, bool custom_link_section) { + // TODO(bill): caching for UTF-16 strings + + LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)}; + + LLVMValueRef data = nullptr; + { + LLVMTypeRef llvm_u16 = LLVMInt16TypeInContext(m->ctx); + + TEMPORARY_ALLOCATOR_GUARD(); + + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, str.len+1); + + for (isize i = 0; i < str.len; i++) { + values[i] = LLVMConstInt(llvm_u16, str.text[i], false); + } + values[str.len] = LLVMConstInt(llvm_u16, 0, false); + + data = LLVMConstArray(llvm_u16, values, cast(unsigned)(str.len+1)); + } + + + u32 id = m->global_array_index.fetch_add(1); + gbString name = gb_string_make(temporary_allocator(), "csbs$"); + name = gb_string_appendc(name, m->module_name); + name = gb_string_append_fmt(name, "$%x", id); + + LLVMTypeRef type = LLVMTypeOf(data); + LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name); + LLVMSetInitializer(global_data, data); + lb_make_global_private_const(global_data); + LLVMSetAlignment(global_data, 1); + + LLVMValueRef ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2); + return ptr; +} + gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str, bool custom_link_section) { LLVMValueRef ptr = nullptr; if (str.len != 0) { diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index d4117b7ff..ea1bae4e9 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -6,6 +6,7 @@ gb_internal bool lb_is_type_aggregate(Type *t) { case Type_Basic: switch (t->Basic.kind) { case Basic_string: + case Basic_string16: case Basic_any: return true; @@ -981,7 +982,8 @@ gb_internal i32 lb_convert_struct_index(lbModule *m, Type *t, i32 index) { } else if (build_context.ptr_size != build_context.int_size) { switch (t->kind) { case Type_Basic: - if (t->Basic.kind != Basic_string) { + if (t->Basic.kind != Basic_string && + t->Basic.kind != Basic_string16) { break; } /*fallthrough*/ @@ -1160,6 +1162,11 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) { case 0: result_type = alloc_type_pointer(t->Slice.elem); break; case 1: result_type = t_int; break; } + } else if (is_type_string16(t)) { + switch (index) { + case 0: result_type = t_u16_ptr; break; + case 1: result_type = t_int; break; + } } else if (is_type_string(t)) { switch (index) { case 0: result_type = t_u8_ptr; break; @@ -1273,6 +1280,12 @@ gb_internal lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) { switch (t->kind) { case Type_Basic: switch (t->Basic.kind) { + case Basic_string16: + switch (index) { + case 0: result_type = t_u16_ptr; break; + case 1: result_type = t_int; break; + } + break; case Basic_string: switch (index) { case 0: result_type = t_u8_ptr; break; @@ -1440,6 +1453,10 @@ gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection e = lb_emit_struct_ep(p, e, index); break; + case Basic_string16: + e = lb_emit_struct_ep(p, e, index); + break; + default: GB_PANIC("un-gep-able type %s", type_to_string(type)); break; diff --git a/src/string.cpp b/src/string.cpp index 8405938f4..8cc0e93f3 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -79,6 +79,13 @@ gb_internal String substring(String const &s, isize lo, isize hi) { return make_string(s.text+lo, hi-lo); } +gb_internal String16 substring(String16 const &s, isize lo, isize hi) { + isize max = s.len; + GB_ASSERT_MSG(lo <= hi && hi <= max, "%td..%td..%td", lo, hi, max); + + return make_string16(s.text+lo, hi-lo); +} + gb_internal char *alloc_cstring(gbAllocator a, String s) { char *c_str = gb_alloc_array(a, char, s.len+1); -- cgit v1.2.3 From 620bf162a048fdf29fdfcedc12abae79cffeedf4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 12:32:18 +0100 Subject: Cache const `string16` in LLVM --- src/check_builtin.cpp | 13 +++++--- src/common.cpp | 1 + src/llvm_backend.hpp | 3 +- src/llvm_backend_const.cpp | 2 +- src/llvm_backend_expr.cpp | 6 ---- src/llvm_backend_general.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 83 insertions(+), 13 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 66ea0cfbd..da5eb8977 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2329,10 +2329,15 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (operand->mode == Addressing_Constant) { mode = Addressing_Constant; - GB_ASSERT_MSG(!is_type_string16(op_type), "TODO(bill): constant utf-16 string len"); - - String str = operand->value.value_string; - value = exact_value_i64(str.len); + if (operand->value.kind == ExactValue_String) { + String str = operand->value.value_string; + value = exact_value_i64(str.len); + } else if (operand->value.kind == ExactValue_String16) { + String16 str = operand->value.value_string16; + value = exact_value_i64(str.len); + } else { + GB_PANIC("Unhandled value kind: %d", operand->value.kind); + } type = t_untyped_integer; } else { mode = Addressing_Value; diff --git a/src/common.cpp b/src/common.cpp index b3761fc36..53848cacf 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -350,6 +350,7 @@ gb_global bool global_module_path_set = false; #include "ptr_map.cpp" #include "ptr_set.cpp" #include "string_map.cpp" +#include "string16_map.cpp" #include "string_set.cpp" #include "priority_queue.cpp" #include "thread_pool.cpp" diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index fef6e754d..648e8a732 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -173,7 +173,8 @@ struct lbModule { PtrMap procedure_values; Array missing_procedures_to_check; - StringMap const_strings; + StringMap const_strings; + String16Map const_string16s; PtrMap function_type_map; diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 8c05ed4a2..cba0000cd 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -594,7 +594,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb return res; } else if (value.kind == ExactValue_String16) { GB_ASSERT(is_type_slice(type)); - GB_PANIC("TODO(bill): UTF-16 String"); + res.value = lb_find_or_add_entity_string16_slice_with_type(m, value.value_string16, original_type).value; return res; }else { ast_node(cl, CompoundLit, value.value_compound); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 3463b6083..8ad6a5a1c 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2526,12 +2526,6 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { if (is_type_untyped(src)) { - if (is_type_string(src) && is_type_string16(dst)) { - GB_PANIC("TODO(bill): UTF-16 string"); - lbAddr result = lb_add_local_generated(p, t, false); - lb_addr_store(p, result, value); - return lb_addr_load(p, result); - } if (is_type_string(src) && is_type_string(dst)) { lbAddr result = lb_add_local_generated(p, t, false); lb_addr_store(p, result, value); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 9ef1c23c0..064d0ef39 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -85,6 +85,7 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) { string_map_init(&m->members); string_map_init(&m->procedures); string_map_init(&m->const_strings); + string16_map_init(&m->const_string16s); map_init(&m->function_type_map); string_map_init(&m->gen_procs); if (USE_SEPARATE_MODULES) { @@ -2716,7 +2717,18 @@ gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String co } gb_internal LLVMValueRef lb_find_or_add_entity_string16_ptr(lbModule *m, String16 const &str, bool custom_link_section) { - // TODO(bill): caching for UTF-16 strings + String16HashKey key = {}; + LLVMValueRef *found = nullptr; + + if (!custom_link_section) { + key = string_hash_string(str); + found = string16_map_get(&m->const_string16s, key); + } + if (found != nullptr) { + return *found; + } + + LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)}; @@ -2749,6 +2761,9 @@ gb_internal LLVMValueRef lb_find_or_add_entity_string16_ptr(lbModule *m, String1 LLVMSetAlignment(global_data, 1); LLVMValueRef ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2); + if (!custom_link_section) { + string16_map_set(&m->const_string16s, key, ptr); + } return ptr; } @@ -2812,6 +2827,60 @@ gb_internal lbValue lb_find_or_add_entity_string_byte_slice_with_type(lbModule * return res; } +gb_internal lbValue lb_find_or_add_entity_string16_slice_with_type(lbModule *m, String16 const &str, Type *slice_type) { + GB_ASSERT(is_type_slice(slice_type)); + LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)}; + LLVMValueRef data = nullptr; + { + LLVMTypeRef llvm_u16 = LLVMInt16TypeInContext(m->ctx); + + TEMPORARY_ALLOCATOR_GUARD(); + + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, str.len+1); + + for (isize i = 0; i < str.len; i++) { + values[i] = LLVMConstInt(llvm_u16, str.text[i], false); + } + values[str.len] = LLVMConstInt(llvm_u16, 0, false); + + data = LLVMConstArray(llvm_u16, values, cast(unsigned)(str.len+1)); + } + + u32 id = m->global_array_index.fetch_add(1); + gbString name = gb_string_make(temporary_allocator(), "csba$"); + name = gb_string_appendc(name, m->module_name); + name = gb_string_append_fmt(name, "$%x", id); + + LLVMTypeRef type = LLVMTypeOf(data); + LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name); + LLVMSetInitializer(global_data, data); + lb_make_global_private_const(global_data); + LLVMSetAlignment(global_data, 1); + + i64 data_len = str.len; + LLVMValueRef ptr = nullptr; + if (data_len != 0) { + ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2); + } else { + ptr = LLVMConstNull(lb_type(m, t_u8_ptr)); + } + if (!is_type_u16_slice(slice_type)) { + Type *bt = base_type(slice_type); + Type *elem = bt->Slice.elem; + i64 sz = type_size_of(elem); + GB_ASSERT(sz > 0); + ptr = LLVMConstPointerCast(ptr, lb_type(m, alloc_type_pointer(elem))); + data_len /= sz; + } + + LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), data_len, true); + LLVMValueRef values[2] = {ptr, len}; + + lbValue res = {}; + res.value = llvm_const_named_struct(m, slice_type, values, 2); + res.type = slice_type; + return res; +} gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *expr) { -- cgit v1.2.3 From dca9bf0b0c8a3ad2ac6854c901d99868d3a296d5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 13:11:34 +0100 Subject: Fix string16 literal length set in LLVM --- src/llvm_backend_const.cpp | 13 ++++++++++--- src/llvm_backend_general.cpp | 4 ++-- src/string.cpp | 2 -- 3 files changed, 12 insertions(+), 7 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index cba0000cd..e64be49f2 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -782,9 +782,12 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb lbValue res = {}; res.type = default_type(original_type); + isize len = value.value_string.len; + if (is_type_string16(res.type) || is_type_cstring16(res.type)) { TEMPORARY_ALLOCATOR_GUARD(); String16 s16 = string_to_string16(temporary_allocator(), value.value_string); + len = s16.len; ptr = lb_find_or_add_entity_string16_ptr(m, s16, custom_link_section); } else { ptr = lb_find_or_add_entity_string_ptr(m, value.value_string, custom_link_section); @@ -797,10 +800,14 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb if (is_type_cstring(res.type) || is_type_cstring16(res.type)) { res.value = ptr; } else { - if (value.value_string.len == 0) { - ptr = LLVMConstNull(lb_type(m, t_u8_ptr)); + if (len == 0) { + if (is_type_string16(res.type)) { + ptr = LLVMConstNull(lb_type(m, t_u16_ptr)); + } else { + ptr = LLVMConstNull(lb_type(m, t_u8_ptr)); + } } - LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), value.value_string.len, true); + LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), len, true); GB_ASSERT(is_type_string(original_type)); if (is_type_string16(res.type)) { diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 064d0ef39..d84b8302b 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2758,7 +2758,7 @@ gb_internal LLVMValueRef lb_find_or_add_entity_string16_ptr(lbModule *m, String1 LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name); LLVMSetInitializer(global_data, data); lb_make_global_private_const(global_data); - LLVMSetAlignment(global_data, 1); + LLVMSetAlignment(global_data, 2); LLVMValueRef ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2); if (!custom_link_section) { @@ -2855,7 +2855,7 @@ gb_internal lbValue lb_find_or_add_entity_string16_slice_with_type(lbModule *m, LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name); LLVMSetInitializer(global_data, data); lb_make_global_private_const(global_data); - LLVMSetAlignment(global_data, 1); + LLVMSetAlignment(global_data, 2); i64 data_len = str.len; LLVMValueRef ptr = nullptr; diff --git a/src/string.cpp b/src/string.cpp index 8cc0e93f3..2087a5fee 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -658,7 +658,6 @@ gb_internal String normalize_path(gbAllocator a, String const &path, String cons -// TODO(bill): Make this non-windows specific gb_internal String16 string_to_string16(gbAllocator a, String s) { int len, len1; u16 *text; @@ -680,7 +679,6 @@ gb_internal String16 string_to_string16(gbAllocator a, String s) { return make_string16(nullptr, 0); } text[len] = 0; - return make_string16(text, len); } -- cgit v1.2.3 From 46b7abee9fdd339bac31fe7bc79551f2b3881f46 Mon Sep 17 00:00:00 2001 From: Sunagatov Denis Date: Fri, 8 Aug 2025 03:18:57 +1100 Subject: Fix ICE on missing procedure in base:runtime When a required built-in procedure is missing from the base:runtime package, an assert should be triggered. However this does not happen and instead the compiler crashes silently. The cause is the null-dereference after scope_lookup_current returns nullptr. This adds an assertion that the runtime procedure is found, before proceeding to check it's type and performing further lookups. --- src/llvm_backend_general.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 5d6a55973..67e799918 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2792,6 +2792,7 @@ gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *e gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) { lbGenerator *gen = m->gen; + GB_ASSERT(e != nullptr); GB_ASSERT(is_type_proc(e->type)); e = strip_entity_wrapping(e); GB_ASSERT(e != nullptr); -- cgit v1.2.3 From 738a72943bdb9d0998b11d38efb5300cd02d8190 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 18 Sep 2025 15:04:16 +0100 Subject: Try moving parapoly procs into a separate module when doing weak monomorphization --- src/build_settings.cpp | 1 + src/llvm_backend.cpp | 2 +- src/llvm_backend.hpp | 2 ++ src/llvm_backend_debug.cpp | 2 +- src/llvm_backend_general.cpp | 63 ++++++++++++++++++++++++++++++++++++++------ src/llvm_backend_proc.cpp | 2 +- src/main.cpp | 5 ++++ 7 files changed, 66 insertions(+), 11 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 077660f10..867c80ac1 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -554,6 +554,7 @@ struct BuildContext { bool internal_no_inline; bool internal_by_value; + bool internal_weak_monomorphization; bool no_threaded_checker; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 11b979774..2bc18872e 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2145,7 +2145,7 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker lbModule *m = &gen->default_module; if (USE_SEPARATE_MODULES) { - m = lb_module_of_entity(gen, e); + m = lb_module_of_entity(gen, e, m); } GB_ASSERT(m != nullptr); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index cc3dcaa4a..e4b8c07fa 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -150,6 +150,8 @@ struct lbModule { struct lbGenerator *gen; LLVMTargetMachineRef target_machine; + lbModule *polymorphic_module; + CheckerInfo *info; AstPackage *pkg; // possibly associated AstFile *file; // possibly associated diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 182920fc7..3372165f2 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -1327,7 +1327,7 @@ gb_internal void lb_add_debug_info_for_global_constant_from_entity(lbGenerator * } lbModule *m = &gen->default_module; if (USE_SEPARATE_MODULES) { - m = lb_module_of_entity(gen, e); + m = lb_module_of_entity(gen, e, m); } GB_ASSERT(m != nullptr); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 49a04641c..4907e114d 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -46,6 +46,12 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) { } module_name = gb_string_appendc(module_name, "builtin"); } + if (m->polymorphic_module == m) { + if (gb_string_length(module_name)) { + module_name = gb_string_appendc(module_name, "-"); + } + module_name = gb_string_appendc(module_name, "$parapoly"); + } m->module_name = module_name; m->ctx = LLVMContextCreate(); @@ -147,17 +153,42 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { m->gen = gen; map_set(&gen->modules, cast(void *)pkg, m); lb_init_module(m, c); + + if (build_context.internal_weak_monomorphization) { + auto pm = gb_alloc_item(permanent_allocator(), lbModule); + pm->pkg = pkg; + pm->gen = gen; + m->polymorphic_module = pm; + pm->polymorphic_module = pm; + + map_set(&gen->modules, cast(void *)pm, pm); // point to itself just add it to the list + + lb_init_module(pm, c); + } + if (!module_per_file) { continue; } // NOTE(bill): Probably per file is not a good idea, so leave this for later for (AstFile *file : pkg->files) { - auto m = gb_alloc_item(permanent_allocator(), lbModule); + auto m = gb_alloc_item(permanent_allocator(), lbModule); m->file = file; - m->pkg = pkg; - m->gen = gen; + m->pkg = pkg; + m->gen = gen; map_set(&gen->modules, cast(void *)file, m); lb_init_module(m, c); + + + if (build_context.internal_weak_monomorphization) { + auto pm = gb_alloc_item(permanent_allocator(), lbModule); + pm->file = file; + pm->gen = gen; + pm->polymorphic_module = pm; + + map_set(&gen->modules, cast(void *)pm, pm); // point to itself just add it to the list + + lb_init_module(pm, c); + } } } } @@ -403,9 +434,9 @@ gb_internal lbModule *lb_module_of_expr(lbGenerator *gen, Ast *expr) { return &gen->default_module; } -gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) { - GB_ASSERT(e != nullptr); +gb_internal lbModule *lb_module_of_entity_internal(lbGenerator *gen, Entity *e, lbModule *curr_module) { lbModule **found = nullptr; + if (e->kind == Entity_Procedure && e->decl_info && e->decl_info->code_gen_module) { @@ -428,6 +459,22 @@ gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) { return &gen->default_module; } + +gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e, lbModule *curr_module) { + GB_ASSERT(e != nullptr); + GB_ASSERT(curr_module != nullptr); + lbModule *m = lb_module_of_entity_internal(gen, e, curr_module); + + if (USE_SEPARATE_MODULES && build_context.internal_weak_monomorphization) { + if (e->kind == Entity_Procedure && e->Procedure.generated_from_polymorphic) { + if (m->polymorphic_module) { + return m->polymorphic_module; + } + } + } + return m; +} + gb_internal lbAddr lb_addr(lbValue addr) { lbAddr v = {lbAddr_Default, addr}; return v; @@ -2914,7 +2961,7 @@ gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *e return lb_find_procedure_value_from_entity(m, e); } if (USE_SEPARATE_MODULES) { - lbModule *other_module = lb_module_of_entity(m->gen, e); + lbModule *other_module = lb_module_of_entity(m->gen, e, m); if (other_module != m) { String name = lb_get_entity_name(other_module, e); @@ -2962,7 +3009,7 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) lbModule *other_module = m; if (USE_SEPARATE_MODULES) { - other_module = lb_module_of_entity(gen, e); + other_module = lb_module_of_entity(gen, e, m); } if (other_module == m) { debugf("Missing Procedure (lb_find_procedure_value_from_entity): %.*s module %p\n", LIT(e->token.string), m); @@ -3145,7 +3192,7 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) { } if (USE_SEPARATE_MODULES) { - lbModule *other_module = lb_module_of_entity(m->gen, e); + lbModule *other_module = lb_module_of_entity(m->gen, e, m); bool is_external = other_module != m; if (!is_external) { diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 06829efab..20d627fa2 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -84,7 +84,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i String link_name = {}; if (ignore_body) { - lbModule *other_module = lb_module_of_entity(m->gen, entity); + lbModule *other_module = lb_module_of_entity(m->gen, entity, m); link_name = lb_get_entity_name(other_module, entity); } else { link_name = lb_get_entity_name(m, entity); diff --git a/src/main.cpp b/src/main.cpp index 184b1eaac..130c3f31d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -403,6 +403,7 @@ enum BuildFlagKind { BuildFlag_InternalCached, BuildFlag_InternalNoInline, BuildFlag_InternalByValue, + BuildFlag_InternalWeakMonomorphization, BuildFlag_Tilde, @@ -626,6 +627,7 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_InternalCached, str_lit("internal-cached"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalNoInline, str_lit("internal-no-inline"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalByValue, str_lit("internal-by-value"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_InternalWeakMonomorphization, str_lit("internal-weak-monomorphization"), BuildFlagParam_None, Command_all); #if ALLOW_TILDE add_flag(&build_flags, BuildFlag_Tilde, str_lit("tilde"), BuildFlagParam_None, Command__does_build); @@ -1584,6 +1586,9 @@ gb_internal bool parse_build_flags(Array args) { case BuildFlag_InternalByValue: build_context.internal_by_value = true; break; + case BuildFlag_InternalWeakMonomorphization: + build_context.internal_weak_monomorphization = true; + break; case BuildFlag_Tilde: build_context.tilde_backend = true; -- cgit v1.2.3 From 0f307fbc5d27a9b0fa880dda7a2754d2d1ca7bfd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 18 Sep 2025 15:34:19 +0100 Subject: Use multiple modules per file in package runtime --- src/llvm_backend.cpp | 50 +++++++++++++------------------------------- src/llvm_backend.hpp | 2 ++ src/llvm_backend_general.cpp | 8 +++++-- 3 files changed, 23 insertions(+), 37 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 2bc18872e..c6a372ac2 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2303,6 +2303,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) { struct lbLLVMModulePassWorkerData { lbModule *m; LLVMTargetMachineRef target_machine; + bool do_threading; }; gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) { @@ -2386,6 +2387,13 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) { return 1; } #endif + + if (wd->do_threading) { + thread_pool_add_task(lb_llvm_module_verification_worker_proc, wd->m); + } else { + lb_llvm_module_verification_worker_proc(wd->m); + } + return 0; } @@ -2468,19 +2476,16 @@ gb_internal void lb_llvm_function_passes(lbGenerator *gen, bool do_threading) { } -gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) { +gb_internal void lb_llvm_module_passes_and_verification(lbGenerator *gen, bool do_threading) { if (do_threading) { for (auto const &entry : gen->modules) { lbModule *m = entry.value; auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); wd->m = m; wd->target_machine = m->target_machine; + wd->do_threading = true; - if (do_threading) { - thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd); - } else { - lb_llvm_module_pass_worker_proc(wd); - } + thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd); } thread_pool_wait(); } else { @@ -2489,6 +2494,7 @@ gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) { auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); wd->m = m; wd->target_machine = m->target_machine; + wd->do_threading = false; lb_llvm_module_pass_worker_proc(wd); } } @@ -2569,31 +2575,6 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) { } - -gb_internal bool lb_llvm_module_verification(lbGenerator *gen, bool do_threading) { - if (LLVM_IGNORE_VERIFICATION) { - return true; - } - - if (do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - thread_pool_add_task(lb_llvm_module_verification_worker_proc, m); - } - thread_pool_wait(); - - } else { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - if (lb_llvm_module_verification_worker_proc(m)) { - return false; - } - } - } - - return true; -} - gb_internal void lb_add_foreign_library_paths(lbGenerator *gen) { for (auto const &entry : gen->modules) { lbModule *m = entry.value; @@ -3479,11 +3460,10 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Function Pass"); lb_llvm_function_passes(gen, do_threading && !build_context.ODIN_DEBUG); - TIME_SECTION("LLVM Module Pass"); - lb_llvm_module_passes(gen, do_threading); + TIME_SECTION("LLVM Module Pass and Verification"); + lb_llvm_module_passes_and_verification(gen, do_threading); - TIME_SECTION("LLVM Module Verification"); - if (!lb_llvm_module_verification(gen, do_threading)) { + if (gen->module_verification_failed.load(std::memory_order_relaxed)) { return false; } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index e4b8c07fa..b031aeb8c 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -239,6 +239,8 @@ struct lbGenerator : LinkerData { isize used_module_count; + std::atomic module_verification_failed; + lbProcedure *startup_runtime; lbProcedure *cleanup_runtime; lbProcedure *objc_names; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 4907e114d..9561f86dd 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -158,7 +158,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { auto pm = gb_alloc_item(permanent_allocator(), lbModule); pm->pkg = pkg; pm->gen = gen; - m->polymorphic_module = pm; + m->polymorphic_module = pm; pm->polymorphic_module = pm; map_set(&gen->modules, cast(void *)pm, pm); // point to itself just add it to the list @@ -166,7 +166,9 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { lb_init_module(pm, c); } - if (!module_per_file) { + if (pkg->kind == Package_Runtime) { + // allow this to be per file + } else if (!module_per_file) { continue; } // NOTE(bill): Probably per file is not a good idea, so leave this for later @@ -182,7 +184,9 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { if (build_context.internal_weak_monomorphization) { auto pm = gb_alloc_item(permanent_allocator(), lbModule); pm->file = file; + pm->pkg = pkg; pm->gen = gen; + m->polymorphic_module = pm; pm->polymorphic_module = pm; map_set(&gen->modules, cast(void *)pm, pm); // point to itself just add it to the list -- cgit v1.2.3 From 5bc9d79f772cd01f9ca1e42dbee17e1f4762aad1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 18 Sep 2025 16:56:19 +0100 Subject: Change mutex usage for missing procedures --- src/llvm_backend.hpp | 2 ++ src/llvm_backend_general.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index ebe9fe796..5d6f56433 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -173,6 +173,8 @@ struct lbModule { StringMap members; StringMap procedures; PtrMap procedure_values; + + BlockingMutex missing_procedures_to_check_mutex; Array missing_procedures_to_check; StringMap const_strings; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 9561f86dd..3ac405e0b 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -3030,10 +3030,11 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) } if (ignore_body) { - mutex_lock(&gen->anonymous_proc_lits_mutex); - defer (mutex_unlock(&gen->anonymous_proc_lits_mutex)); + // mutex_lock(&gen->anonymous_proc_lits_mutex); + // defer (mutex_unlock(&gen->anonymous_proc_lits_mutex)); GB_ASSERT(other_module != nullptr); + mutex_lock(&other_module->missing_procedures_to_check_mutex); rw_mutex_shared_lock(&other_module->values_mutex); auto *found = map_get(&other_module->values, e); rw_mutex_shared_unlock(&other_module->values_mutex); @@ -3042,6 +3043,7 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) lbProcedure *missing_proc_in_other_module = lb_create_procedure(other_module, e, false); array_add(&other_module->missing_procedures_to_check, missing_proc_in_other_module); } + mutex_unlock(&other_module->missing_procedures_to_check_mutex); } else { array_add(&m->missing_procedures_to_check, missing_proc); } -- cgit v1.2.3 From 9cf69576ab8cb220af5802a04a0aa53dc92046a5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 18 Sep 2025 20:58:24 +0100 Subject: More improvements to minimize code gen size --- base/runtime/core_builtin.odin | 22 +++++++++++++------ base/runtime/dynamic_map_internal.odin | 3 +++ src/build_settings.cpp | 1 + src/llvm_backend.cpp | 39 ++++++++++++++++++++++++++-------- src/llvm_backend.hpp | 4 ++-- src/llvm_backend_general.cpp | 6 +++--- src/main.cpp | 6 ++++++ 7 files changed, 60 insertions(+), 21 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index 497c4b147..903ea7ed7 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -166,11 +166,17 @@ remove_range :: proc(array: ^$D/[dynamic]$T, #any_int lo, hi: int, loc := #calle @builtin pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { assert(len(array) > 0, loc=loc) - res = array[len(array)-1] - (^Raw_Dynamic_Array)(array).len -= 1 + _pop_type_erased(&res, (^Raw_Dynamic_Array)(array), size_of(E)) return res } +_pop_type_erased :: proc(res: rawptr, array: ^Raw_Dynamic_Array, elem_size: int, loc := #caller_location) { + end := rawptr(uintptr(array.data) + uintptr(elem_size*(array.len-1))) + intrinsics.mem_copy_non_overlapping(res, end, elem_size) + array.len -= 1 +} + + // `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1. // If the operation is not possible, it will return false. @@ -387,16 +393,18 @@ make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allo // // Note: Prefer using the procedure group `make`. @(builtin, require_results) -make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error { - return make_dynamic_array_len_cap(T, 0, 0, allocator, loc) +make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error { + err = _make_dynamic_array_len_cap((^Raw_Dynamic_Array)(&array), size_of(E), align_of(E), 0, 0, allocator, loc) + return } // `make_dynamic_array_len` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. // // Note: Prefer using the procedure group `make`. @(builtin, require_results) -make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error { - return make_dynamic_array_len_cap(T, len, len, allocator, loc) +make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error { + err = _make_dynamic_array_len_cap((^Raw_Dynamic_Array)(&array), size_of(E), align_of(E), len, len, allocator, loc) + return } // `make_dynamic_array_len_cap` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. @@ -501,7 +509,7 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) { // Note: Prefer the procedure group `reserve` @builtin reserve_map :: proc(m: ^$T/map[$K]$V, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { - return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) if m != nil else nil + return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) } // Shrinks the capacity of a map down to the current length. diff --git a/base/runtime/dynamic_map_internal.odin b/base/runtime/dynamic_map_internal.odin index 7b65a2fa0..e288d1f53 100644 --- a/base/runtime/dynamic_map_internal.odin +++ b/base/runtime/dynamic_map_internal.odin @@ -985,6 +985,9 @@ __dynamic_map_entry :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_ // IMPORTANT: USED WITHIN THE COMPILER @(private) __dynamic_map_reserve :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uint, loc := #caller_location) -> Allocator_Error { + if m == nil { + return nil + } return map_reserve_dynamic(m, info, uintptr(new_capacity), loc) } diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 867c80ac1..54f11a42d 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -555,6 +555,7 @@ struct BuildContext { bool internal_no_inline; bool internal_by_value; bool internal_weak_monomorphization; + bool internal_ignore_llvm_verification; bool no_threaded_checker; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 46c319a90..5d2accd90 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -8,7 +8,11 @@ #endif #ifndef LLVM_IGNORE_VERIFICATION -#define LLVM_IGNORE_VERIFICATION 0 +#define LLVM_IGNORE_VERIFICATION build_context.internal_ignore_llvm_verification +#endif + +#ifndef LLVM_WEAK_MONOMORPHIZATION +#define LLVM_WEAK_MONOMORPHIZATION build_context.internal_weak_monomorphization #endif @@ -620,6 +624,7 @@ gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type) { #define LLVM_SET_VALUE_NAME(value, name) LLVMSetValueName2((value), (name), gb_count_of((name))-1); + gb_internal lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) { GB_ASSERT(!build_context.dynamic_map_calls); type = base_type(type); @@ -634,6 +639,9 @@ gb_internal lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) { lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_get_proc); string_map_set(&m->gen_procs, proc_name, p); + + p->internal_gen_type = type; + lb_begin_procedure_body(p); defer (lb_end_procedure_body(p)); @@ -1891,6 +1899,10 @@ gb_internal void lb_verify_function(lbModule *m, lbProcedure *p, bool dump_ll=fa } gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) { + if (LLVM_IGNORE_VERIFICATION) { + return 0; + } + char *llvm_error = nullptr; defer (LLVMDisposeMessage(llvm_error)); lbModule *m = cast(lbModule *)data; @@ -2298,6 +2310,14 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) { } +void lb_remove_unused_functions_and_globals(lbGenerator *gen) { + for (auto &entry : gen->modules) { + lbModule *m = entry.value; + lb_run_remove_unused_function_pass(m); + lb_run_remove_unused_globals_pass(m); + } +} + struct lbLLVMModulePassWorkerData { lbModule *m; LLVMTargetMachineRef target_machine; @@ -2307,9 +2327,6 @@ struct lbLLVMModulePassWorkerData { gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) { auto wd = cast(lbLLVMModulePassWorkerData *)data; - lb_run_remove_unused_function_pass(wd->m); - lb_run_remove_unused_globals_pass(wd->m); - LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager(); lb_populate_module_pass_manager(wd->target_machine, module_pass_manager, build_context.optimization_level); LLVMRunPassManager(module_pass_manager, wd->m->mod); @@ -2386,6 +2403,10 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) { } #endif + if (LLVM_IGNORE_VERIFICATION) { + return 0; + } + if (wd->do_threading) { thread_pool_add_task(lb_llvm_module_verification_worker_proc, wd->m); } else { @@ -3464,12 +3485,14 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Function Pass"); lb_llvm_function_passes(gen, do_threading && !build_context.ODIN_DEBUG); + TIME_SECTION("LLVM Remove Unused Functions and Globals"); + lb_remove_unused_functions_and_globals(gen); + TIME_SECTION("LLVM Module Pass and Verification"); lb_llvm_module_passes_and_verification(gen, do_threading); - if (gen->module_verification_failed.load(std::memory_order_relaxed)) { - return false; - } + TIME_SECTION("LLVM Correct Entity Linkage"); + lb_correct_entity_linkage(gen); llvm_error = nullptr; defer (LLVMDisposeMessage(llvm_error)); @@ -3497,8 +3520,6 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { } } - TIME_SECTION("LLVM Correct Entity Linkage"); - lb_correct_entity_linkage(gen); //////////////////////////////////////////// for (auto const &entry: gen->modules) { diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 5d6f56433..698e7657f 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -241,8 +241,6 @@ struct lbGenerator : LinkerData { isize used_module_count; - std::atomic module_verification_failed; - lbProcedure *startup_runtime; lbProcedure *cleanup_runtime; lbProcedure *objc_names; @@ -409,6 +407,8 @@ struct lbProcedure { void (*generate_body)(lbModule *m, lbProcedure *p); Array *global_variables; lbProcedure *objc_names; + + Type *internal_gen_type; // map_set, map_get, etc. }; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 3ac405e0b..fb9fa4cea 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -154,7 +154,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { map_set(&gen->modules, cast(void *)pkg, m); lb_init_module(m, c); - if (build_context.internal_weak_monomorphization) { + if (LLVM_WEAK_MONOMORPHIZATION) { auto pm = gb_alloc_item(permanent_allocator(), lbModule); pm->pkg = pkg; pm->gen = gen; @@ -181,7 +181,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { lb_init_module(m, c); - if (build_context.internal_weak_monomorphization) { + if (LLVM_WEAK_MONOMORPHIZATION) { auto pm = gb_alloc_item(permanent_allocator(), lbModule); pm->file = file; pm->pkg = pkg; @@ -469,7 +469,7 @@ gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e, lbModule GB_ASSERT(curr_module != nullptr); lbModule *m = lb_module_of_entity_internal(gen, e, curr_module); - if (USE_SEPARATE_MODULES && build_context.internal_weak_monomorphization) { + if (USE_SEPARATE_MODULES) { if (e->kind == Entity_Procedure && e->Procedure.generated_from_polymorphic) { if (m->polymorphic_module) { return m->polymorphic_module; diff --git a/src/main.cpp b/src/main.cpp index 130c3f31d..bbaf6f23f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -404,6 +404,7 @@ enum BuildFlagKind { BuildFlag_InternalNoInline, BuildFlag_InternalByValue, BuildFlag_InternalWeakMonomorphization, + BuildFlag_InternalLLVMVerification, BuildFlag_Tilde, @@ -628,6 +629,7 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_InternalNoInline, str_lit("internal-no-inline"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalByValue, str_lit("internal-by-value"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalWeakMonomorphization, str_lit("internal-weak-monomorphization"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_InternalLLVMVerification, str_lit("internal-ignore-llvm-verification"), BuildFlagParam_None, Command_all); #if ALLOW_TILDE add_flag(&build_flags, BuildFlag_Tilde, str_lit("tilde"), BuildFlagParam_None, Command__does_build); @@ -1589,6 +1591,10 @@ gb_internal bool parse_build_flags(Array args) { case BuildFlag_InternalWeakMonomorphization: build_context.internal_weak_monomorphization = true; break; + case BuildFlag_InternalLLVMVerification: + build_context.internal_ignore_llvm_verification = true; + break; + case BuildFlag_Tilde: build_context.tilde_backend = true; -- cgit v1.2.3 From 5d14df4112368617b9b534745f8ce8934be74ffd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 18 Sep 2025 23:26:11 +0100 Subject: Multithread `lb_module_init` --- src/llvm_backend.hpp | 2 ++ src/llvm_backend_general.cpp | 34 ++++++++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 6 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 698e7657f..370a6f2ca 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -147,6 +147,8 @@ struct lbModule { LLVMModuleRef mod; LLVMContextRef ctx; + Checker *checker; + struct lbGenerator *gen; LLVMTargetMachineRef target_machine; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index fb9fa4cea..64d574920 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -15,7 +15,9 @@ gb_global isize lb_global_type_info_member_offsets_index = 0; gb_global isize lb_global_type_info_member_usings_index = 0; gb_global isize lb_global_type_info_member_tags_index = 0; -gb_internal void lb_init_module(lbModule *m, Checker *c) { +gb_internal WORKER_TASK_PROC(lb_init_module_worker_proc) { + lbModule *m = cast(lbModule *)data; + Checker *c = m->checker; m->info = &c->info; @@ -119,6 +121,15 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) { m->const_dummy_builder = LLVMCreateBuilderInContext(m->ctx); + return 0; +} + +gb_internal void lb_init_module(lbModule *m, bool do_threading) { + if (do_threading) { + thread_pool_add_task(lb_init_module_worker_proc, m); + } else { + lb_init_module_worker_proc(m); + } } gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { @@ -131,6 +142,10 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { return false; } + isize thread_count = gb_max(build_context.thread_count, 1); + isize worker_count = thread_count-1; + bool do_threading = !!(LLVMIsMultithreaded() && USE_SEPARATE_MODULES && MULTITHREAD_OBJECT_GENERATION && worker_count > 0); + String init_fullpath = c->parser->init_fullpath; linker_data_init(gen, &c->info, init_fullpath); @@ -151,19 +166,21 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { auto m = gb_alloc_item(permanent_allocator(), lbModule); m->pkg = pkg; m->gen = gen; + m->checker = c; map_set(&gen->modules, cast(void *)pkg, m); - lb_init_module(m, c); + lb_init_module(m, do_threading); if (LLVM_WEAK_MONOMORPHIZATION) { auto pm = gb_alloc_item(permanent_allocator(), lbModule); pm->pkg = pkg; pm->gen = gen; + pm->checker = c; m->polymorphic_module = pm; pm->polymorphic_module = pm; map_set(&gen->modules, cast(void *)pm, pm); // point to itself just add it to the list - lb_init_module(pm, c); + lb_init_module(pm, do_threading); } if (pkg->kind == Package_Runtime) { @@ -177,8 +194,9 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { m->file = file; m->pkg = pkg; m->gen = gen; + m->checker = c; map_set(&gen->modules, cast(void *)file, m); - lb_init_module(m, c); + lb_init_module(m, do_threading); if (LLVM_WEAK_MONOMORPHIZATION) { @@ -186,20 +204,24 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { pm->file = file; pm->pkg = pkg; pm->gen = gen; + pm->checker = c; m->polymorphic_module = pm; pm->polymorphic_module = pm; map_set(&gen->modules, cast(void *)pm, pm); // point to itself just add it to the list - lb_init_module(pm, c); + lb_init_module(pm, do_threading); } } } } gen->default_module.gen = gen; + gen->default_module.checker = c; map_set(&gen->modules, cast(void *)1, &gen->default_module); - lb_init_module(&gen->default_module, c); + lb_init_module(&gen->default_module, do_threading); + + thread_pool_wait(); for (auto const &entry : gen->modules) { lbModule *m = entry.value; -- 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/llvm_backend_general.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 1a4da5cb0fa5731926ba7abb7a7496be6a39831f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 19 Sep 2025 11:13:18 +0100 Subject: Distribute anonymous procedure literals correctly across LLVM modules --- src/llvm_backend_general.cpp | 48 +++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 12 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 129634ee6..57eb869fa 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -3083,7 +3083,7 @@ 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; + lbGenerator *gen = m->gen; ast_node(pl, ProcLit, expr); if (pl->decl->entity.load() != nullptr) { @@ -3112,8 +3112,11 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr e->pkg = e->file->pkg; e->scope = e->file->scope; + lbModule *target_module = lb_module_of_entity(gen, e, m); + GB_ASSERT(target_module != nullptr); + // 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; + pl->decl->code_gen_module = target_module; e->decl_info = pl->decl; e->parent_proc_decl = pl->decl->parent; e->Procedure.is_anonymous = true; @@ -3121,20 +3124,41 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr pl->decl->entity.store(e); - lbProcedure *p = lb_create_procedure(m, e); - GB_ASSERT(e->code_gen_module == m); - lbValue value = {}; - value.value = p->value; - value.type = p->type; + if (target_module != m) { + mutex_lock(&target_module->missing_procedures_to_check_mutex); + rw_mutex_shared_lock(&target_module->values_mutex); + lbValue *found = map_get(&target_module->values, e); + rw_mutex_shared_unlock(&target_module->values_mutex); + if (found == nullptr) { + // THIS IS THE RACE CONDITION + lbProcedure *missing_proc_in_target_module = lb_create_procedure(target_module, e, false); + array_add(&target_module->missing_procedures_to_check, missing_proc_in_target_module); + } + + mutex_unlock(&target_module->missing_procedures_to_check_mutex); - array_add(&m->procedures_to_generate, p); - if (parent != nullptr) { - array_add(&parent->children, p); + lbProcedure *p = lb_create_procedure(m, e, true); + + lbValue value = {}; + value.value = p->value; + value.type = p->type; + return value; } else { - string_map_set(&m->members, name, value); + lbProcedure *p = lb_create_procedure(m, e); + + lbValue value = {}; + value.value = p->value; + value.type = p->type; + + array_add(&m->procedures_to_generate, p); + if (parent != nullptr) { + array_add(&parent->children, p); + } else { + string_map_set(&m->members, name, value); + } + return value; } - return value; } -- cgit v1.2.3 From 6338e0a8a3db948624817c99c431e5889afc636a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 19 Sep 2025 11:56:44 +0100 Subject: Allow unions with one variant to be constant --- src/check_decl.cpp | 2 +- src/check_expr.cpp | 28 +++++++++++++++++++++++++++- src/llvm_backend_const.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++- src/llvm_backend_general.cpp | 13 ++++++++++++- 4 files changed, 82 insertions(+), 4 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 092222d3c..bda1059fb 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1565,7 +1565,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } } -gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, Ast *init_expr) { +gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) { GB_ASSERT(e->type == nullptr); GB_ASSERT(e->kind == Entity_Variable); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d863d6cf6..0ea0952f9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3500,6 +3500,21 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type return false; } +gb_internal bool is_type_union_constantable(Type *type) { + Type *bt = base_type(type); + GB_ASSERT(bt->kind == Type_Union); + + + if (bt->Union.variants.count <= 1) { + return true; + } + + // for (Type *v : bt->Union.variants) { + + // } + return false; +} + gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) { bool is_const_expr = x->mode == Addressing_Constant; @@ -3524,6 +3539,9 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) } else if (is_type_slice(type) && is_type_string(x->type)) { x->mode = Addressing_Value; } else if (is_type_union(type)) { + if (is_type_union_constantable(type)) { + return true; + } x->mode = Addressing_Value; } if (x->mode == Addressing_Value) { @@ -3582,7 +3600,11 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type, bool forb Type *final_type = type; if (is_const_expr && !is_type_constant_type(type)) { if (is_type_union(type)) { - convert_to_typed(c, x, type); + if (is_type_union_constantable(type)) { + + } else { + convert_to_typed(c, x, type); + } } final_type = default_type(x->type); } @@ -8151,7 +8173,11 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } else { // NOTE(bill): Otherwise the compiler can override the polymorphic type // as it assumes it is determining the type + AddressingMode old_mode = operand->mode; check_cast(c, operand, t); + if (old_mode == Addressing_Constant && old_mode != operand->mode) { + gb_printf_err("HERE: %d -> %d %s\n", old_mode, operand->mode, expr_to_string(operand->expr)); + } } } operand->type = t; diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index e64be49f2..98e50dd78 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -168,7 +168,7 @@ gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValue return llvm_const_named_struct_internal(struct_type, values, value_count_); } Type *bt = base_type(t); - GB_ASSERT(bt->kind == Type_Struct); + GB_ASSERT(bt->kind == Type_Struct || bt->kind == Type_Union); GB_ASSERT(value_count_ == bt->Struct.fields.count); @@ -585,6 +585,47 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb bool is_local = cc.allow_local && m->curr_procedure != nullptr; + if (is_type_union(type) && is_type_union_constantable(type)) { + Type *bt = base_type(type); + GB_ASSERT(bt->kind == Type_Union); + GB_ASSERT(bt->Union.variants.count <= 1); + if (bt->Union.variants.count == 0) { + return lb_const_nil(m, original_type); + } else if (bt->Union.variants.count == 1) { + Type *t = bt->Union.variants[0]; + lbValue cv = lb_const_value(m, t, value, cc); + GB_ASSERT(LLVMIsConstant(cv.value)); + + LLVMTypeRef llvm_type = lb_type(m, original_type); + + if (is_type_union_maybe_pointer(type)) { + LLVMValueRef values[1] = {cv.value}; + res.value = llvm_const_named_struct_internal(llvm_type, values, 1); + res.type = original_type; + return res; + } else { + + unsigned tag_value = 1; + if (bt->Union.kind == UnionType_no_nil) { + tag_value = 0; + } + LLVMValueRef tag = LLVMConstInt(LLVMStructGetTypeAtIndex(llvm_type, 1), tag_value, false); + LLVMValueRef padding = nullptr; + LLVMValueRef values[3] = {cv.value, tag, padding}; + + isize value_count = 2; + if (LLVMCountStructElementTypes(llvm_type) > 2) { + value_count = 3; + padding = LLVMConstNull(LLVMStructGetTypeAtIndex(llvm_type, 2)); + } + res.value = llvm_const_named_struct_internal(llvm_type, values, value_count); + res.type = original_type; + return res; + } + } + + } + // GB_ASSERT_MSG(is_type_typed(type), "%s", type_to_string(type)); if (is_type_slice(type)) { diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 57eb869fa..cae08ec2f 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2226,6 +2226,18 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { if (is_type_union_maybe_pointer(type)) { LLVMTypeRef variant = lb_type(m, type->Union.variants[0]); array_add(&fields, variant); + } else if (type->Union.variants.count == 1) { + LLVMTypeRef block_type = lb_type(m, type->Union.variants[0]); + + LLVMTypeRef tag_type = lb_type(m, union_tag_type(type)); + array_add(&fields, block_type); + array_add(&fields, tag_type); + i64 used_size = lb_sizeof(block_type) + lb_sizeof(tag_type); + i64 padding = size - used_size; + if (padding > 0) { + LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align); + array_add(&fields, padding_type); + } } else { LLVMTypeRef block_type = nullptr; @@ -3131,7 +3143,6 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr lbValue *found = map_get(&target_module->values, e); rw_mutex_shared_unlock(&target_module->values_mutex); if (found == nullptr) { - // THIS IS THE RACE CONDITION lbProcedure *missing_proc_in_target_module = lb_create_procedure(target_module, e, false); array_add(&target_module->missing_procedures_to_check, missing_proc_in_target_module); } -- cgit v1.2.3 From a8b2b1a23bc40343e1642edb3b7a1e4e1ecec493 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 19 Sep 2025 13:49:09 +0100 Subject: Temporarily revert anonymous procedure load balancing --- src/llvm_backend_general.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index cae08ec2f..49a7fb13f 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -3096,6 +3096,8 @@ 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; + gb_unused(gen); + ast_node(pl, ProcLit, expr); if (pl->decl->entity.load() != nullptr) { @@ -3121,10 +3123,15 @@ 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(); + +#if 0 e->pkg = e->file->pkg; e->scope = e->file->scope; lbModule *target_module = lb_module_of_entity(gen, e, m); +#else + lbModule *target_module = m; +#endif GB_ASSERT(target_module != nullptr); // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time -- cgit v1.2.3 From 6bca1475edb8e2aec4bcbe942835bcc54f9e837e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 19 Sep 2025 14:15:25 +0100 Subject: Convert `procedures_to_generate` to a queue --- src/llvm_backend.cpp | 54 +++++++++++++++++++++++++------------------- src/llvm_backend.hpp | 7 +++++- src/llvm_backend_general.cpp | 18 ++++++++++++--- src/llvm_backend_proc.cpp | 2 +- src/llvm_backend_stmt.cpp | 2 +- 5 files changed, 54 insertions(+), 29 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index f18d4af64..3ac5970f8 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -246,26 +246,12 @@ gb_internal String lb_internal_gen_name_from_type(char const *prefix, Type *type return proc_name; } - -gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) { - type = base_type(type); - GB_ASSERT(is_type_comparable(type)); +gb_internal void lb_equal_proc_generate_body(lbModule *m, lbProcedure *p) { + Type *type = p->internal_gen_type; Type *pt = alloc_type_pointer(type); LLVMTypeRef ptr_type = lb_type(m, pt); - String proc_name = lb_internal_gen_name_from_type("__$equal", type); - lbProcedure **found = string_map_get(&m->gen_procs, proc_name); - lbProcedure *compare_proc = nullptr; - if (found) { - compare_proc = *found; - GB_ASSERT(compare_proc != nullptr); - return {compare_proc->value, compare_proc->type}; - } - - - lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc); - string_map_set(&m->gen_procs, proc_name, p); lb_begin_procedure_body(p); LLVMSetLinkage(p->value, LLVMInternalLinkage); @@ -393,9 +379,29 @@ gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) { } lb_end_procedure_body(p); +} + +gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) { + type = base_type(type); + GB_ASSERT(is_type_comparable(type)); + + String proc_name = lb_internal_gen_name_from_type("__$equal", type); + lbProcedure **found = string_map_get(&m->gen_procs, proc_name); + if (found) { + lbProcedure *p = *found; + GB_ASSERT(p != nullptr); + return {p->value, p->type}; + } - compare_proc = p; - return {compare_proc->value, compare_proc->type}; + lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc); + string_map_set(&m->gen_procs, proc_name, p); + p->internal_gen_type = type; + p->generate_body = lb_equal_proc_generate_body; + + // p->generate_body(m, p); + mpsc_enqueue(&m->procedures_to_generate, p); + + return {p->value, p->type}; } gb_internal lbValue lb_simple_compare_hash(lbProcedure *p, Type *type, lbValue data, lbValue seed) { @@ -2068,7 +2074,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc p->global_variables = &global_variables; p->objc_names = objc_names; - array_add(&main_module->procedures_to_generate, p); + mpsc_enqueue(&main_module->procedures_to_generate, p); return p; } @@ -2110,7 +2116,7 @@ gb_internal WORKER_TASK_PROC(lb_generate_procedures_and_types_per_module) { for (Entity *e : m->global_procedures_to_create) { (void)lb_get_entity_name(m, e); - array_add(&m->procedures_to_generate, lb_create_procedure(m, e)); + mpsc_enqueue(&m->procedures_to_generate, lb_create_procedure(m, e)); } return 0; } @@ -2298,7 +2304,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) { lb_llvm_function_pass_per_function_internal(m, m->gen->objc_names); } - for (lbProcedure *p : m->procedures_to_generate) { + MUTEX_GUARD_BLOCK(&m->generated_procedures_mutex) for (lbProcedure *p : m->generated_procedures) { if (p->body != nullptr) { // Build Procedure lbFunctionPassManagerKind pass_manager_kind = lbFunctionPassManager_default; if (p->flags & lbProcedureFlag_WithoutMemcpyPass) { @@ -2447,8 +2453,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) { gb_internal WORKER_TASK_PROC(lb_generate_procedures_worker_proc) { lbModule *m = cast(lbModule *)data; - for (isize i = 0; i < m->procedures_to_generate.count; i++) { - lbProcedure *p = m->procedures_to_generate[i]; + for (lbProcedure *p = nullptr; mpsc_dequeue(&m->procedures_to_generate, &p); /**/) { lb_generate_procedure(p->module, p); } return 0; @@ -2876,6 +2881,9 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) { } lb_verify_function(m, p, true); + + MUTEX_GUARD(&m->generated_procedures_mutex); + array_add(&m->generated_procedures, p); } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 7fe4651bb..6d94df399 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -186,10 +186,13 @@ struct lbModule { StringMap gen_procs; // key is the canonicalized name - Array procedures_to_generate; + MPSCQueue procedures_to_generate; Array global_procedures_to_create; Array global_types_to_create; + BlockingMutex generated_procedures_mutex; + Array generated_procedures; + lbProcedure *curr_procedure; LLVMBuilderRef const_dummy_builder; @@ -238,6 +241,8 @@ struct lbGenerator : LinkerData { PtrMap modules_through_ctx; lbModule default_module; + lbModule *equal_module; + isize used_module_count; lbProcedure *startup_runtime; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 49a7fb13f..c84edc178 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -97,12 +97,16 @@ gb_internal WORKER_TASK_PROC(lb_init_module_worker_proc) { map_init(&m->function_type_map); string_map_init(&m->gen_procs); if (USE_SEPARATE_MODULES) { - array_init(&m->procedures_to_generate, a, 0, 1<<10); + mpsc_init(&m->procedures_to_generate, a); map_init(&m->procedure_values, 1<<11); + array_init(&m->generated_procedures, a, 0, 1<<10); } else { - array_init(&m->procedures_to_generate, a, 0, c->info.all_procedures.count); + mpsc_init(&m->procedures_to_generate, a); map_init(&m->procedure_values, c->info.all_procedures.count*2); + array_init(&m->generated_procedures, a, 0, c->info.all_procedures.count*2); } + + array_init(&m->global_procedures_to_create, a, 0, 1024); array_init(&m->global_types_to_create, a, 0, 1024); array_init(&m->missing_procedures_to_check, a, 0, 16); @@ -213,6 +217,14 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { } } } + + if (LLVM_WEAK_MONOMORPHIZATION) { + lbModule *m = gb_alloc_item(permanent_allocator(), lbModule); + gen->equal_module = m; + m->checker = c; + map_set(&gen->modules, cast(void *)m, m); // point to itself just add it to the list + lb_init_module(m, do_threading); + } } gen->default_module.gen = gen; @@ -3169,7 +3181,7 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr value.value = p->value; value.type = p->type; - array_add(&m->procedures_to_generate, p); + mpsc_enqueue(&m->procedures_to_generate, p); if (parent != nullptr) { array_add(&parent->children, p); } else { diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index f71b693eb..3b8a829b7 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -835,7 +835,7 @@ gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e) lb_add_entity(m, e, value); array_add(&p->children, nested_proc); - array_add(&m->procedures_to_generate, nested_proc); + mpsc_enqueue(&m->procedures_to_generate, nested_proc); } diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 590920b59..f247fa2a7 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -92,7 +92,7 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) value.value = nested_proc->value; value.type = nested_proc->type; - array_add(&p->module->procedures_to_generate, nested_proc); + mpsc_enqueue(&p->module->procedures_to_generate, nested_proc); array_add(&p->children, nested_proc); string_map_set(&p->module->members, name, value); } -- cgit v1.2.3 From 76705c680087d58b6b50492f848a4804318d1ba8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 19 Sep 2025 14:18:09 +0100 Subject: Convert `missing_procedures_to_check` to a queue --- src/llvm_backend.cpp | 14 +++++++------- src/llvm_backend.hpp | 3 +-- src/llvm_backend_general.cpp | 12 ++++-------- 3 files changed, 12 insertions(+), 17 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 3ac5970f8..02df76243 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2477,15 +2477,9 @@ gb_internal void lb_generate_procedures(lbGenerator *gen, bool do_threading) { gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc) { lbModule *m = cast(lbModule *)data; - for (isize i = 0; i < m->missing_procedures_to_check.count; i++) { - lbProcedure *p = m->missing_procedures_to_check[i]; + for (lbProcedure *p = nullptr; mpsc_dequeue(&m->missing_procedures_to_check, &p); /**/) { debugf("Generate missing procedure: %.*s module %p\n", LIT(p->name), m); - isize count = m->procedures_to_generate.count; lb_generate_procedure(m, p); - isize new_count = m->procedures_to_generate.count; - if (count != new_count) { - gb_printf_err("NEW STUFF!\n"); - } } return 0; } @@ -2505,6 +2499,12 @@ gb_internal void lb_generate_missing_procedures(lbGenerator *gen, bool do_thread lb_generate_missing_procedures_to_check_worker_proc(m); } } + + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; + GB_ASSERT(m->missing_procedures_to_check.count == 0); + GB_ASSERT(m->procedures_to_generate.count == 0); + } } gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) { diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 6d94df399..559ec8300 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -176,8 +176,7 @@ struct lbModule { StringMap procedures; PtrMap procedure_values; - BlockingMutex missing_procedures_to_check_mutex; - Array missing_procedures_to_check; + MPSCQueue missing_procedures_to_check; StringMap const_strings; String16Map const_string16s; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index c84edc178..cb8c9406e 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -109,7 +109,7 @@ gb_internal WORKER_TASK_PROC(lb_init_module_worker_proc) { array_init(&m->global_procedures_to_create, a, 0, 1024); array_init(&m->global_types_to_create, a, 0, 1024); - array_init(&m->missing_procedures_to_check, a, 0, 16); + mpsc_init(&m->missing_procedures_to_check, a); map_init(&m->debug_values); string_map_init(&m->objc_classes); @@ -3079,18 +3079,16 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) // defer (mutex_unlock(&gen->anonymous_proc_lits_mutex)); GB_ASSERT(other_module != nullptr); - mutex_lock(&other_module->missing_procedures_to_check_mutex); rw_mutex_shared_lock(&other_module->values_mutex); auto *found = map_get(&other_module->values, e); rw_mutex_shared_unlock(&other_module->values_mutex); if (found == nullptr) { // THIS IS THE RACE CONDITION lbProcedure *missing_proc_in_other_module = lb_create_procedure(other_module, e, false); - array_add(&other_module->missing_procedures_to_check, missing_proc_in_other_module); + mpsc_enqueue(&other_module->missing_procedures_to_check, missing_proc_in_other_module); } - mutex_unlock(&other_module->missing_procedures_to_check_mutex); } else { - array_add(&m->missing_procedures_to_check, missing_proc); + mpsc_enqueue(&m->missing_procedures_to_check, missing_proc); } rw_mutex_shared_lock(&m->values_mutex); @@ -3157,16 +3155,14 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr if (target_module != m) { - mutex_lock(&target_module->missing_procedures_to_check_mutex); rw_mutex_shared_lock(&target_module->values_mutex); lbValue *found = map_get(&target_module->values, e); rw_mutex_shared_unlock(&target_module->values_mutex); if (found == nullptr) { lbProcedure *missing_proc_in_target_module = lb_create_procedure(target_module, e, false); - array_add(&target_module->missing_procedures_to_check, missing_proc_in_target_module); + mpsc_enqueue(&target_module->missing_procedures_to_check, missing_proc_in_target_module); } - mutex_unlock(&target_module->missing_procedures_to_check_mutex); lbProcedure *p = lb_create_procedure(m, e, true); -- cgit v1.2.3 From 655176e5e7ab10b66f7fd936a23f7472a818e5f2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 19 Sep 2025 15:17:38 +0100 Subject: Remove comments of dead code --- src/llvm_backend_general.cpp | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index cb8c9406e..f42087f6b 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -3075,9 +3075,6 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) } if (ignore_body) { - // mutex_lock(&gen->anonymous_proc_lits_mutex); - // defer (mutex_unlock(&gen->anonymous_proc_lits_mutex)); - GB_ASSERT(other_module != nullptr); rw_mutex_shared_lock(&other_module->values_mutex); auto *found = map_get(&other_module->values, e); @@ -3163,7 +3160,6 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr mpsc_enqueue(&target_module->missing_procedures_to_check, missing_proc_in_target_module); } - lbProcedure *p = lb_create_procedure(m, e, true); lbValue value = {}; -- cgit v1.2.3 From 9b8771b475a2e0a75205408b2615f69d65a329bd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 19 Sep 2025 16:15:04 +0100 Subject: Handle missing procedures better --- src/llvm_backend.cpp | 16 +++++++++++----- src/llvm_backend.hpp | 6 +++--- src/llvm_backend_general.cpp | 9 +++++++-- src/llvm_backend_proc.cpp | 1 - 4 files changed, 21 insertions(+), 11 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 02df76243..1742271b6 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -12,7 +12,7 @@ #endif #ifndef LLVM_WEAK_MONOMORPHIZATION -#define LLVM_WEAK_MONOMORPHIZATION build_context.internal_weak_monomorphization +#define LLVM_WEAK_MONOMORPHIZATION 1 #endif @@ -2478,8 +2478,14 @@ gb_internal void lb_generate_procedures(lbGenerator *gen, bool do_threading) { gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc) { lbModule *m = cast(lbModule *)data; for (lbProcedure *p = nullptr; mpsc_dequeue(&m->missing_procedures_to_check, &p); /**/) { - debugf("Generate missing procedure: %.*s module %p\n", LIT(p->name), m); - lb_generate_procedure(m, p); + if (!p->is_done.load(std::memory_order_relaxed)) { + debugf("Generate missing procedure: %.*s module %p\n", LIT(p->name), m); + lb_generate_procedure(m, p); + } + + for (lbProcedure *nested = nullptr; mpsc_dequeue(&m->procedures_to_generate, &nested); /**/) { + mpsc_enqueue(&m->missing_procedures_to_check, nested); + } } return 0; } @@ -2860,7 +2866,7 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star } gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) { - if (p->is_done) { + if (p->is_done.load(std::memory_order_relaxed)) { return; } @@ -2869,7 +2875,7 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) { lb_begin_procedure_body(p); lb_build_stmt(p, p->body); lb_end_procedure_body(p); - p->is_done = true; + p->is_done.store(true, std::memory_order_relaxed); m->curr_procedure = nullptr; } else if (p->generate_body != nullptr) { p->generate_body(m, p); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 559ec8300..cee46701f 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -366,9 +366,9 @@ struct lbProcedure { lbFunctionType *abi_function_type; - LLVMValueRef value; - LLVMBuilderRef builder; - bool is_done; + LLVMValueRef value; + LLVMBuilderRef builder; + std::atomic is_done; lbAddr return_ptr; Array defer_stmts; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index f42087f6b..02f67b1b8 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -3082,9 +3082,12 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) if (found == nullptr) { // THIS IS THE RACE CONDITION lbProcedure *missing_proc_in_other_module = lb_create_procedure(other_module, e, false); - mpsc_enqueue(&other_module->missing_procedures_to_check, missing_proc_in_other_module); + if (!missing_proc_in_other_module->is_done.load(std::memory_order_relaxed)) { + mpsc_enqueue(&other_module->missing_procedures_to_check, missing_proc_in_other_module); + } } } else { + GB_PANIC("missing procedure: %.*s", LIT(missing_proc->name)); mpsc_enqueue(&m->missing_procedures_to_check, missing_proc); } @@ -3157,7 +3160,9 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr rw_mutex_shared_unlock(&target_module->values_mutex); if (found == nullptr) { lbProcedure *missing_proc_in_target_module = lb_create_procedure(target_module, e, false); - mpsc_enqueue(&target_module->missing_procedures_to_check, missing_proc_in_target_module); + if (!missing_proc_in_target_module->is_done.load(std::memory_order_relaxed)) { + mpsc_enqueue(&target_module->missing_procedures_to_check, missing_proc_in_target_module); + } } lbProcedure *p = lb_create_procedure(m, e, true); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 3b8a829b7..ee17ef771 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -99,7 +99,6 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i } } - lbProcedure *p = gb_alloc_item(permanent_allocator(), lbProcedure); p->module = m; -- cgit v1.2.3 From f36ade8c640b78c7c2343d3d2bed54a224ef3deb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 19 Sep 2025 16:44:57 +0100 Subject: Remove extra checks --- src/llvm_backend_general.cpp | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 02f67b1b8..4b2e01c40 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -3082,12 +3082,9 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) if (found == nullptr) { // THIS IS THE RACE CONDITION lbProcedure *missing_proc_in_other_module = lb_create_procedure(other_module, e, false); - if (!missing_proc_in_other_module->is_done.load(std::memory_order_relaxed)) { - mpsc_enqueue(&other_module->missing_procedures_to_check, missing_proc_in_other_module); - } + mpsc_enqueue(&other_module->missing_procedures_to_check, missing_proc_in_other_module); } } else { - GB_PANIC("missing procedure: %.*s", LIT(missing_proc->name)); mpsc_enqueue(&m->missing_procedures_to_check, missing_proc); } @@ -3133,15 +3130,9 @@ 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(); - -#if 0 - e->pkg = e->file->pkg; e->scope = e->file->scope; - lbModule *target_module = lb_module_of_entity(gen, e, m); -#else lbModule *target_module = m; -#endif GB_ASSERT(target_module != nullptr); // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time @@ -3160,9 +3151,7 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr rw_mutex_shared_unlock(&target_module->values_mutex); if (found == nullptr) { lbProcedure *missing_proc_in_target_module = lb_create_procedure(target_module, e, false); - if (!missing_proc_in_target_module->is_done.load(std::memory_order_relaxed)) { - mpsc_enqueue(&target_module->missing_procedures_to_check, missing_proc_in_target_module); - } + mpsc_enqueue(&target_module->missing_procedures_to_check, missing_proc_in_target_module); } lbProcedure *p = lb_create_procedure(m, e, true); -- cgit v1.2.3 From 2f132b51ce133cb21986987b38f1d75fa560fa1b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 23 Sep 2025 01:23:01 +0100 Subject: Add missing `gen` --- src/llvm_backend_general.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 4b2e01c40..3fa7a076e 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -221,7 +221,8 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { if (LLVM_WEAK_MONOMORPHIZATION) { lbModule *m = gb_alloc_item(permanent_allocator(), lbModule); gen->equal_module = m; - m->checker = c; + m->gen = gen; + m->checker = c; map_set(&gen->modules, cast(void *)m, m); // point to itself just add it to the list lb_init_module(m, do_threading); } -- cgit v1.2.3 From 31f0aaa62f2799d7c5e38c10cabe034284ae8e5d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 24 Sep 2025 09:55:22 +0100 Subject: Try to improve const `union` LLVM construction --- src/llvm_backend.cpp | 2 +- src/llvm_backend_const.cpp | 94 ++++++++++++++++++++++++++++++++++++++++++++ src/llvm_backend_expr.cpp | 15 +++++++ src/llvm_backend_general.cpp | 78 ++++++++++++++++++++++++++++-------- 4 files changed, 172 insertions(+), 17 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 33b03e235..3af473c3a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1914,7 +1914,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) { lbModule *m = cast(lbModule *)data; if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) { - gb_printf_err("LLVM Error:\n%s\n", llvm_error); + gb_printf_err("LLVM Error in module %s:\n%s\n", m->module_name, llvm_error); if (build_context.keep_temp_files) { TIME_SECTION("LLVM Print Module to File"); String filepath_ll = lb_filepath_ll_for_module(m); diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 98e50dd78..781cc52b8 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -537,6 +537,100 @@ gb_internal bool lb_is_nested_possibly_constant(Type *ft, Selection const &sel, return lb_is_elem_const(elem, ft); } +gb_internal LLVMValueRef lb_construct_const_union(lbModule *m, LLVMValueRef variant_value, Type *variant_type, Type *union_type) { + Type *bt = base_type(union_type); + GB_ASSERT(bt->kind == Type_Union); + GB_ASSERT(lb_type(m, variant_type) == LLVMTypeOf(variant_value)); + + if (bt->Union.variants.count == 0) { + GB_ASSERT(LLVMIsNull(variant_value)); + return variant_value; + } + + LLVMTypeRef llvm_type = lb_type(m, union_type); + LLVMTypeRef llvm_variant_type = lb_type(m, variant_type); + + if (is_type_union_maybe_pointer(bt)) { + GB_ASSERT(lb_sizeof(LLVMTypeOf(variant_value)) == lb_sizeof(llvm_type)); + return LLVMConstBitCast(variant_value, llvm_type); + } + + if (bt->Union.variants.count == 1) { + unsigned long long the_tag = cast(unsigned long long)union_variant_index(union_type, variant_type); + LLVMTypeRef tag_type = lb_type(m, union_tag_type(bt)); + + LLVMValueRef values[3] = {}; + unsigned i = 0; + values[i++] = variant_value; + values[i++] = LLVMConstInt(tag_type, the_tag, false); + + i64 used_size = bt->Union.variant_block_size + lb_sizeof(tag_type); + i64 padding = type_size_of(union_type) - used_size; + i64 align = type_align_of(union_type); + if (padding > 0) { + LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align); + values[i++] = LLVMConstNull(padding_type); + } + + return LLVMConstNamedStruct(llvm_type, values, i); + } + + LLVMTypeRef block_type = LLVMStructGetTypeAtIndex(llvm_type, 0); + + LLVMTypeRef block_padding = nullptr; + i64 block_padding_size = bt->Union.variant_block_size - type_size_of(variant_type); + if (block_padding_size > 0) { + block_padding = lb_type_padding_filler(m, block_padding_size, type_align_of(variant_type)); + return nullptr; + } + + LLVMTypeRef tag_type = lb_type(m, union_tag_type(bt)); + + i64 used_size = bt->Union.variant_block_size + lb_sizeof(tag_type); + i64 padding = type_size_of(union_type) - used_size; + i64 align = type_align_of(union_type); + LLVMTypeRef padding_type = nullptr; + if (padding > 0) { + padding_type = lb_type_padding_filler(m, padding, align); + } + + + unsigned i = 0; + LLVMValueRef values[3] = {}; + + LLVMValueRef variant_value_wrapped = variant_value; + + if (lb_sizeof(llvm_variant_type) == 0) { + variant_value_wrapped = LLVMConstNull(block_type); + } else if (block_type != llvm_variant_type) { + return nullptr; + } + + values[i++] = variant_value_wrapped; + + if (block_padding_size > 0) { + values[i++] = LLVMConstNull(block_padding); + } + unsigned long long the_tag = cast(unsigned long long)union_variant_index(union_type, variant_type); + values[i++] = LLVMConstInt(tag_type, the_tag, false); + if (padding > 0) { + values[i++] = LLVMConstNull(padding_type); + } + return LLVMConstNamedStruct(llvm_type, values, i); +} + +gb_internal bool lb_try_construct_const_union(lbModule *m, lbValue *value, Type *variant_type, Type *union_type) { + if (lb_is_const(*value)) { + LLVMValueRef res = lb_construct_const_union(m, value->value, variant_type, union_type); + if (res != nullptr) { + *value = {res, union_type}; + return true; + } + } + return false; +} + + gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc) { if (cc.allow_local) { cc.is_rodata = false; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index cff91813e..4a1f39c19 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2495,6 +2495,11 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { Type *vt = dst->Union.variants[0]; if (internal_check_is_assignable_to(src_type, vt)) { value = lb_emit_conv(p, value, vt); + if (lb_is_const(value)) { + LLVMValueRef res = lb_construct_const_union(m, value.value, vt, t); + return {res, t}; + } + lbAddr parent = lb_add_local_generated(p, t, true); lb_emit_store_union_variant(p, parent.addr, value, vt); return lb_addr_load(p, parent); @@ -2503,11 +2508,18 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { for (Type *vt : dst->Union.variants) { if (src_type == t_llvm_bool && is_type_boolean(vt)) { value = lb_emit_conv(p, value, vt); + if (lb_try_construct_const_union(m, &value, vt, t)) { + return value; + } + lbAddr parent = lb_add_local_generated(p, t, true); lb_emit_store_union_variant(p, parent.addr, value, vt); return lb_addr_load(p, parent); } if (are_types_identical(src_type, vt)) { + if (lb_try_construct_const_union(m, &value, vt, t)) { + return value; + } lbAddr parent = lb_add_local_generated(p, t, true); lb_emit_store_union_variant(p, parent.addr, value, vt); return lb_addr_load(p, parent); @@ -2545,6 +2557,9 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { if (valid_count == 1) { Type *vt = dst->Union.variants[first_success_index]; value = lb_emit_conv(p, value, vt); + if (lb_try_construct_const_union(m, &value, vt, t)) { + return value; + } lbAddr parent = lb_add_local_generated(p, t, true); lb_emit_store_union_variant(p, parent.addr, value, vt); return lb_addr_load(p, parent); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 3fa7a076e..fc770102c 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1719,8 +1719,69 @@ gb_internal LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *t map_set(&m->func_raw_types, type, new_abi_fn_type); return new_abi_fn_type; +} + + +gb_internal LLVMTypeRef lb_type_internal_union_block_type(lbModule *m, Type *type) { + GB_ASSERT(type->kind == Type_Union); + + if (type->Union.variants.count <= 0) { + return nullptr; + } + if (type->Union.variants.count == 1) { + return lb_type(m, type->Union.variants[0]); + } + + i64 align = type_align_of(type); + i64 size = type_size_of(type); + gb_unused(size); + unsigned block_size = cast(unsigned)type->Union.variant_block_size; + + bool all_pointers = align == build_context.ptr_size; + for (isize i = 0; all_pointers && i < type->Union.variants.count; i++) { + Type *t = type->Union.variants[i]; + if (!is_type_internally_pointer_like(t)) { + all_pointers = false; + } + } + if (all_pointers) { + return lb_type(m, t_rawptr); + } + + { + Type *pt = type->Union.variants[0]; + for (isize i = 1; i < type->Union.variants.count; i++) { + Type *t = type->Union.variants[i]; + if (!are_types_identical(pt, t)) { + goto end_check_for_all_the_same; + } + } + return lb_type(m, pt); + } end_check_for_all_the_same:; + + { + Type *first_different = nullptr; + for (isize i = 0; i < type->Union.variants.count; i++) { + Type *t = type->Union.variants[i]; + if (type_size_of(t) == 0) { + continue; + } + if (first_different == nullptr) { + first_different = t; + } else if (!are_types_identical(first_different, t)) { + goto end_rest_zero_except_one; + } + } + if (first_different != nullptr) { + return lb_type(m, first_different); + } + } end_rest_zero_except_one:; + + return lb_type_padding_filler(m, block_size, align); } + + gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { LLVMContextRef ctx = m->ctx; i64 size = type_size_of(type); // Check size @@ -2233,8 +2294,6 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { return LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false); } - unsigned block_size = cast(unsigned)type->Union.variant_block_size; - auto fields = array_make(temporary_allocator(), 0, 3); if (is_type_union_maybe_pointer(type)) { LLVMTypeRef variant = lb_type(m, type->Union.variants[0]); @@ -2252,20 +2311,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { array_add(&fields, padding_type); } } else { - LLVMTypeRef block_type = nullptr; - - bool all_pointers = align == build_context.ptr_size; - for (isize i = 0; all_pointers && i < type->Union.variants.count; i++) { - Type *t = type->Union.variants[i]; - if (!is_type_internally_pointer_like(t)) { - all_pointers = false; - } - } - if (all_pointers) { - block_type = lb_type(m, t_rawptr); - } else { - block_type = lb_type_padding_filler(m, block_size, align); - } + LLVMTypeRef block_type = lb_type_internal_union_block_type(m, type); LLVMTypeRef tag_type = lb_type(m, union_tag_type(type)); array_add(&fields, block_type); -- cgit v1.2.3 From bad495519b604081e34e69bbafd33c81f4d3fc1e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 24 Sep 2025 10:08:25 +0100 Subject: Improve const union attemps --- src/llvm_backend_const.cpp | 26 +++++++++++++++++++++++--- src/llvm_backend_general.cpp | 22 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 781cc52b8..9069f3bdf 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -547,6 +547,9 @@ gb_internal LLVMValueRef lb_construct_const_union(lbModule *m, LLVMValueRef vari return variant_value; } + i64 block_size = bt->Union.variant_block_size; + i64 variant_size = type_size_of(variant_type); + LLVMTypeRef llvm_type = lb_type(m, union_type); LLVMTypeRef llvm_variant_type = lb_type(m, variant_type); @@ -564,7 +567,7 @@ gb_internal LLVMValueRef lb_construct_const_union(lbModule *m, LLVMValueRef vari values[i++] = variant_value; values[i++] = LLVMConstInt(tag_type, the_tag, false); - i64 used_size = bt->Union.variant_block_size + lb_sizeof(tag_type); + i64 used_size = block_size + lb_sizeof(tag_type); i64 padding = type_size_of(union_type) - used_size; i64 align = type_align_of(union_type); if (padding > 0) { @@ -578,7 +581,7 @@ gb_internal LLVMValueRef lb_construct_const_union(lbModule *m, LLVMValueRef vari LLVMTypeRef block_type = LLVMStructGetTypeAtIndex(llvm_type, 0); LLVMTypeRef block_padding = nullptr; - i64 block_padding_size = bt->Union.variant_block_size - type_size_of(variant_type); + i64 block_padding_size = block_size - variant_size; if (block_padding_size > 0) { block_padding = lb_type_padding_filler(m, block_padding_size, type_align_of(variant_type)); return nullptr; @@ -586,7 +589,7 @@ gb_internal LLVMValueRef lb_construct_const_union(lbModule *m, LLVMValueRef vari LLVMTypeRef tag_type = lb_type(m, union_tag_type(bt)); - i64 used_size = bt->Union.variant_block_size + lb_sizeof(tag_type); + i64 used_size = block_size + lb_sizeof(tag_type); i64 padding = type_size_of(union_type) - used_size; i64 align = type_align_of(union_type); LLVMTypeRef padding_type = nullptr; @@ -603,9 +606,26 @@ gb_internal LLVMValueRef lb_construct_const_union(lbModule *m, LLVMValueRef vari if (lb_sizeof(llvm_variant_type) == 0) { variant_value_wrapped = LLVMConstNull(block_type); } else if (block_type != llvm_variant_type) { + if (block_size != variant_size) { + return nullptr; + } + if (LLVMGetTypeKind(block_type) == LLVMIntegerTypeKind) { + switch (LLVMGetTypeKind(llvm_variant_type)) { + case LLVMHalfTypeKind: + case LLVMFloatTypeKind: + case LLVMDoubleTypeKind: + variant_value_wrapped = LLVMConstBitCast(variant_value_wrapped, block_type); + goto assign_value_wrapped; + case LLVMPointerTypeKind: + variant_value_wrapped = LLVMConstPtrToInt(variant_value_wrapped, block_type); + goto assign_value_wrapped; + } + } + return nullptr; } +assign_value_wrapped:; values[i++] = variant_value_wrapped; if (block_padding_size > 0) { diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index fc770102c..4c60d9f4c 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1778,6 +1778,28 @@ gb_internal LLVMTypeRef lb_type_internal_union_block_type(lbModule *m, Type *typ } } end_rest_zero_except_one:; + // { + // LLVMTypeRef first_different = nullptr; + // for (isize i = 0; i < type->Union.variants.count; i++) { + // Type *t = type->Union.variants[i]; + // if (type_size_of(t) == 0) { + // continue; + // } + // if (first_different == nullptr) { + // first_different = lb_type(m, base_type(t)); + // } else { + // LLVMTypeRef llvm_t = lb_type(m, base_type(t)); + // if (llvm_t != first_different) { + // goto end_rest_zero_except_one_llvm_like; + // } + // } + // } + // if (first_different != nullptr) { + // return first_different; + // } + // } end_rest_zero_except_one_llvm_like:; + + return lb_type_padding_filler(m, block_size, align); } -- cgit v1.2.3 From ad85ec765bcf3b3e501f445ab4e35169bc7576d7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 24 Sep 2025 10:21:38 +0100 Subject: More const union improvements --- src/llvm_backend_const.cpp | 71 ++++++++++++++++++++++++++++++++++---------- src/llvm_backend_general.cpp | 5 ++-- 2 files changed, 58 insertions(+), 18 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 9069f3bdf..219700ffd 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -542,6 +542,12 @@ gb_internal LLVMValueRef lb_construct_const_union(lbModule *m, LLVMValueRef vari GB_ASSERT(bt->kind == Type_Union); GB_ASSERT(lb_type(m, variant_type) == LLVMTypeOf(variant_value)); + LLVMTypeRef llvm_type = lb_type(m, union_type); + + if (LLVMIsNull(variant_value)) { + return LLVMConstNull(llvm_type); + } + if (bt->Union.variants.count == 0) { GB_ASSERT(LLVMIsNull(variant_value)); return variant_value; @@ -550,7 +556,6 @@ gb_internal LLVMValueRef lb_construct_const_union(lbModule *m, LLVMValueRef vari i64 block_size = bt->Union.variant_block_size; i64 variant_size = type_size_of(variant_type); - LLVMTypeRef llvm_type = lb_type(m, union_type); LLVMTypeRef llvm_variant_type = lb_type(m, variant_type); if (is_type_union_maybe_pointer(bt)) { @@ -580,13 +585,6 @@ gb_internal LLVMValueRef lb_construct_const_union(lbModule *m, LLVMValueRef vari LLVMTypeRef block_type = LLVMStructGetTypeAtIndex(llvm_type, 0); - LLVMTypeRef block_padding = nullptr; - i64 block_padding_size = block_size - variant_size; - if (block_padding_size > 0) { - block_padding = lb_type_padding_filler(m, block_padding_size, type_align_of(variant_type)); - return nullptr; - } - LLVMTypeRef tag_type = lb_type(m, union_tag_type(bt)); i64 used_size = block_size + lb_sizeof(tag_type); @@ -601,12 +599,55 @@ gb_internal LLVMValueRef lb_construct_const_union(lbModule *m, LLVMValueRef vari unsigned i = 0; LLVMValueRef values[3] = {}; - LLVMValueRef variant_value_wrapped = variant_value; + LLVMValueRef block_value = variant_value; if (lb_sizeof(llvm_variant_type) == 0) { - variant_value_wrapped = LLVMConstNull(block_type); + block_value = LLVMConstNull(block_type); } else if (block_type != llvm_variant_type) { if (block_size != variant_size) { + + if (LLVMGetTypeKind(block_type) == LLVMArrayTypeKind && + LLVMGetTypeKind(llvm_variant_type) == LLVMIntegerTypeKind) { + LLVMTypeRef elem = LLVMGetElementType(block_type); + unsigned count = LLVMGetArrayLength(block_type); + if (elem == llvm_variant_type) { + LLVMValueRef *elems = temporary_alloc_array(count); + elems[0] = variant_value; + for (unsigned j = 1; j < count; j++) { + elems[j] = LLVMConstNull(elem); + } + block_value = LLVMConstArray(elem, elems, count); + + goto assign_value_wrapped; + } else if (!is_type_different_to_arch_endianness(variant_type)) { + i64 elem_size = lb_sizeof(elem); + i64 variant_size = lb_sizeof(llvm_variant_type); + if (elem_size > variant_size) { + u64 val = LLVMConstIntGetZExtValue(variant_value); + + LLVMValueRef *elems = temporary_alloc_array(count); + elems[0] = LLVMConstInt(elem, val, false); + for (unsigned j = 1; j < count; j++) { + elems[j] = LLVMConstNull(elem); + } + block_value = LLVMConstArray(elem, elems, count); + + goto assign_value_wrapped; + } + } + } else if (LLVMGetTypeKind(block_type) == LLVMIntegerTypeKind && + LLVMGetTypeKind(llvm_variant_type) == LLVMIntegerTypeKind) { + if (!is_type_different_to_arch_endianness(variant_type)) { + i64 variant_size = lb_sizeof(llvm_variant_type); + if (block_size > variant_size) { + u64 val = LLVMConstIntGetZExtValue(variant_value); + block_value = LLVMConstInt(block_type, val, false); + + goto assign_value_wrapped; + } + } + } + return nullptr; } if (LLVMGetTypeKind(block_type) == LLVMIntegerTypeKind) { @@ -614,10 +655,10 @@ gb_internal LLVMValueRef lb_construct_const_union(lbModule *m, LLVMValueRef vari case LLVMHalfTypeKind: case LLVMFloatTypeKind: case LLVMDoubleTypeKind: - variant_value_wrapped = LLVMConstBitCast(variant_value_wrapped, block_type); + block_value = LLVMConstBitCast(block_value, block_type); goto assign_value_wrapped; case LLVMPointerTypeKind: - variant_value_wrapped = LLVMConstPtrToInt(variant_value_wrapped, block_type); + block_value = LLVMConstPtrToInt(block_value, block_type); goto assign_value_wrapped; } } @@ -626,11 +667,8 @@ gb_internal LLVMValueRef lb_construct_const_union(lbModule *m, LLVMValueRef vari } assign_value_wrapped:; - values[i++] = variant_value_wrapped; + values[i++] = block_value; - if (block_padding_size > 0) { - values[i++] = LLVMConstNull(block_padding); - } unsigned long long the_tag = cast(unsigned long long)union_variant_index(union_type, variant_type); values[i++] = LLVMConstInt(tag_type, the_tag, false); if (padding > 0) { @@ -646,6 +684,7 @@ gb_internal bool lb_try_construct_const_union(lbModule *m, lbValue *value, Type *value = {res, union_type}; return true; } + // gb_printf_err("%s -> %s\n", LLVMPrintValueToString(value->value), LLVMPrintTypeToString(lb_type(m, union_type))); } return false; } diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 4c60d9f4c..6e513a075 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1733,10 +1733,11 @@ gb_internal LLVMTypeRef lb_type_internal_union_block_type(lbModule *m, Type *typ } i64 align = type_align_of(type); - i64 size = type_size_of(type); - gb_unused(size); unsigned block_size = cast(unsigned)type->Union.variant_block_size; + if (block_size == 0) { + return lb_type_padding_filler(m, block_size, align); + } bool all_pointers = align == build_context.ptr_size; for (isize i = 0; all_pointers && i < type->Union.variants.count; i++) { -- cgit v1.2.3 From ffdfbfe2c2d0e09087f166be79f3dbc2859844e6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 28 Sep 2025 20:20:26 +0100 Subject: Begin to support constant array of unions --- src/check_expr.cpp | 28 ++++++++-------------------- src/llvm_backend_const.cpp | 18 ++++++++++-------- src/llvm_backend_general.cpp | 11 ++++++++++- src/llvm_backend_proc.cpp | 2 +- src/llvm_backend_type.cpp | 6 +++--- src/parser.hpp | 18 ++++++++++++++++++ src/types.cpp | 22 +++++++++++++++++++++- 7 files changed, 71 insertions(+), 34 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a825ec7bf..2da8776eb 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3505,24 +3505,6 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type return false; } -gb_internal bool is_type_union_constantable(Type *type) { - Type *bt = base_type(type); - GB_ASSERT(bt->kind == Type_Union); - - if (bt->Union.variants.count == 0) { - return true; - } else if (bt->Union.variants.count == 1) { - return is_type_constant_type(bt->Union.variants[0]); - } - - for (Type *v : bt->Union.variants) { - if (!is_type_constant_type(v)) { - return false; - } - } - return true; -} - gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) { bool is_const_expr = x->mode == Addressing_Constant; @@ -4880,7 +4862,10 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar break; } operand->type = new_type; - operand->mode = Addressing_Value; + if (operand->mode != Addressing_Constant || + !elem_type_can_be_constant(operand->type)) { + operand->mode = Addressing_Value; + } break; } else if (valid_count > 1) { ERROR_BLOCK(); @@ -9895,7 +9880,10 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * Operand o = {}; check_expr_or_type(c, &o, elem, field->type); - if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { + if (is_type_any(field->type) || + is_type_raw_union(field->type) || + (is_type_union(field->type) && !is_type_union_constantable(field->type)) || + is_type_typeid(field->type)) { is_constant = false; } if (is_constant) { diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 415d6743b..8cdc889e0 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -96,10 +96,6 @@ gb_internal LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) { case LLVMPointerTypeKind: return LLVMConstPointerCast(val, dst); case LLVMStructTypeKind: - // GB_PANIC("%s -> %s", LLVMPrintValueToString(val), LLVMPrintTypeToString(dst)); - // NOTE(bill): It's not possible to do a bit cast on a struct, why was this code even here in the first place? - // It seems mostly to exist to get around the "anonymous -> named" struct assignments - // return LLVMConstBitCast(val, dst); return val; default: GB_PANIC("Unhandled const cast %s to %s", LLVMPrintTypeToString(src), LLVMPrintTypeToString(dst)); @@ -199,11 +195,17 @@ gb_internal LLVMValueRef llvm_const_named_struct_internal(LLVMTypeRef t, LLVMVal return LLVMConstNamedStruct(t, values, value_count); } -gb_internal LLVMValueRef llvm_const_array(LLVMTypeRef elem_type, LLVMValueRef *values, isize value_count_) { +gb_internal LLVMValueRef llvm_const_array(lbModule *m, LLVMTypeRef elem_type, LLVMValueRef *values, isize value_count_) { unsigned value_count = cast(unsigned)value_count_; for (unsigned i = 0; i < value_count; i++) { values[i] = llvm_const_cast(values[i], elem_type); } + for (unsigned i = 0; i < value_count; i++) { + if (elem_type != LLVMTypeOf(values[i])) { + return LLVMConstStructInContext(m->ctx, values, value_count, false); + } + } + return LLVMConstArray(elem_type, values, value_count); } @@ -461,7 +463,7 @@ gb_internal LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, return lb_addr_load(p, v).value; } - return llvm_const_array(lb_type(m, elem_type), values, cast(unsigned int)count); + return llvm_const_array(m, lb_type(m, elem_type), values, cast(unsigned int)count); } gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, BigInt const *a) { @@ -1016,7 +1018,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb } GB_ASSERT(offset == s.len); - res.value = llvm_const_array(et, elems, cast(unsigned)count); + res.value = llvm_const_array(m, et, elems, cast(unsigned)count); return res; } // NOTE(bill, 2021-10-07): Allow for array programming value constants @@ -1046,7 +1048,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb elems[i] = single_elem.value; } - res.value = llvm_const_array(lb_type(m, elem), elems, cast(unsigned)count); + res.value = llvm_const_array(m, lb_type(m, elem), elems, cast(unsigned)count); return res; } else if (is_type_matrix(type) && value.kind != ExactValue_Invalid && diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 6e513a075..8e5efcb52 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -3253,11 +3253,18 @@ gb_internal lbAddr lb_add_global_generated_with_name(lbModule *m, Type *type, lb GB_ASSERT(type != nullptr); type = default_type(type); + LLVMTypeRef actual_type = lb_type(m, type); + if (value.value != nullptr) { + LLVMTypeRef value_type = LLVMTypeOf(value.value); + GB_ASSERT(lb_sizeof(actual_type) == lb_sizeof(value_type)); + actual_type = value_type; + } + Scope *scope = nullptr; Entity *e = alloc_entity_variable(scope, make_token_ident(name), type); lbValue g = {}; g.type = alloc_type_pointer(type); - g.value = LLVMAddGlobal(m->mod, lb_type(m, type), alloc_cstring(temporary_allocator(), name)); + g.value = LLVMAddGlobal(m->mod, actual_type, alloc_cstring(temporary_allocator(), name)); if (value.value != nullptr) { GB_ASSERT_MSG(LLVMIsConstant(value.value), LLVMPrintValueToString(value.value)); LLVMSetInitializer(g.value, value.value); @@ -3265,6 +3272,8 @@ gb_internal lbAddr lb_add_global_generated_with_name(lbModule *m, Type *type, lb LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, type))); } + g.value = LLVMConstPointerCast(g.value, lb_type(m, g.type)); + lb_add_entity(m, e, g); lb_add_member(m, name, g); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index ee17ef771..19213e1a3 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2237,7 +2237,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu elements[i] = element; } - LLVMValueRef backing_array = llvm_const_array(lb_type(m, t_load_directory_file), elements, count); + LLVMValueRef backing_array = llvm_const_array(m, lb_type(m, t_load_directory_file), elements, count); Type *array_type = alloc_type_array(t_load_directory_file, count); lbAddr backing_array_addr = lb_add_global_generated_from_procedure(p, array_type, {backing_array, array_type}); diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index b2eec218f..7d412eb15 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -302,7 +302,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ (name##_values)[i] = LLVMConstNull(elem); \ } \ } \ - LLVMSetInitializer(name.addr.value, llvm_const_array(elem, name##_values, at->Array.count)); \ + LLVMSetInitializer(name.addr.value, llvm_const_array(m, elem, name##_values, at->Array.count)); \ }) type_info_allocate_values(lb_global_type_info_member_types); @@ -752,8 +752,8 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ value_values[i] = lb_const_value(m, t_i64, fields[i]->Constant.value).value; } - LLVMValueRef name_init = llvm_const_array(lb_type(m, t_string), name_values, cast(unsigned)fields.count); - LLVMValueRef value_init = llvm_const_array(lb_type(m, t_type_info_enum_value), value_values, cast(unsigned)fields.count); + LLVMValueRef name_init = llvm_const_array(m, lb_type(m, t_string), name_values, cast(unsigned)fields.count); + LLVMValueRef value_init = llvm_const_array(m, lb_type(m, t_type_info_enum_value), value_values, cast(unsigned)fields.count); LLVMSetInitializer(name_array.value, name_init); LLVMSetInitializer(value_array.value, value_init); LLVMSetGlobalConstant(name_array.value, true); diff --git a/src/parser.hpp b/src/parser.hpp index 56447df43..979b44618 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -27,6 +27,24 @@ enum AddressingMode : u8 { Addressing_SwizzleVariable = 14, // Swizzle indexed variable }; +gb_global String const addressing_mode_strings[] = { + str_lit("Invalid"), + str_lit("NoValue"), + str_lit("Value"), + str_lit("Context"), + str_lit("Variable"), + str_lit("Constant"), + str_lit("Type"), + str_lit("Builtin"), + str_lit("ProcGroup"), + str_lit("MapIndex"), + str_lit("OptionalOk"), + str_lit("OptionalOkPtr"), + str_lit("SoaVariable"), + str_lit("SwizzleValue"), + str_lit("SwizzleVariable"), +}; + struct TypeAndValue { Type * type; AddressingMode mode; diff --git a/src/types.cpp b/src/types.cpp index 0c6702103..46e41bb4b 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2507,15 +2507,35 @@ gb_internal bool type_has_nil(Type *t) { return false; } +gb_internal bool is_type_union_constantable(Type *type) { + Type *bt = base_type(type); + GB_ASSERT(bt->kind == Type_Union); + + if (bt->Union.variants.count == 0) { + return true; + } else if (bt->Union.variants.count == 1) { + return is_type_constant_type(bt->Union.variants[0]); + } + + for (Type *v : bt->Union.variants) { + if (!is_type_constant_type(v)) { + return false; + } + } + return true; +} gb_internal bool elem_type_can_be_constant(Type *t) { t = base_type(t); if (t == t_invalid) { return false; } - if (is_type_any(t) || is_type_union(t) || is_type_raw_union(t)) { + if (is_type_any(t) || is_type_raw_union(t)) { return false; } + if (is_type_union(t)) { + return is_type_union_constantable(t); + } return true; } -- cgit v1.2.3 From f743110f63d7659d0990715311ea64fea056e249 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 28 Sep 2025 21:00:36 +0100 Subject: Correct union type checking for constants --- src/check_expr.cpp | 208 ++++++++++++++++++++++--------------------- src/llvm_backend_general.cpp | 2 +- 2 files changed, 108 insertions(+), 102 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2da8776eb..ebf61ab0c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -821,9 +821,11 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand } } - if (is_type_enum(dst) && are_types_identical(dst->Enum.base_type, operand->type)) { - if (c->in_enum_type) { - return 3; + if (c != nullptr) { + if (is_type_enum(dst) && are_types_identical(dst->Enum.base_type, operand->type)) { + if (c->in_enum_type) { + return 3; + } } } @@ -3590,11 +3592,7 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type, bool forb Type *final_type = type; if (is_const_expr && !is_type_constant_type(type)) { if (is_type_union(type)) { - if (is_type_union_constantable(type)) { - - } else { - convert_to_typed(c, x, type); - } + convert_to_typed(c, x, type); } final_type = default_type(x->type); } @@ -8033,6 +8031,106 @@ gb_internal bool check_call_parameter_mixture(Slice const &args, char con return Expr_Stmt; \ } +gb_internal ExprKind check_call_expr_as_type_cast(CheckerContext *c, Operand *operand, Ast *call, Slice const &args, Type *type_hint) { + GB_ASSERT(operand->mode == Addressing_Type); + Type *t = operand->type; + if (is_type_polymorphic_record(t)) { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("polymorphic type construction"); + + if (!is_type_named(t)) { + gbString s = expr_to_string(operand->expr); + error(call, "Illegal use of an unnamed polymorphic record, %s", s); + gb_string_free(s); + operand->mode = Addressing_Invalid; + operand->type = t_invalid;; + return Expr_Expr; + } + auto err = check_polymorphic_record_type(c, operand, call); + if (err == 0) { + Ast *ident = operand->expr; + while (ident->kind == Ast_SelectorExpr) { + Ast *s = ident->SelectorExpr.selector; + ident = s; + } + Type *ot = operand->type; + GB_ASSERT(ot->kind == Type_Named); + Entity *e = ot->Named.type_name; + add_entity_use(c, ident, e); + add_type_and_value(c, call, Addressing_Type, ot, empty_exact_value); + } else { + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + } + } else { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("type conversion"); + + operand->mode = Addressing_Invalid; + isize arg_count = args.count; + switch (arg_count) { + case 0: + { + gbString str = type_to_string(t); + error(call, "Missing argument in conversion to '%s'", str); + gb_string_free(str); + } break; + default: + { + gbString str = type_to_string(t); + if (t->kind == Type_Basic) { + ERROR_BLOCK(); + switch (t->Basic.kind) { + case Basic_complex32: + case Basic_complex64: + case Basic_complex128: + error(call, "Too many arguments in conversion to '%s'", str); + error_line("\tSuggestion: %s(1+2i) or construct with 'complex'\n", str); + break; + case Basic_quaternion64: + case Basic_quaternion128: + case Basic_quaternion256: + error(call, "Too many arguments in conversion to '%s'", str); + error_line("\tSuggestion: %s(1+2i+3j+4k) or construct with 'quaternion'\n", str); + break; + default: + error(call, "Too many arguments in conversion to '%s'", str); + } + } else { + error(call, "Too many arguments in conversion to '%s'", str); + } + gb_string_free(str); + } break; + case 1: { + Ast *arg = args[0]; + if (arg->kind == Ast_FieldValue) { + error(call, "'field = value' cannot be used in a type conversion"); + arg = arg->FieldValue.value; + // NOTE(bill): Carry on the cast regardless + } + check_expr_with_type_hint(c, operand, arg, t); + if (operand->mode != Addressing_Invalid) { + if (is_type_polymorphic(t)) { + error(call, "A polymorphic type cannot be used in a type conversion"); + } else { + // NOTE(bill): Otherwise the compiler can override the polymorphic type + // as it assumes it is determining the type + check_cast(c, operand, t); + } + } + operand->type = t; + operand->expr = call; + + + if (operand->mode != Addressing_Invalid) { + update_untyped_expr_type(c, arg, t, false); + } + break; + } + } + } + return Expr_Expr; +} + + 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 && @@ -8089,99 +8187,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } if (operand->mode == Addressing_Type) { - Type *t = operand->type; - if (is_type_polymorphic_record(t)) { - CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("polymorphic type construction"); - - if (!is_type_named(t)) { - gbString s = expr_to_string(operand->expr); - error(call, "Illegal use of an unnamed polymorphic record, %s", s); - gb_string_free(s); - operand->mode = Addressing_Invalid; - operand->type = t_invalid;; - return Expr_Expr; - } - auto err = check_polymorphic_record_type(c, operand, call); - if (err == 0) { - Ast *ident = operand->expr; - while (ident->kind == Ast_SelectorExpr) { - Ast *s = ident->SelectorExpr.selector; - ident = s; - } - Type *ot = operand->type; - GB_ASSERT(ot->kind == Type_Named); - Entity *e = ot->Named.type_name; - add_entity_use(c, ident, e); - add_type_and_value(c, call, Addressing_Type, ot, empty_exact_value); - } else { - operand->mode = Addressing_Invalid; - operand->type = t_invalid; - } - } else { - CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("type conversion"); - - operand->mode = Addressing_Invalid; - isize arg_count = args.count; - switch (arg_count) { - case 0: - { - gbString str = type_to_string(t); - error(call, "Missing argument in conversion to '%s'", str); - gb_string_free(str); - } break; - default: - { - gbString str = type_to_string(t); - if (t->kind == Type_Basic) { - ERROR_BLOCK(); - switch (t->Basic.kind) { - case Basic_complex32: - case Basic_complex64: - case Basic_complex128: - error(call, "Too many arguments in conversion to '%s'", str); - error_line("\tSuggestion: %s(1+2i) or construct with 'complex'\n", str); - break; - case Basic_quaternion64: - case Basic_quaternion128: - case Basic_quaternion256: - error(call, "Too many arguments in conversion to '%s'", str); - error_line("\tSuggestion: %s(1+2i+3j+4k) or construct with 'quaternion'\n", str); - break; - default: - error(call, "Too many arguments in conversion to '%s'", str); - } - } else { - error(call, "Too many arguments in conversion to '%s'", str); - } - gb_string_free(str); - } break; - case 1: { - Ast *arg = args[0]; - if (arg->kind == Ast_FieldValue) { - error(call, "'field = value' cannot be used in a type conversion"); - arg = arg->FieldValue.value; - // NOTE(bill): Carry on the cast regardless - } - check_expr_with_type_hint(c, operand, arg, t); - if (operand->mode != Addressing_Invalid) { - if (is_type_polymorphic(t)) { - error(call, "A polymorphic type cannot be used in a type conversion"); - } else { - // NOTE(bill): Otherwise the compiler can override the polymorphic type - // as it assumes it is determining the type - check_cast(c, operand, t); - } - } - operand->type = t; - operand->expr = call; - if (operand->mode != Addressing_Invalid) { - update_untyped_expr_type(c, arg, t, false); - } - break; - } - } - } - return Expr_Expr; + return check_call_expr_as_type_cast(c, operand, call, args, type_hint); } if (operand->mode == Addressing_Builtin) { diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 8e5efcb52..b181788b9 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -3256,7 +3256,7 @@ gb_internal lbAddr lb_add_global_generated_with_name(lbModule *m, Type *type, lb LLVMTypeRef actual_type = lb_type(m, type); if (value.value != nullptr) { LLVMTypeRef value_type = LLVMTypeOf(value.value); - GB_ASSERT(lb_sizeof(actual_type) == lb_sizeof(value_type)); + GB_ASSERT_MSG(lb_sizeof(actual_type) == lb_sizeof(value_type), "%s vs %s", LLVMPrintTypeToString(actual_type), LLVMPrintTypeToString(value_type)); actual_type = value_type; } -- cgit v1.2.3 From cbab97fbd74834ef4b06126b8a6787479e177b24 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 28 Sep 2025 22:50:36 +0100 Subject: Use `memcpy` for local constant slice arrays from a global constant --- src/llvm_backend_const.cpp | 202 ++++++------------------------------------- src/llvm_backend_expr.cpp | 18 ---- src/llvm_backend_general.cpp | 5 +- 3 files changed, 30 insertions(+), 195 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 66e2e50aa..dc555a7bd 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -641,161 +641,6 @@ gb_internal Slice lb_construct_const_union_flatten_values(lbModule return {}; } -gb_internal LLVMValueRef lb_construct_const_union(lbModule *m, LLVMValueRef variant_value, Type *variant_type, Type *union_type) { -#if 1 - return nullptr; -#else - Type *bt = base_type(union_type); - GB_ASSERT(bt->kind == Type_Union); - GB_ASSERT(lb_type(m, variant_type) == LLVMTypeOf(variant_value)); - - LLVMTypeRef llvm_type = lb_type(m, union_type); - - if (LLVMIsNull(variant_value)) { - return LLVMConstNull(llvm_type); - } - - if (bt->Union.variants.count == 0) { - GB_ASSERT(LLVMIsNull(variant_value)); - return variant_value; - } - - i64 block_size = bt->Union.variant_block_size; - i64 variant_size = type_size_of(variant_type); - - LLVMTypeRef llvm_variant_type = lb_type(m, variant_type); - - if (is_type_union_maybe_pointer(bt)) { - GB_ASSERT(lb_sizeof(LLVMTypeOf(variant_value)) == lb_sizeof(llvm_type)); - return LLVMConstBitCast(variant_value, llvm_type); - } - - if (bt->Union.variants.count == 1) { - unsigned long long the_tag = cast(unsigned long long)union_variant_index(union_type, variant_type); - LLVMTypeRef tag_type = lb_type(m, union_tag_type(bt)); - - LLVMValueRef values[3] = {}; - unsigned i = 0; - values[i++] = variant_value; - values[i++] = LLVMConstInt(tag_type, the_tag, false); - - i64 used_size = block_size + lb_sizeof(tag_type); - i64 padding = type_size_of(union_type) - used_size; - i64 align = type_align_of(union_type); - if (padding > 0) { - LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align); - values[i++] = LLVMConstNull(padding_type); - } - - return LLVMConstNamedStruct(llvm_type, values, i); - } else if (true) { - // TODO(bill): ignore this for the time being - return nullptr; - } - - LLVMTypeRef block_type = LLVMStructGetTypeAtIndex(llvm_type, 0); - - LLVMTypeRef tag_type = lb_type(m, union_tag_type(bt)); - - i64 used_size = block_size + lb_sizeof(tag_type); - i64 padding = type_size_of(union_type) - used_size; - i64 align = type_align_of(union_type); - LLVMTypeRef padding_type = nullptr; - if (padding > 0) { - padding_type = lb_type_padding_filler(m, padding, align); - } - - - unsigned i = 0; - LLVMValueRef values[3] = {}; - - LLVMValueRef block_value = variant_value; - - if (block_size == 0) { - block_value = LLVMConstNull(block_type); - } else if (lb_sizeof(llvm_variant_type) == 0) { - block_value = LLVMConstNull(block_type); - } else if (block_type != llvm_variant_type) { - - LLVMTypeKind block_kind = LLVMGetTypeKind(block_type); - LLVMTypeKind variant_kind = LLVMGetTypeKind(llvm_variant_type); - - - if (block_kind == LLVMArrayTypeKind) { - LLVMTypeRef elem = LLVMGetElementType(block_type); - unsigned count = LLVMGetArrayLength(block_type); - - Slice partial_elems = lb_construct_const_union_flatten_values(m, variant_value, variant_type, elem); - if (partial_elems.count == count) { - block_value = LLVMConstArray(elem, partial_elems.data, count); - goto assign_value_wrapped; - } - - Slice full_elems = temporary_slice_make(count); - slice_copy(&full_elems, partial_elems); - for (isize j = partial_elems.count; j < count; j++) { - full_elems[j] = LLVMConstNull(elem); - } - block_value = LLVMConstArray(elem, full_elems.data, count); - goto assign_value_wrapped; - - } else if (block_size != variant_size) { - if (block_kind == LLVMIntegerTypeKind && !is_type_different_to_arch_endianness(variant_type)) { - Slice partial_elems = lb_construct_const_union_flatten_values(m, variant_value, variant_type, block_type); - if (partial_elems.count == 1) { - block_value = partial_elems[0]; - goto assign_value_wrapped; - } - } - - return nullptr; - } - if (block_kind == LLVMIntegerTypeKind) { - GB_ASSERT(block_size == variant_size); - - switch (variant_kind) { - case LLVMHalfTypeKind: - case LLVMFloatTypeKind: - case LLVMDoubleTypeKind: - block_value = LLVMConstBitCast(block_value, block_type); - goto assign_value_wrapped; - case LLVMPointerTypeKind: - block_value = LLVMConstPtrToInt(block_value, block_type); - goto assign_value_wrapped; - } - } - - return nullptr; - } else { - // TODO(bill): ignore this for the time being - return nullptr; - } - -assign_value_wrapped:; - values[i++] = block_value; - - unsigned long long the_tag = cast(unsigned long long)union_variant_index(union_type, variant_type); - values[i++] = LLVMConstInt(tag_type, the_tag, false); - if (padding > 0) { - values[i++] = LLVMConstNull(padding_type); - } - return LLVMConstNamedStruct(llvm_type, values, i); -#endif -} - -gb_internal bool lb_try_construct_const_union(lbModule *m, lbValue *value, Type *variant_type, Type *union_type) { - if (lb_is_const(*value)) { - LLVMValueRef res = lb_construct_const_union(m, value->value, variant_type, union_type); - if (res != nullptr) { - *value = {res, union_type}; - return true; - } - // gb_printf_err("%s -> %s\n", LLVMPrintValueToString(value->value), LLVMPrintTypeToString(lb_type(m, union_type))); - } - return false; -} - - gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc, Type *value_type) { if (cc.allow_local) { cc.is_rodata = false; @@ -888,6 +733,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb Type *tag_type = union_tag_type(bt); LLVMTypeRef llvm_tag_type = lb_type(m, tag_type); i64 tag_index = union_variant_index(bt, variant_type); + GB_ASSERT(tag_index >= 0); values[value_count++] = LLVMConstInt(llvm_tag_type, tag_index, false); i64 used_size = block_size + type_size_of(tag_type); i64 union_size = type_size_of(bt); @@ -946,27 +792,42 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb count = gb_max(cast(isize)cl->max_count, count); Type *elem = base_type(type)->Slice.elem; Type *t = alloc_type_array(elem, count); - lbValue backing_array = lb_const_value(m, t, value, cc); + lbValue backing_array = lb_const_value(m, t, value, cc, nullptr); LLVMValueRef array_data = nullptr; + u32 id = m->global_array_index.fetch_add(1); + gbString str = gb_string_make(temporary_allocator(), "csba$"); + str = gb_string_appendc(str, m->module_name); + str = gb_string_append_fmt(str, "$%x", id); + + String name = make_string(cast(u8 const *)str, gb_string_length(str)); + + Entity *e = alloc_entity_constant(nullptr, make_token_ident(name), t, value); + array_data = LLVMAddGlobal(m->mod, LLVMTypeOf(backing_array.value), str); + LLVMSetInitializer(array_data, backing_array.value); + + + if (is_local) { + LLVMSetGlobalConstant(array_data, true); + // NOTE(bill, 2020-06-08): This is a bit of a hack but a "constant" slice needs // its backing data on the stack lbProcedure *p = m->curr_procedure; - LLVMTypeRef llvm_type = lb_type(m, t); - array_data = llvm_alloca(p, llvm_type, 16); - - { - LLVMValueRef ptr = array_data; - ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(LLVMTypeOf(backing_array.value), 0), ""); - LLVMBuildStore(p->builder, backing_array.value, ptr); - } + // NOTE(bill, 2025-09-28): make the array data global BUT memcpy it + // to make a local copy + LLVMTypeRef llvm_type = lb_type(m, t); + LLVMValueRef local_copy = llvm_alloca(p, llvm_type, 16); + LLVMBuildMemCpy(p->builder, + local_copy, 16, + array_data, 1, + LLVMConstInt(lb_type(m, t_int), type_size_of(t), false)); { LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)}; - LLVMValueRef ptr = LLVMBuildInBoundsGEP2(p->builder, llvm_type, array_data, indices, 2, ""); + LLVMValueRef ptr = LLVMBuildInBoundsGEP2(p->builder, llvm_type, local_copy, indices, 2, ""); LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), count, true); lbAddr slice = lb_add_local_generated(p, original_type, false); @@ -976,17 +837,6 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb return lb_addr_load(p, slice); } } else { - u32 id = m->global_array_index.fetch_add(1); - gbString str = gb_string_make(temporary_allocator(), "csba$"); - str = gb_string_appendc(str, m->module_name); - str = gb_string_append_fmt(str, "$%x", id); - - String name = make_string(cast(u8 const *)str, gb_string_length(str)); - - Entity *e = alloc_entity_constant(nullptr, make_token_ident(name), t, value); - array_data = LLVMAddGlobal(m->mod, LLVMTypeOf(backing_array.value), str); - LLVMSetInitializer(array_data, backing_array.value); - if (cc.link_section.len > 0) { LLVMSetSection(array_data, alloc_cstring(permanent_allocator(), cc.link_section)); } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 0e8562194..187c34595 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2495,13 +2495,6 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { Type *vt = dst->Union.variants[0]; if (internal_check_is_assignable_to(src_type, vt)) { value = lb_emit_conv(p, value, vt); - if (lb_is_const(value)) { - LLVMValueRef res = lb_construct_const_union(m, value.value, vt, t); - if (res != nullptr) { - return {res, t}; - } - } - lbAddr parent = lb_add_local_generated(p, t, true); lb_emit_store_union_variant(p, parent.addr, value, vt); return lb_addr_load(p, parent); @@ -2509,19 +2502,11 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { } for (Type *vt : dst->Union.variants) { if (src_type == t_llvm_bool && is_type_boolean(vt)) { - value = lb_emit_conv(p, value, vt); - if (lb_try_construct_const_union(m, &value, vt, t)) { - return value; - } - lbAddr parent = lb_add_local_generated(p, t, true); lb_emit_store_union_variant(p, parent.addr, value, vt); return lb_addr_load(p, parent); } if (are_types_identical(src_type, vt)) { - if (lb_try_construct_const_union(m, &value, vt, t)) { - return value; - } lbAddr parent = lb_add_local_generated(p, t, true); lb_emit_store_union_variant(p, parent.addr, value, vt); return lb_addr_load(p, parent); @@ -2559,9 +2544,6 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { if (valid_count == 1) { Type *vt = dst->Union.variants[first_success_index]; value = lb_emit_conv(p, value, vt); - if (lb_try_construct_const_union(m, &value, vt, t)) { - return value; - } lbAddr parent = lb_add_local_generated(p, t, true); lb_emit_store_union_variant(p, parent.addr, value, vt); return lb_addr_load(p, parent); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index b181788b9..123af51f5 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1493,8 +1493,11 @@ gb_internal lbValue lb_emit_union_tag_ptr(lbProcedure *p, lbValue u) { unsigned element_count = LLVMCountStructElementTypes(uvt); GB_ASSERT_MSG(element_count >= 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt)); + LLVMValueRef ptr = u.value; + ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(uvt, 0), ""); + lbValue tag_ptr = {}; - tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, u.value, 1, ""); + tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 1, ""); tag_ptr.type = alloc_type_pointer(tag_type); return tag_ptr; } -- cgit v1.2.3 From 34d040cef18635b95f5bc13bef793c35ff0647ed Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 29 Sep 2025 16:51:38 +0100 Subject: Correct format strings --- src/llvm_backend_general.cpp | 29 ++++++++++++++++++++++++++--- src/llvm_backend_utility.cpp | 26 ++++++++++++++++++++------ 2 files changed, 46 insertions(+), 9 deletions(-) (limited to 'src/llvm_backend_general.cpp') diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 123af51f5..39cf70a6a 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -186,9 +186,32 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { lb_init_module(pm, do_threading); } - if (pkg->kind == Package_Runtime) { - // allow this to be per file - } else if (!module_per_file) { + bool allow_for_per_file = pkg->kind == Package_Runtime || module_per_file; + + #if 0 + if (!allow_for_per_file) { + if (pkg->files.count >= 20) { + isize proc_count = 0; + for (Entity *e : gen->info->entities) { + if (e->kind != Entity_Procedure) { + continue; + } + if (e->Procedure.is_foreign) { + continue; + } + if (e->pkg == pkg) { + proc_count += 1; + } + } + + if (proc_count >= 300) { + allow_for_per_file = true; + } + } + } + #endif + + if (!allow_for_per_file) { continue; } // NOTE(bill): Probably per file is not a good idea, so leave this for later diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index fa8f85d0f..155c55675 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2897,7 +2897,7 @@ gb_internal void lb_do_para_poly_diagnostics(lbGenerator *gen) { f64 average = cast(f64)code_size / cast(f64)gb_max(count, 1); - gb_printf("%23td | %19d | %25.2f | %.*s\n", code_size, count, average, LIT(name)); + gb_printf("%23td | %19td | %25.2f | %.*s\n", code_size, count, average, LIT(name)); if (max_count-- <= 0) { break; } @@ -2930,7 +2930,7 @@ gb_internal void lb_do_para_poly_diagnostics(lbGenerator *gen) { f64 average = cast(f64)code_size / cast(f64)gb_max(count, 1); - gb_printf("%19d | %23td | %25.2f | %.*s\n", count, code_size, average, LIT(name)); + gb_printf("%19td | %23td | %25.2f | %.*s\n", count, code_size, average, LIT(name)); if (max_count-- <= 0) { break; } @@ -2989,8 +2989,8 @@ gb_internal void lb_do_module_diagnostics(lbGenerator *gen) { array_init(&modules, heap_allocator()); defer (array_free(&modules)); - for (auto &entry : gen->modules) { - lbModule *m = entry.value; + for (auto &em : gen->modules) { + lbModule *m = em.value; { lbDiagModuleEntry entry = {}; @@ -3040,14 +3040,28 @@ gb_internal void lb_do_module_diagnostics(lbGenerator *gen) { }); gb_printf("Module Diagnostics\n\n"); - gb_printf("Total Instructions | Global Internals | Global Externals | Proc Internals | Proc Externals | Module Name\n"); + gb_printf("Total Instructions | Global Internals | Global Externals | Proc Internals | Proc Externals | Files | Instructions/File | Instructions/Proc | Module Name\n"); + gb_printf("-------------------+------------------+------------------+----------------+----------------+-------+-------------------+-------------------+------------\n"); for (auto &entry : modules) { - gb_printf("%18td | %16td | %16td | %14td | %14d | %s \n", + isize file_count = 1; + if (entry.m->file != nullptr) { + file_count = 1; + } else if (entry.m->pkg) { + file_count = entry.m->pkg->files.count; + } + + f64 instructions_per_file = cast(f64)entry.total_instruction_count / gb_max(1.0, cast(f64)file_count); + f64 instructions_per_proc = cast(f64)entry.total_instruction_count / gb_max(1.0, cast(f64)entry.proc_internal_count); + + gb_printf("%18td | %16td | %16td | %14td | %14td | %5td | %17.1f | %17.1f | %s \n", entry.total_instruction_count, entry.global_internal_count, entry.global_external_count, entry.proc_internal_count, entry.proc_external_count, + file_count, + instructions_per_file, + instructions_per_proc, entry.m->module_name); } -- cgit v1.2.3