From 1c0e9030fade8a57600ead71f911cff45c50dfdf Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Tue, 20 Jan 2026 15:27:31 -0800 Subject: [PATCH] move lib member queue state to the link step and release it after link step is done --- src/linker/hash_table.h | 1 + src/linker/lnk.c | 101 +++++++++++++++++++++++++++++++--------- src/linker/lnk.h | 31 ++++++++++-- src/linker/lnk_lib.c | 39 ---------------- src/linker/lnk_lib.h | 12 ----- 5 files changed, 107 insertions(+), 77 deletions(-) diff --git a/src/linker/hash_table.h b/src/linker/hash_table.h index 3b1d0707..80f0ea30 100644 --- a/src/linker/hash_table.h +++ b/src/linker/hash_table.h @@ -66,6 +66,7 @@ internal BucketNode * hash_table_push_path_raw (Arena *arena, HashTable *ht, internal BucketNode * hash_table_push_path_u64 (Arena *arena, HashTable *ht, String8 path, U64 value); internal BucketNode * hash_table_push_u64_u64 (Arena *arena, HashTable *ht, U64 key, U64 value); internal BucketNode * hash_table_push_u32_u32 (Arena *arena, HashTable *ht, U32 key, U32 value); +internal BucketNode * hash_table_push_raw_raw (Arena *arena, HashTable *ht, void *key, void *value); internal BucketNode * hash_table_search_string (HashTable *ht, String8 key); internal BucketNode * hash_table_search_u32 (HashTable *ht, U32 key); diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 64098309..2f8c472d 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -1196,13 +1196,13 @@ lnk_lib_member_ref_list_concat_in_place_array(LNK_LibMemberRefList *list, LNK_Li SLLConcatInPlaceArray(list, to_concat_arr, count); } +static LNK_LibMemberInfo *g_sort_lib_member_context; + internal int lnk_lib_member_ref_is_before(void *raw_a, void *raw_b) { LNK_LibMemberRef **a = raw_a, **b = raw_b; - LNK_Symbol *a_pull_in_ref = (*a)->lib->member_links[(*a)->member_idx]; - LNK_Symbol *b_pull_in_ref = (*b)->lib->member_links[(*b)->member_idx]; - return lnk_symbol_is_before(a_pull_in_ref, b_pull_in_ref); + return lnk_symbol_is_before(g_sort_lib_member_context[(*a)->member_idx].link, g_sort_lib_member_context[(*b)->member_idx].link); } internal LNK_LibMemberRef ** @@ -1481,9 +1481,40 @@ lnk_load_inputs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer } internal void -lnk_queue_lib_member(Arena *arena, LNK_LibMemberRefList *queued_members, LNK_Symbol *link_symbol, LNK_Lib *lib, U32 member_idx) +lnk_queue_lib_member(Arena *arena, LNK_LibMemberRefList *queued_members, LNK_Symbol *link_symbol, LNK_Lib *lib, LNK_LibMemberInfo *member_infos, U32 member_idx) { - B32 was_linked = lnk_lib_set_link_symbol(lib, member_idx, link_symbol); + // associate link symbol to lib member + for (LNK_Symbol *leader = link_symbol;;) { + LNK_Symbol *slot = ins_atomic_ptr_eval_assign(&member_infos[member_idx].link, 0); + + // update slot symbol if it is empty or link symbol comes before symbol in the slot + if (slot) { + if (lnk_symbol_is_before(slot, leader)) { + leader = slot; + } + } else { + leader = link_symbol; + } + + // try to insert back updated slot symbol + LNK_Symbol *swap = ins_atomic_ptr_eval_cond_assign(&member_infos[member_idx].link, leader, 0); + + // exit if slot symbol was null + if (swap == 0) { + break; + } + } + + B32 was_linked; + if (str8_starts_with(link_symbol->name, str8_lit("__imp_"))) { + U8 member_flags = ins_atomic_u8_or(&member_infos[member_idx].flags, LNK_LibMemberFlag_LinkedImp); + was_linked = !(member_flags & LNK_LibMemberFlag_LinkedImp); + } else { + U8 flag = LNK_LibMemberFlag_LinkedRegular; + U8 member_flags = ins_atomic_u8_or(&member_infos[member_idx].flags, LNK_LibMemberFlag_LinkedRegular); + was_linked = !(member_flags & LNK_LibMemberFlag_LinkedRegular); + } + if (was_linked) { LNK_LibMemberRef *member_ref = push_array(arena, LNK_LibMemberRef, 1); member_ref->lib = lib; @@ -1500,6 +1531,7 @@ THREAD_POOL_TASK_FUNC(lnk_search_lib_task) LNK_Lib *lib = task->lib; LNK_SymbolTable *symtab = task->symtab; B32 search_anti_deps = task->search_anti_deps; + LNK_LibMemberInfo *lib_member_infos = task->lib_member_infos; LNK_LibMemberRefList *member_ref_list = &task->member_ref_lists[task_id]; for EachNode(c, LNK_SymbolHashTrieChunk, symtab->search_chunks[task_id].first) { @@ -1512,14 +1544,14 @@ THREAD_POOL_TASK_FUNC(lnk_search_lib_task) if (symbol_interp == COFF_SymbolValueInterp_Undefined) { U32 member_idx; if (lnk_search_lib(lib, symbol->name, &member_idx)) { - lnk_queue_lib_member(arena, member_ref_list, symbol, lib, member_idx); + lnk_queue_lib_member(arena, member_ref_list, symbol, lib, lib_member_infos, member_idx); } } else if (symbol_interp == COFF_SymbolValueInterp_Weak) { COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(symbol_parsed, symbol_ref.obj->header.is_big_obj); if (weak_ext->characteristics == COFF_WeakExt_SearchLibrary) { U32 member_idx; if (lnk_search_lib(lib, symbol->name, &member_idx)) { - lnk_queue_lib_member(arena, member_ref_list, symbol, lib, member_idx); + lnk_queue_lib_member(arena, member_ref_list, symbol, lib, lib_member_infos, member_idx); } } else if (weak_ext->characteristics == COFF_WeakExt_AntiDependency) { if (search_anti_deps) { @@ -1530,7 +1562,7 @@ THREAD_POOL_TASK_FUNC(lnk_search_lib_task) if (dep_interp == COFF_SymbolValueInterp_Weak) { U32 member_idx; if (lnk_search_lib(lib, symbol_parsed.name, &member_idx)) { - lnk_queue_lib_member(arena, member_ref_list, symbol, lib, member_idx); + lnk_queue_lib_member(arena, member_ref_list, symbol, lib, lib_member_infos, member_idx); } } } @@ -1560,13 +1592,19 @@ lnk_link_inputs(TP_Context *tp, for EachNode(lib_n, LNK_LibNode, link->libs.first) { LNK_Lib *lib = &lib_n->data; + LNK_LibMemberInfo *lib_member_infos = hash_table_search_raw_raw(link->lib_member_infos_ht, lib); + if (lib_member_infos == 0) { + lib_member_infos = push_array(link->arena, LNK_LibMemberInfo, lib->member_count); + hash_table_push_raw_raw(link->arena, link->lib_member_infos_ht, lib, lib_member_infos); + } + ProfBeginV("Search %S", str8_skip_last_slash(lib->path)); do { lnk_load_inputs(tp, arena, config, inputer, symtab, link); // search symbols in lib MemoryZeroTyped(member_ref_lists, tp->worker_count); - tp_for_parallel(tp, arena, tp->worker_count, lnk_search_lib_task, &(LNK_SearchLibTask){ .search_anti_deps = search_anti_deps, .lib = lib, .symtab = symtab, .member_ref_lists = member_ref_lists }); + tp_for_parallel(tp, arena, tp->worker_count, lnk_search_lib_task, &(LNK_SearchLibTask){ .search_anti_deps = search_anti_deps, .lib = lib, .symtab = symtab, .lib_member_infos = lib_member_infos, .member_ref_lists = member_ref_lists }); LNK_LibMemberRefList queued_members = {0}; lnk_lib_member_ref_list_concat_in_place_array(&queued_members, member_ref_lists, tp->worker_count); @@ -1574,6 +1612,7 @@ lnk_link_inputs(TP_Context *tp, // sort library member refs to match the order of their appearance in obj symbol tables LNK_LibMemberRef **member_refs = lnk_array_from_lib_member_list(scratch.arena, queued_members); //qsort(member_refs, queued_members.count, sizeof(member_refs[0]), lnk_lib_member_ref_compar); + g_sort_lib_member_context = lib_member_infos; radsort(member_refs, queued_members.count, lnk_lib_member_ref_is_before); if (queued_members.count) { @@ -1619,18 +1658,21 @@ lnk_link_inputs(TP_Context *tp, // same import symbol must never be queued more than once, if it is, there is a bug in the link set logic AssertAlways(member_ref->link_symbol->refs != import_stub->refs); - // replace the import symbol with a stub, which is later replaced with the real import symbol once import obj is ready + // replace the import symbol with a stub, which is later replaced with the real import symbol once import obj is ready. member_ref->link_symbol->refs = import_stub->refs; // push import member for import obj generation - if (!(lib->member_flags[member_ref->member_idx] & LNK_LibMemberFlag_WasQueued)) { - lib->member_flags[member_ref->member_idx] |= LNK_LibMemberFlag_WasQueued; + if (!(lib_member_infos[member_ref->member_idx].flags & LNK_LibMemberFlag_WasGenQueued)) { + lib_member_infos[member_ref->member_idx].flags |= LNK_LibMemberFlag_WasGenQueued; lnk_lib_member_ref_list_push_node(&link->imports, member_ref); } } break; case COFF_DataType_BigObj: case COFF_DataType_Obj: { if (lib->type == COFF_Archive_Thin) { + Assert(!(lib_member_infos[member_ref->member_idx].flags & LNK_LibMemberFlag_WasGenQueued)); + lib_member_infos[member_ref->member_idx].flags |= LNK_LibMemberFlag_WasGenQueued; + // obj path in thin archive is relative to the directory with lib String8List obj_path_list = {0}; str8_list_push(scratch.arena, &obj_path_list, str8_chop_last_slash(lib->path)); @@ -1696,7 +1738,7 @@ lnk_link_inputs(TP_Context *tp, ProfEnd(); } -internal LNK_Link * +internal LNK_LinkResult lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab) { ProfBeginFunction(); @@ -1706,11 +1748,13 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer // init link context // LNK_Link *link = push_array(arena->v[0], LNK_Link, 1); + link->arena = arena_alloc(); link->last_symbol_input = &link->objs.first; link->last_include = &config->include_symbol_list.first; link->last_default_lib = &config->input_default_lib_list.first; link->last_obj_lib = &config->input_obj_lib_list.first; link->last_cmd_lib = &config->input_list[LNK_Input_Lib].first; + link->lib_member_infos_ht = hash_table_init(link->arena, config->input_list[LNK_Input_Lib].node_count * 2); link->try_to_resolve_entry_point = 1; // input :null_obj @@ -1750,9 +1794,10 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer String8List static_dll_names = {0}; for EachNode(member_ref, LNK_LibMemberRef, link->imports.first) { - LNK_Lib *lib = member_ref->lib; - U64 member_idx = member_ref->member_idx; - LNK_Symbol *link_symbol = lib->member_links[member_idx]; + LNK_Lib *lib = member_ref->lib; + U64 member_idx = member_ref->member_idx; + LNK_LibMemberInfo *member_infos = hash_table_search_raw_raw(link->lib_member_infos_ht, lib); + LNK_Symbol *link_symbol = member_infos[member_idx].link; COFF_ArchiveMember member_info = coff_archive_member_from_offset(lib->data, lib->member_offsets[member_idx]); COFF_DataType member_type = coff_data_type_from_data(member_info.data); @@ -2130,6 +2175,18 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer config->infer_function_pad_min = 0; } + // + // fill out result + // + LNK_LinkResult result = {0}; + result.objs = link->objs; + result.libs = link->libs; + + // + // release link context + // + arena_release(link->arena); + // // log // @@ -2146,7 +2203,7 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer scratch_end(scratch); ProfEnd(); - return link; + return result; } internal void @@ -4975,12 +5032,12 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) // // Link Image // - LNK_Link *link = lnk_link_image(tp, arena, config, inputer, symtab); + LNK_LinkResult link = lnk_link_image(tp, arena, config, inputer, symtab); - U64 objs_count = link->objs.count; - U64 libs_count = link->libs.count; - LNK_Obj **objs = lnk_array_from_obj_list(scratch.arena, link->objs); - LNK_Lib **libs = lnk_array_from_lib_list(scratch.arena, link->libs); + U64 objs_count = link.objs.count; + U64 libs_count = link.libs.count; + LNK_Obj **objs = lnk_array_from_obj_list(scratch.arena, link.objs); + LNK_Lib **libs = lnk_array_from_lib_list(scratch.arena, link.libs); // // Layout Image diff --git a/src/linker/lnk.h b/src/linker/lnk.h index 4f8f9cbe..d565e023 100644 --- a/src/linker/lnk.h +++ b/src/linker/lnk.h @@ -77,8 +77,23 @@ typedef struct LNK_Inputer #define LNK_SECTION_FLAG_LIVE (1 << 0) #define LNK_SECTION_FLAG_DEBUG (1 << 1) +typedef U8 LNK_LibMemberFlags; +enum +{ + LNK_LibMemberFlag_LinkedRegular = (1 << 0), + LNK_LibMemberFlag_LinkedImp = (1 << 1), + LNK_LibMemberFlag_WasGenQueued = (1 << 2), +}; + +typedef struct LNK_LibMemberInfo +{ + LNK_Symbol *link; + LNK_LibMemberFlags flags; +} LNK_LibMemberInfo; + typedef struct LNK_Link { + Arena *arena; LNK_ObjList objs; LNK_LibList libs; LNK_ObjNode **last_symbol_input; @@ -86,10 +101,17 @@ typedef struct LNK_Link String8Node **last_cmd_lib; String8Node **last_default_lib; String8Node **last_obj_lib; + HashTable *lib_member_infos_ht; LNK_LibMemberRefList imports; B32 try_to_resolve_entry_point; } LNK_Link; +typedef struct LNK_LinkResult +{ + LNK_ObjList objs; + LNK_LibList libs; +} LNK_LinkResult; + // -- Image Layout ------------------------------------------------------------ #define LNK_REMOVED_SECTION_NUMBER_32 (U32)-3 @@ -186,6 +208,7 @@ typedef struct LNK_SymbolTable *symtab; LNK_Symbol *import_stub; LNK_Lib *lib; + LNK_LibMemberInfo *lib_member_infos; LNK_LibMemberRefList *member_ref_lists; } LNK_SearchLibTask; @@ -342,10 +365,10 @@ internal void lnk_lib_member_ref_list_concat_in_place_array(LNK_L internal int lnk_lib_member_ref_is_before(void *raw_a, void *raw_b); internal LNK_LibMemberRef ** lnk_array_from_lib_member_list(Arena *arena, LNK_LibMemberRefList list); -internal LNK_ObjNode * lnk_load_objs (TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link, U64 *objs_count_out); -internal void lnk_load_libs (TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_Link *link); -internal void lnk_link_inputs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link); -internal LNK_Link * lnk_link_image (TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab); +internal LNK_ObjNode * lnk_load_objs (TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link, U64 *objs_count_out); +internal void lnk_load_libs (TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_Link *link); +internal void lnk_link_inputs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link); +internal LNK_LinkResult lnk_link_image (TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab); // --- Optimizations ----------------------------------------------------------- diff --git a/src/linker/lnk_lib.c b/src/linker/lnk_lib.c index cd00876a..7f546e88 100644 --- a/src/linker/lnk_lib.c +++ b/src/linker/lnk_lib.c @@ -141,8 +141,6 @@ lnk_lib_from_data(Arena *arena, String8 data, String8 path, U64 input_idx, LNK_L lib_out->symbol_count = Min(symbol_count, symbol_names.count); // TODO: warn about mismatched number of symbol names and symbol count in the header lib_out->member_offsets = member_offsets; lib_out->symbol_indices = symbol_indices; - lib_out->member_links = push_array(arena, LNK_Symbol *, member_count); - lib_out->member_flags = push_array(arena, LNK_LibMemberFlags, member_count); lib_out->symbol_names = symbol_names; lib_out->long_names = parse.long_names; lib_out->input_idx = input_idx; @@ -222,43 +220,6 @@ lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, U return result; } -internal B32 -lnk_lib_set_link_symbol(LNK_Lib *lib, U32 member_idx, LNK_Symbol *link_symbol) -{ - B32 was_linked; - if (str8_starts_with(link_symbol->name, str8_lit("__imp_"))) { - U8 member_flags = ins_atomic_u8_or(&lib->member_flags[member_idx], LNK_LibMemberFlag_LinkedImp); - was_linked = !!(~member_flags & LNK_LibMemberFlag_LinkedImp); - } else { - U8 flag = LNK_LibMemberFlag_LinkedRegular; - U8 member_flags = ins_atomic_u8_or(&lib->member_flags[member_idx], LNK_LibMemberFlag_LinkedRegular); - was_linked = !!(~member_flags & LNK_LibMemberFlag_LinkedRegular); - } - - for (LNK_Symbol *leader = link_symbol;;) { - LNK_Symbol *slot = ins_atomic_ptr_eval_assign(&lib->member_links[member_idx], 0); - - // update slot symbol if it is empty or link symbol comes before symbol in the slot - if (slot) { - if (lnk_symbol_is_before(slot, leader)) { - leader = slot; - } - } else { - leader = link_symbol; - } - - // try to insert back updated slot symbol - LNK_Symbol *swap = ins_atomic_ptr_eval_cond_assign(&lib->member_links[member_idx], leader, 0); - - // exit if slot symbol was null - if (swap == 0) { - break; - } - } - - return was_linked; -} - internal force_inline B32 lnk_search_lib(LNK_Lib *lib, String8 symbol_name, U32 *member_idx_out) { diff --git a/src/linker/lnk_lib.h b/src/linker/lnk_lib.h index 4f1fdde0..79b682c7 100644 --- a/src/linker/lnk_lib.h +++ b/src/linker/lnk_lib.h @@ -3,14 +3,6 @@ #pragma once -typedef U8 LNK_LibMemberFlags; -enum -{ - LNK_LibMemberFlag_LinkedRegular = (1 << 0), - LNK_LibMemberFlag_LinkedImp = (1 << 1), - LNK_LibMemberFlag_WasQueued = (1 << 2), -}; - typedef struct LNK_Lib { String8 path; @@ -20,8 +12,6 @@ typedef struct LNK_Lib U32 symbol_count; U32 *member_offsets; U16 *symbol_indices; - LNK_Symbol **member_links; - LNK_LibMemberFlags *member_flags; String8Array symbol_names; String8 long_names; U64 input_idx; @@ -76,7 +66,5 @@ internal LNK_Lib ** lnk_array_from_lib_list(Arena *arena, LNK_LibList list internal void lnk_lib_list_push_node(LNK_LibList *list, LNK_LibNode *node); internal LNK_LibNodeArray lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, U64 inputs_count, struct LNK_Input **inputs); -internal B32 lnk_lib_set_link_symbol(LNK_Lib *lib, U32 member_idx, LNK_Symbol *link_symbol); - internal B32 lnk_search_lib(LNK_Lib *lib, String8 symbol_name, U32 *member_idx_out);