From 29106aaa1e14a2aa82a99363837405b9be293353 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 3 Oct 2026 01:26:01 +0100 Subject: [PATCH] Cache type layouts and union constantability, build nested constants once, create debug files lazily --- src/llvm_abi.cpp | 84 +++++++++++++++++------------------- src/llvm_backend.cpp | 18 +++----- src/llvm_backend_const.cpp | 65 +++++++++++++++++++++++++++- src/llvm_backend_debug.cpp | 29 ++++++++++--- src/llvm_backend_general.cpp | 13 +++--- src/llvm_backend_proc.cpp | 6 +-- src/llvm_backend_stmt.cpp | 2 +- src/types.cpp | 35 +++++++++++---- 8 files changed, 168 insertions(+), 84 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index f4c91256e..fd60d5482 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -240,6 +240,44 @@ gb_internal void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType } +struct lbTypeLayout { + i64 size; + i64 align; +}; + +gb_thread_local PtrMap lb_aggregate_layouts; + +gb_internal lbTypeLayout lb_aggregate_layout(LLVMTypeRef type) { + if (lbTypeLayout *found = map_get(&lb_aggregate_layouts, type)) { + return *found; + } + lbTypeLayout layout = {0, 1}; + if (LLVMGetTypeKind(type) == LLVMArrayTypeKind) { + LLVMTypeRef elem = OdinLLVMGetArrayElementType(type); + layout.size = cast(i64)LLVMGetArrayLength(type) * lb_sizeof(elem); + layout.align = lb_alignof(elem); + } else { + GB_ASSERT(LLVMGetTypeKind(type) == LLVMStructTypeKind); + if (LLVMIsOpaqueStruct(type)) { + return layout; + } + bool is_packed = LLVMIsPackedStruct(type); + unsigned field_count = LLVMCountStructElementTypes(type); + for (unsigned i = 0; i < field_count; i++) { + LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i); + if (!is_packed) { + i64 field_align = lb_alignof(field); + layout.size = llvm_align_formula(layout.size, field_align); + layout.align = gb_max(layout.align, field_align); + } + layout.size += lb_sizeof(field); + } + layout.size = llvm_align_formula(layout.size, layout.align); + } + map_set(&lb_aggregate_layouts, type, layout); + return layout; +} + gb_internal i64 lb_sizeof(LLVMTypeRef type) { LLVMTypeKind kind = LLVMGetTypeKind(type); switch (kind) { @@ -259,35 +297,8 @@ gb_internal i64 lb_sizeof(LLVMTypeRef type) { case LLVMPointerTypeKind: return build_context.ptr_size; case LLVMStructTypeKind: - { - unsigned field_count = LLVMCountStructElementTypes(type); - i64 offset = 0; - if (LLVMIsPackedStruct(type)) { - for (unsigned i = 0; i < field_count; i++) { - LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i); - offset += lb_sizeof(field); - } - } else { - for (unsigned i = 0; i < field_count; i++) { - LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i); - i64 align = lb_alignof(field); - offset = llvm_align_formula(offset, align); - offset += lb_sizeof(field); - } - offset = llvm_align_formula(offset, lb_alignof(type)); - } - return offset; - } - break; case LLVMArrayTypeKind: - { - LLVMTypeRef elem = OdinLLVMGetArrayElementType(type); - i64 elem_size = lb_sizeof(elem); - i64 count = LLVMGetArrayLength(type); - i64 size = count * elem_size; - return size; - } - break; + return lb_aggregate_layout(type).size; #if LLVM_VERSION_MAJOR < 20 case LLVMX86_MMXTypeKind: @@ -327,23 +338,8 @@ gb_internal i64 lb_alignof(LLVMTypeRef type) { case LLVMPointerTypeKind: return build_context.ptr_size; case LLVMStructTypeKind: - { - if (LLVMIsPackedStruct(type)) { - return 1; - } else { - unsigned field_count = LLVMCountStructElementTypes(type); - i64 max_align = 1; - for (unsigned i = 0; i < field_count; i++) { - LLVMTypeRef field = LLVMStructGetTypeAtIndex(type, i); - i64 field_align = lb_alignof(field); - max_align = gb_max(max_align, field_align); - } - return max_align; - } - } - break; case LLVMArrayTypeKind: - return lb_alignof(OdinLLVMGetArrayElementType(type)); + return lb_aggregate_layout(type).align; #if LLVM_VERSION_MAJOR < 20 case LLVMX86_MMXTypeKind: diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index f2057f6dc..031a71f3d 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2316,6 +2316,8 @@ gb_internal GB_COMPARE_PROC(llvm_global_entity_cmp) { return lb_entity_type_cmp(x, y); } +gb_internal Array lb_modules_by_cost(lbGenerator *gen); + gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, CheckerInfo *info, bool do_threading) { for (Entity *e : info->entities) { String name = e->token.string; @@ -2385,12 +2387,12 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker lbModule *m = entry.value; array_sort(m->global_types_to_create, llvm_global_entity_cmp); array_sort(m->global_procedures_to_create, llvm_global_entity_cmp); + m->estimated_cost = m->global_types_to_create.count + m->global_procedures_to_create.count; } gen->modules_in_parallel = true; if (do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; + for (lbModule *m : lb_modules_by_cost(gen)) { thread_pool_add_task(lb_generate_procedures_and_types_per_module, m); } } else { @@ -3346,14 +3348,6 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { for (auto const &entry : gen->modules) { lbModule *m = entry.value; if (m->debug_builder) { // Debug Info - for (auto const &file_entry : info->files) { - AstFile *f = file_entry.value; - LLVMMetadataRef res = LLVMDIBuilderCreateFile(m->debug_builder, - cast(char const *)f->filename.text, f->filename.len, - cast(char const *)f->directory.text, f->directory.len); - lb_set_llvm_metadata(m, f, res); - } - TEMPORARY_ALLOCATOR_GUARD(); gbString producer = gb_string_make(temporary_allocator(), "odin"); @@ -3382,7 +3376,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { LLVMBool debug_info_for_profiling = false; m->debug_compile_unit = LLVMDIBuilderCreateCompileUnit(m->debug_builder, LLVMDWARFSourceLanguageC99, - lb_get_llvm_metadata(m, init_file), + lb_get_file_metadata(m, init_file), producer, gb_string_length(producer), is_optimized, "", 0, 1, split_name, gb_string_length(split_name), @@ -3619,7 +3613,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { if (m->debug_builder) { String global_name = e->token.string; if (global_name.len != 0 && global_name != "_") { - LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, e->file); + LLVMMetadataRef llvm_file = lb_get_file_metadata(m, e->file); LLVMMetadataRef llvm_scope = llvm_file; LLVMBool local_to_unit = LLVMGetLinkage(g.value) == LLVMInternalLinkage; diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index df674b913..aa080dca8 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -239,6 +239,60 @@ gb_internal LLVMValueRef llvm_const_array(lbModule *m, LLVMTypeRef elem_type, LL return LLVMConstArray(elem_type, values, value_count); } +// Each constant `insertvalue` rebuilds and uniques the whole aggregate, +// so the nested fields of a compound literal are collected and built once. +struct lbConstAggregate { + LLVMValueRef value; + lbConstAggregate *elems; + unsigned elem_count; +}; + +gb_internal void lb_const_aggregate_insert(lbModule *m, lbConstAggregate *agg, LLVMValueRef base, LLVMValueRef val, unsigned *indices, isize count) { + if (agg->elems == nullptr) { + agg->value = base; + } + for (isize i = 0; i < count; i++) { + if (agg->elems == nullptr) { + LLVMTypeRef type = LLVMTypeOf(agg->value); + if (LLVMGetTypeKind(type) == LLVMArrayTypeKind) { + agg->elem_count = cast(unsigned)LLVMGetArrayLength(type); + } else { + agg->elem_count = LLVMCountStructElementTypes(type); + } + agg->elems = gb_alloc_array(temporary_allocator(), lbConstAggregate, agg->elem_count); + for (unsigned j = 0; j < agg->elem_count; j++) { + agg->elems[j].value = llvm_const_extract_value(m, agg->value, j); + } + } + agg = &agg->elems[indices[i]]; + } + agg->value = val; + agg->elems = nullptr; +} + +gb_internal LLVMValueRef lb_const_aggregate_build(lbModule *m, lbConstAggregate *agg) { + if (agg->elems == nullptr) { + return agg->value; + } + LLVMTypeRef type = LLVMTypeOf(agg->value); + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, agg->elem_count); + for (unsigned i = 0; i < agg->elem_count; i++) { + values[i] = lb_const_aggregate_build(m, &agg->elems[i]); + } + if (LLVMGetTypeKind(type) == LLVMArrayTypeKind) { + return llvm_const_array(m, OdinLLVMGetArrayElementType(type), values, agg->elem_count); + } + return llvm_const_named_struct_internal(m, type, values, agg->elem_count); +} + +gb_internal LLVMValueRef lb_const_aggregate_take(lbModule *m, lbConstAggregate *agg, LLVMValueRef value) { + if (agg->elems != nullptr) { + value = lb_const_aggregate_build(m, agg); + } + *agg = {}; + return value; +} + gb_internal LLVMValueRef llvm_const_slice_internal(lbModule *m, LLVMValueRef data, LLVMValueRef len) { if (build_context.metrics.ptr_size < build_context.metrics.int_size) { GB_ASSERT(build_context.metrics.ptr_size == 4); @@ -2005,6 +2059,8 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb bool *visited = gb_alloc_array(temporary_allocator(), bool, value_count); if (cl->elems[0]->kind == Ast_FieldValue) { + lbConstAggregate *nested = gb_alloc_array(temporary_allocator(), lbConstAggregate, value_count); + isize elem_count = cl->elems.count; for (isize i = 0; i < elem_count; i++) { ast_node(fv, FieldValue, cl->elems[i]); @@ -2026,6 +2082,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb GB_ASSERT_MSG(lb_sizeof(value_type) == type_size_of(f->type), "%s vs %s", LLVMPrintTypeToString(value_type), type_to_string(f->type)); values[index] = value.value; visited[index] = true; + nested[index] = {}; } else { if (!visited[index]) { auto new_cc = cc; @@ -2073,13 +2130,15 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb if (LLVMIsConstant(elem_value) && LLVMIsConstant(values[index])) { if (is_type_union(cv_type) || is_type_raw_union(cv_type)) { force_non_named = true; + values[index] = lb_const_aggregate_take(m, &nested[index], values[index]); values[index] = llvm_const_insert_value_with_rebuild(m, values[index], elem_value, idx_list, idx_list_len); } else { - values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len); + lb_const_aggregate_insert(m, &nested[index], values[index], elem_value, idx_list, idx_list_len); } } else if (is_local) { lbProcedure *p = m->curr_procedure; GB_ASSERT(p != nullptr); + values[index] = lb_const_aggregate_take(m, &nested[index], values[index]); LLVMTypeRef field_llvm_type = lb_type(m, f->type); @@ -2123,6 +2182,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb } } } + + for (unsigned i = 0; i < value_count; i++) { + values[i] = lb_const_aggregate_take(m, &nested[i], values[i]); + } } else { isize multiple_return_offset = 0; for_array(i, cl->elems) { diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index b70071e5b..2b1cbf872 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -18,6 +18,21 @@ gb_internal void lb_set_llvm_metadata(lbModule *m, void *key, LLVMMetadataRef va } } +gb_internal LLVMMetadataRef lb_get_file_metadata(lbModule *m, AstFile *f) { + if (f == nullptr || m->debug_builder == nullptr) { + return nullptr; + } + MUTEX_GUARD(&m->debug_values_mutex); + LLVMMetadataRef res = lb_get_llvm_metadata(m, f); + if (res == nullptr) { + res = LLVMDIBuilderCreateFile(m->debug_builder, + cast(char const *)f->filename.text, f->filename.len, + cast(char const *)f->directory.text, f->directory.len); + lb_set_llvm_metadata(m, f, res); + } + return res; +} + gb_internal void lb_add_raddbg_string(lbModule *m, String const &str) { mpsc_enqueue(&m->gen->raddebug_section_strings, copy_string(permanent_allocator(), str)); } @@ -70,7 +85,7 @@ gb_internal LLVMMetadataRef lb_debug_end_location_from_ast(lbProcedure *p, Ast * gb_internal void lb_debug_file_line(lbModule *m, Type *type, Ast *node, LLVMMetadataRef *file, unsigned *line) { if (*file == nullptr && type->kind == Type_Named) { if (node) { - *file = lb_get_llvm_metadata(m, node->file()); + *file = lb_get_file_metadata(m, node->file()); *line = cast(unsigned)ast_token(node).pos.line; } } @@ -174,7 +189,7 @@ gb_internal LLVMMetadataRef lb_debug_struct_field(lbModule *m, String const &nam AstPackage *pkg = m->info->runtime_package; GB_ASSERT(pkg->files.count != 0); - LLVMMetadataRef file = lb_get_llvm_metadata(m, pkg->files[0]); + LLVMMetadataRef file = lb_get_file_metadata(m, pkg->files[0]); LLVMMetadataRef scope = file; return LLVMDIBuilderCreateMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, field_line, @@ -185,7 +200,7 @@ gb_internal LLVMMetadataRef lb_debug_struct_field(lbModule *m, String const &nam gb_internal LLVMMetadataRef lb_debug_basic_struct(lbModule *m, String const &name, u64 size_in_bits, u32 align_in_bits, LLVMMetadataRef *elements, unsigned element_count) { AstPackage *pkg = m->info->runtime_package; GB_ASSERT(pkg->files.count != 0); - LLVMMetadataRef file = lb_get_llvm_metadata(m, pkg->files[0]); + LLVMMetadataRef file = lb_get_file_metadata(m, pkg->files[0]); LLVMMetadataRef scope = file; return LLVMDIBuilderCreateStructType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, 1, size_in_bits, align_in_bits, LLVMDIFlagZero, nullptr, elements, element_count, 0, nullptr, "", 0); @@ -1119,7 +1134,7 @@ gb_internal LLVMMetadataRef lb_get_base_scope_metadata(lbModule *m, Scope *scope } } if (scope->flags & ScopeFlag_File) { - found = lb_get_llvm_metadata(m, scope->file); + found = lb_get_file_metadata(m, scope->file); if (found) { return found; } @@ -1219,7 +1234,7 @@ gb_internal void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, T AstFile *file = p->body->file(); LLVMMetadataRef llvm_scope = lb_get_current_debug_scope(p); - LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file); + LLVMMetadataRef llvm_file = lb_get_file_metadata(m, file); GB_ASSERT(llvm_scope != nullptr); if (llvm_file == nullptr) { llvm_file = LLVMDIScopeGetFile(llvm_scope); @@ -1286,7 +1301,7 @@ gb_internal void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, T AstFile *file = p->body->file(); LLVMMetadataRef llvm_scope = lb_get_current_debug_scope(p); - LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file); + LLVMMetadataRef llvm_file = lb_get_file_metadata(m, file); GB_ASSERT(llvm_scope != nullptr); if (llvm_file == nullptr) { llvm_file = LLVMDIScopeGetFile(llvm_scope); @@ -1479,7 +1494,7 @@ gb_internal void lb_add_debug_label(lbProcedure *p, Ast *label, lbBlock *target) } AstFile *file = label->file(); - LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file); + LLVMMetadataRef llvm_file = lb_get_file_metadata(m, file); if (llvm_file == nullptr) { debugf("llvm file not found for label\n"); return; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index fd9964529..6b7fefcc6 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -407,6 +407,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { if (global_error_collector.count != 0) { return false; } + global_types_are_complete.store(true); isize tc = c->parser->total_token_count; if (tc < 2) { @@ -700,20 +701,16 @@ gb_internal lbValue lb_zero(lbModule *m, Type *t) { return v; } gb_internal LLVMValueRef llvm_const_extract_value(lbModule *m, LLVMValueRef agg, unsigned index) { - LLVMValueRef res = agg; - GB_ASSERT(LLVMIsConstant(res)); - res = LLVMBuildExtractValue(m->const_dummy_builder, res, index, ""); - GB_ASSERT(LLVMIsConstant(res)); + GB_ASSERT(LLVMIsConstant(agg)); + LLVMValueRef res = LLVMGetAggregateElement(agg, index); + GB_ASSERT(res != nullptr); return res; } gb_internal LLVMValueRef llvm_const_extract_value(lbModule *m, LLVMValueRef agg, unsigned *indices, isize count) { - // return LLVMConstExtractValue(value, indices, count); LLVMValueRef res = agg; - GB_ASSERT(LLVMIsConstant(res)); for (isize i = 0; i < count; i++) { - res = LLVMBuildExtractValue(m->const_dummy_builder, res, indices[i], ""); - GB_ASSERT(LLVMIsConstant(res)); + res = llvm_const_extract_value(m, res, indices[i]); } return res; } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index a135904d9..a5f8ba66a 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -362,13 +362,13 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i Ast *ident = entity->identifier.load(); if (entity->file != nullptr) { - file = lb_get_llvm_metadata(m, entity->file); + file = lb_get_file_metadata(m, entity->file); scope = file; } else if (ident != nullptr && ident->file_id != 0) { - file = lb_get_llvm_metadata(m, ident->file()); + file = lb_get_file_metadata(m, ident->file()); scope = file; } else if (entity->scope != nullptr) { - file = lb_get_llvm_metadata(m, entity->scope->file); + file = lb_get_file_metadata(m, entity->scope->file); scope = file; } GB_ASSERT_MSG(file != nullptr, "%.*s", LIT(entity->token.string)); diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 025de69ad..d35b48835 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -334,7 +334,7 @@ gb_internal void lb_open_scope(lbProcedure *p, Scope *s, bool lifetime_scope=fal LLVMMetadataRef file = nullptr; AstFile *ast_file = s->node->file(); if (ast_file != nullptr) { - file = lb_get_llvm_metadata(m, ast_file); + file = lb_get_file_metadata(m, ast_file); } LLVMMetadataRef scope = nullptr; if (p->scope_stack.count > 0) { diff --git a/src/types.cpp b/src/types.cpp index 6067cd1d6..015b61ed3 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -188,6 +188,7 @@ struct TypeUnion { bool is_polymorphic; bool is_poly_specialized; UnionTypeKind kind; + std::atomic constantable; // 0 unknown, 1 false, 2 true }; struct TypeProc { @@ -417,6 +418,9 @@ gb_internal Type *base_type(Type *t); gb_internal Type *alloc_type_multi_pointer(Type *elem); gb_internal void wait_for_record_signal(Wait_Signal *signal, Futex *checking_thread); +// set once checking is done; until then a type may still be incomplete or part of an illegal cycle +gb_global std::atomic global_types_are_complete; + gb_internal u32 type_info_flags_of_type(Type *type) { if (type == nullptr) { return 0; @@ -2787,16 +2791,25 @@ 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; - } - - for (Type *v : bt->Union.variants) { - if (!is_type_constant_type_for_unions(v)) { - return false; + bool use_cache = global_types_are_complete.load(std::memory_order_relaxed); + if (use_cache) { + u8 cached = bt->Union.constantable.load(std::memory_order_relaxed); + if (cached != 0) { + return cached == 2; } } - return true; + + bool res = true; + for (Type *v : bt->Union.variants) { + if (!is_type_constant_type_for_unions(v)) { + res = false; + break; + } + } + if (use_cache) { + bt->Union.constantable.store(res ? 2 : 1, std::memory_order_relaxed); + } + return res; } gb_internal bool is_type_raw_union_constantable(Type *type) { @@ -4389,6 +4402,9 @@ gb_internal i64 type_size_of_struct_pretend_is_packed(Type *ot) { gb_internal i64 type_size_of(Type *t) { + if (t != nullptr && t->kind == Type_Named && global_types_are_complete.load(std::memory_order_relaxed)) { + t = base_type(t); + } if (t == nullptr) { return 0; } @@ -4431,6 +4447,9 @@ gb_internal i64 type_size_of(Type *t) { } gb_internal i64 type_align_of(Type *t) { + if (t != nullptr && t->kind == Type_Named && global_types_are_complete.load(std::memory_order_relaxed)) { + t = base_type(t); + } if (t == nullptr) { return 1; }