diff --git a/src/linker/linker.natvis b/src/linker/linker.natvis index d1031fa1..8414241e 100644 --- a/src/linker/linker.natvis +++ b/src/linker/linker.natvis @@ -16,6 +16,7 @@ + {{count={count} first={first} last={last} }} @@ -33,6 +34,7 @@ + {{count={count} first={first} }} diff --git a/src/linker/lnk.c b/src/linker/lnk.c index f23c5eac..31dcb462 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -1626,7 +1626,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) if (lnk_get_log_status(LNK_Log_InputLib)) { if (libs.count > 0) { U64 input_size = 0; - for EachIndex(i, libs.count) { input_size += libs.v[i].data.data.size; } + for EachIndex(i, libs.count) { input_size += libs.v[i]->data.data.size; } lnk_log(LNK_Log_InputObj, "[ Lib Input Size %M ]", input_size); } } @@ -1658,6 +1658,8 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) } break; case State_SearchUndefined: { ProfBegin("Search Undefined"); + LNK_InputImportList new_imports = {0}; + LNK_InputObjList new_objs = {0}; for EachIndex(i, ArrayCount(lib_index)) { for (LNK_LibNode *lib_n = lib_index[i].first; lib_n != 0; lib_n = lib_n->next) { for EachIndex(worker_id, symtab->arena->count) { @@ -1669,22 +1671,38 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) if (symbol_interp == COFF_SymbolValueInterp_Undefined) { U32 member_idx; if (lnk_search_lib(&lib_n->data, symbol->name, &member_idx)) { - lnk_queue_lib_member_for_input(scratch.arena, config, symbol, &lib_n->data, member_idx, &input_import_list, &input_obj_list); + lnk_queue_lib_member_for_input(scratch.arena, config, symbol, &lib_n->data, member_idx, &new_imports, &new_objs); } } } } } - if (input_import_list.count || input_obj_list.count) { + if (new_imports.count || new_objs.count) { goto end_search_undefined; } } } end_search_undefined:; + + { + LNK_InputImportNode **import_nodes = lnk_input_import_arr_from_list(scratch.arena, new_imports); + radsort(import_nodes, new_imports.count, lnk_input_import_is_before); + new_imports = lnk_list_from_input_import_arr(import_nodes, new_imports.count); + + LNK_InputObj **obj_nodes = lnk_array_from_input_obj_list(scratch.arena, new_objs); + radsort(obj_nodes, new_objs.count, lnk_input_obj_compar_is_before); + new_objs = lnk_list_from_input_obj_arr(obj_nodes, new_objs.count); + + lnk_input_import_list_concat_in_place(&input_import_list, &new_imports); + lnk_input_obj_list_concat_in_place(&input_obj_list, &new_objs); + } + ProfEnd(); } break; case State_SearchWeak: { ProfBegin("Search Weak"); + LNK_InputImportList new_imports = {0}; + LNK_InputObjList new_objs = {0}; for EachIndex(i, ArrayCount(lib_index)) { for (LNK_LibNode *lib_n = lib_index[i].first; lib_n != 0; lib_n = lib_n->next) { for EachIndex(worker_id, symtab->arena->count) { @@ -1698,23 +1716,39 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) if (weak_ext->characteristics == COFF_WeakExt_SearchLibrary) { U32 member_idx; if (lnk_search_lib(&lib_n->data, symbol->name, &member_idx)) { - lnk_queue_lib_member_for_input(scratch.arena, config, symbol, &lib_n->data, member_idx, &input_import_list, &input_obj_list); + lnk_queue_lib_member_for_input(scratch.arena, config, symbol, &lib_n->data, member_idx, &new_imports, &new_objs); } } } } } } - if (input_import_list.count || input_obj_list.count) { + if (new_imports.count || new_objs.count) { goto end_search_weak; } } } end_search_weak:; + + { + LNK_InputImportNode **import_nodes = lnk_input_import_arr_from_list(scratch.arena, new_imports); + radsort(import_nodes, new_imports.count, lnk_input_import_is_before); + new_imports = lnk_list_from_input_import_arr(import_nodes, new_imports.count); + + LNK_InputObj **obj_nodes = lnk_array_from_input_obj_list(scratch.arena, new_objs); + radsort(obj_nodes, new_objs.count, lnk_input_obj_compar_is_before); + new_objs = lnk_list_from_input_obj_arr(obj_nodes, new_objs.count); + + lnk_input_import_list_concat_in_place(&input_import_list, &new_imports); + lnk_input_obj_list_concat_in_place(&input_obj_list, &new_objs); + } + ProfEnd(); } break; case State_SearchWeakAntiDep: { ProfBegin("Search Weak AntiDep"); + LNK_InputImportList new_imports = {0}; + LNK_InputObjList new_objs = {0}; for EachIndex(i, ArrayCount(lib_index)) { for (LNK_LibNode *lib_n = lib_index[i].first; lib_n != 0; lib_n = lib_n->next) { for EachIndex(worker_id, symtab->arena->count) { @@ -1732,7 +1766,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) if (dep_interp == COFF_SymbolValueInterp_Weak) { U32 member_idx; if (lnk_search_lib(&lib_n->data, symbol_parsed.name, &member_idx)) { - lnk_queue_lib_member_for_input(scratch.arena, config, symbol, &lib_n->data, member_idx, &input_import_list, &input_obj_list); + lnk_queue_lib_member_for_input(scratch.arena, config, symbol, &lib_n->data, member_idx, &new_imports, &new_objs); } } } @@ -1740,13 +1774,26 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) } } } - - if (input_import_list.count || input_obj_list.count) { + if (new_imports.count || new_objs.count) { goto end_search_antidep; } } } end_search_antidep:; + + { + LNK_InputImportNode **import_nodes = lnk_input_import_arr_from_list(scratch.arena, new_imports); + radsort(import_nodes, new_imports.count, lnk_input_import_is_before); + new_imports = lnk_list_from_input_import_arr(import_nodes, new_imports.count); + + LNK_InputObj **obj_nodes = lnk_array_from_input_obj_list(scratch.arena, new_objs); + radsort(obj_nodes, new_objs.count, lnk_input_obj_compar_is_before); + new_objs = lnk_list_from_input_obj_arr(obj_nodes, new_objs.count); + + lnk_input_import_list_concat_in_place(&input_import_list, &new_imports); + lnk_input_obj_list_concat_in_place(&input_obj_list, &new_objs); + } + ProfEnd(); } break; case State_SearchEntryPoint: { diff --git a/src/linker/lnk_input.c b/src/linker/lnk_input.c index a7f543bf..16f8deef 100644 --- a/src/linker/lnk_input.c +++ b/src/linker/lnk_input.c @@ -84,20 +84,15 @@ lnk_path_array_from_input_obj_array(Arena *arena, LNK_InputObj **arr, U64 count) internal int lnk_input_obj_compar(const void *raw_a, const void *raw_b) { - const LNK_InputObj **a = (const LNK_InputObj **) raw_a; - const LNK_InputObj **b = (const LNK_InputObj **) raw_b; - int cmp = str8_compar_case_sensitive(&(*a)->path, &(*b)->path); - return cmp; + LNK_InputObj * const *a = raw_a, * const *b = raw_b; + return u64_compar(&(*a)->input_idx, &(*b)->input_idx); } internal int lnk_input_obj_compar_is_before(void *raw_a, void *raw_b) { - LNK_InputObj **a = raw_a; - LNK_InputObj **b = raw_b; - int cmp = str8_compar_case_sensitive(&(*a)->path, &(*b)->path); - int is_before = cmp < 0; - return is_before; + LNK_InputObj **a = raw_a, **b = raw_b; + return lnk_input_obj_compar(a, b) < 0; } internal LNK_InputObjList diff --git a/src/linker/lnk_input.h b/src/linker/lnk_input.h index 63eb305f..a8869d37 100644 --- a/src/linker/lnk_input.h +++ b/src/linker/lnk_input.h @@ -36,15 +36,15 @@ typedef struct LNK_InputImportList typedef struct LNK_InputObj { - struct LNK_InputObj *next; + String8 path; + String8 dedup_id; + String8 data; B8 is_thin; B8 exclude_from_debug_info; B8 has_disk_read_failed; - String8 dedup_id; - String8 path; - String8 data; struct LNK_Lib *lib; U64 input_idx; + struct LNK_InputObj *next; } LNK_InputObj; typedef struct LNK_InputObjList diff --git a/src/linker/lnk_lib.c b/src/linker/lnk_lib.c index c8270380..d620f2cb 100644 --- a/src/linker/lnk_lib.c +++ b/src/linker/lnk_lib.c @@ -1,56 +1,17 @@ // Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -internal LNK_LibNode * -lnk_lib_list_pop_node_atomic(LNK_LibList *list) +internal int +lnk_lib_node_is_before(void *a, void *b) { - for (;;) { - LNK_LibNode *expected = list->first; - LNK_LibNode *current = ins_atomic_ptr_eval_cond_assign(&list->first, expected->next, expected); - if (expected == current) { - ins_atomic_u64_dec_eval(&list->count); - return expected; - } - } + return ((LNK_LibNode*)a)->data.input_idx < ((LNK_LibNode*)b)->data.input_idx; } -internal void -lnk_lib_list_push_node_atomic(LNK_LibList *list, LNK_LibNode *node) +internal int +lnk_lib_node_ptr_is_before(void *raw_a, void *raw_b) { - for (;;) { - LNK_LibNode *expected = list->first; - LNK_LibNode *current = ins_atomic_ptr_eval_cond_assign(&list->first, node, expected); - if (current == expected) { - node->next = expected; - ins_atomic_u64_inc_eval(&list->count); - return; - } - } -} - -internal void -lnk_lib_list_push_node(LNK_LibList *list, LNK_LibNode *node) -{ - SLLStackPush(list->first, node); - list->count += 1; -} - -internal LNK_LibList -lnk_lib_list_reserve(Arena *arena, U64 count) -{ - LNK_LibList result = {0}; - LNK_LibNode *nodes = push_array(arena, LNK_LibNode, count); - for (U64 i = 0; i < count; i += 1) { lnk_lib_list_push_node(&result, &nodes[i]); } - return result; -} - -internal LNK_LibNodeArray -lnk_array_from_lib_list(Arena *arena, LNK_LibList list) -{ - LNK_LibNodeArray result = {0}; - result.v = push_array(arena, LNK_LibNode, list.count); - for (LNK_LibNode *n = list.first; n != 0; n = n->next) { result.v[result.count++] = *n; } - return result; + LNK_LibNode **a = raw_a, **b = raw_b; + return lnk_lib_node_is_before(*a, *b); } internal B32 @@ -169,21 +130,25 @@ THREAD_POOL_TASK_FUNC(lnk_lib_initer) { LNK_LibIniter *task = raw_task; - LNK_LibNode *lib_node = lnk_lib_list_pop_node_atomic(&task->free_libs); + U64 lib_node_idx = ins_atomic_u64_inc_eval(&task->next_free_lib_idx)-1; + LNK_LibNode *lib_node = &task->free_libs[lib_node_idx]; lib_node->data.input_idx = task_id; B32 is_valid_lib = lnk_lib_from_data(arena, task->data_arr[task_id], task->path_arr[task_id], &lib_node->data); if (is_valid_lib) { - lnk_lib_list_push_node_atomic(&task->valid_libs, lib_node); + U64 valid_lib_idx = ins_atomic_u64_inc_eval(&task->valid_libs_count)-1; + task->valid_libs[valid_lib_idx] = lib_node; } else { - lnk_lib_list_push_node_atomic(&task->invalid_libs, lib_node); + U64 invalid_lib_idx = ins_atomic_u64_inc_eval(&task->invalid_libs_count); + task->invalid_libs[invalid_lib_idx] = lib_node; } } -internal int -lnk_lib_node_is_before(void *a, void *b) +internal void +lnk_lib_list_push_node(LNK_LibList *list, LNK_LibNode *node) { - return ((LNK_LibNode*)a)->data.input_idx < ((LNK_LibNode*)b)->data.input_idx; + SLLQueuePush(list->first, list->last, node); + list->count += 1; } internal LNK_LibNodeArray @@ -196,27 +161,28 @@ lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, S // parse libs in parallel LNK_LibIniter task = {0}; - task.free_libs = lnk_lib_list_reserve(scratch.arena, lib_count); + task.free_libs = push_array(arena->v[0], LNK_LibNode, lib_count); + task.valid_libs = push_array(scratch.arena, LNK_LibNode *, lib_count); + task.invalid_libs = push_array(scratch.arena, LNK_LibNode *, lib_count); task.data_arr = data_arr.v; task.path_arr = path_arr.v; tp_for_parallel(tp, arena, lib_count, lnk_lib_initer, &task); // report invalid libs - LNK_LibNodeArray invalid_libs = lnk_array_from_lib_list(scratch.arena, task.invalid_libs); - radsort(invalid_libs.v, invalid_libs.count, lnk_lib_node_is_before); - for (U64 i = 0; i < task.invalid_libs.count; i += 1) { - U64 input_idx = invalid_libs.v[i].data.input_idx; + radsort(task.invalid_libs, task.invalid_libs_count, lnk_lib_node_ptr_is_before); + for EachIndex(i, task.invalid_libs_count) { + U64 input_idx = task.invalid_libs[i]->data.input_idx; lnk_error(LNK_Error_InvalidLib, "%S: failed to parse library", path_arr.v[input_idx]); } // push parsed libs - LNK_LibNodeArray result = lnk_array_from_lib_list(arena->v[0], task.valid_libs); - radsort(result.v, result.count, lnk_lib_node_is_before); - for (U64 i = result.count; i > 0; i -= 1) { - result.v[i-1].data.input_idx = list->count; - lnk_lib_list_push_node(list, &result.v[i-1]); + radsort(task.valid_libs, task.valid_libs_count, lnk_lib_node_ptr_is_before); + for EachIndex(i, task.valid_libs_count) { + lnk_lib_list_push_node(list, task.valid_libs[i]); } + LNK_LibNodeArray result = { .count = task.valid_libs_count, task.valid_libs }; + scratch_end(scratch); return result; } diff --git a/src/linker/lnk_lib.h b/src/linker/lnk_lib.h index 04c3ef54..b585d302 100644 --- a/src/linker/lnk_lib.h +++ b/src/linker/lnk_lib.h @@ -16,7 +16,7 @@ typedef struct LNK_Lib String8 long_names; U64 input_idx; } LNK_Lib; - + typedef struct LNK_LibNode { LNK_Lib data; @@ -25,31 +25,39 @@ typedef struct LNK_LibNode typedef struct LNK_LibNodeArray { - U64 count; - LNK_LibNode *v; + U64 count; + LNK_LibNode **v; } LNK_LibNodeArray; typedef struct LNK_LibList { - U64 count; - struct LNK_LibNode *first; + U64 count; + LNK_LibNode *first; + LNK_LibNode *last; } LNK_LibList; + +// --- Workers Contexts -------------------------------------------------------- typedef struct { - String8 *data_arr; - String8 *path_arr; - LNK_LibList free_libs; - LNK_LibList valid_libs; - LNK_LibList invalid_libs; + String8 *data_arr; + String8 *path_arr; + U64 next_free_lib_idx; + U64 valid_libs_count; + U64 invalid_libs_count; + LNK_LibNode *free_libs; + LNK_LibNode **valid_libs; + LNK_LibNode **invalid_libs; } LNK_LibIniter; -internal LNK_LibNode * lnk_lib_list_pop_node_atomic(LNK_LibList *list); -internal void lnk_lib_list_push_node_atomic(LNK_LibList *list, LNK_LibNode *node); -internal void lnk_lib_list_push_node(LNK_LibList *list, LNK_LibNode *node); -internal LNK_LibList lnk_lib_list_reserve(Arena *arena, U64 count); -internal LNK_LibNodeArray lnk_array_from_lib_list(Arena *arena, LNK_LibList list); +// ----------------------------------------------------------------------------- + +internal int lnk_lib_node_is_before(void *a, void *b); +internal int lnk_lib_node_ptr_is_before(void *raw_a, void *raw_b); internal B32 lnk_lib_from_data(Arena *arena, String8 data, String8 path, LNK_Lib *lib_out); +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, String8Array data_arr, String8Array path_arr); +internal B32 lnk_search_lib(LNK_Lib *lib, String8 symbol_name, U32 *member_idx_out); + diff --git a/src/linker/scripts/obj_paths_from_pdb.py b/src/linker/scripts/obj_paths_from_pdb.py index a4d3de02..2bd11b50 100644 --- a/src/linker/scripts/obj_paths_from_pdb.py +++ b/src/linker/scripts/obj_paths_from_pdb.py @@ -8,8 +8,8 @@ def get_sorted_objs(pdb_path): filtered_lines = [line for line in lines if line.lstrip().startswith("Mod ")] # sort by the obj_path portion (line format: "Mod ") def extract_path(line): return line.split(maxsplit=2)[2].lower() - sorted_lines = sorted(filtered_lines, key=extract_path) - return sorted_lines + #sorted_lines = sorted(filtered_lines, key=extract_path) + return filtered_lines if __name__ == "__main__": sorted_objs = get_sorted_objs(sys.argv[1])