From c0418b736d806984c63d75581746499952bb4463 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Tue, 17 Mar 2026 12:48:47 -0700 Subject: [PATCH] WIP: internal and external type servers are functioning except for the PCH indirection --- src/linker/codeview_ext/codeview.c | 122 +- src/linker/codeview_ext/codeview.h | 41 +- src/linker/lnk.c | 18 +- src/linker/lnk_debug_info.c | 2559 ++++++++++------------------ src/linker/lnk_debug_info.h | 290 ++-- src/linker/pdb_ext/pdb_builder.c | 67 +- src/msf/msf_parse.c | 74 +- src/msf/msf_parse.h | 2 + src/torture/torture_radlink.c | 88 +- 9 files changed, 1301 insertions(+), 1960 deletions(-) diff --git a/src/linker/codeview_ext/codeview.c b/src/linker/codeview_ext/codeview.c index 7f61df28..a41b8fc8 100644 --- a/src/linker/codeview_ext/codeview.c +++ b/src/linker/codeview_ext/codeview.c @@ -396,6 +396,12 @@ cv_make_proc_refs(Arena *arena, CV_ModIndex imod, CV_SymbolList symbol_list) return proc_ref_list; } +internal B32 +cv_is_obj_info(CV_Symbol symbol) +{ + return symbol.kind == CV_SymKind_OBJNAME; +} + internal CV_ObjInfo cv_obj_info_from_symbol(CV_Symbol symbol) { @@ -406,12 +412,7 @@ cv_obj_info_from_symbol(CV_Symbol symbol) result.sig = obj_name->sig; str8_deserial_read_cstr(symbol.data, sizeof(CV_SymObjName), &result.name); } break; - case CV_SymKind_OBJNAME_ST: { - NotImplemented; - } break; - default: { - InvalidPath; - } break; + default: { InvalidPath; } break; } return result; } @@ -1092,76 +1093,123 @@ cv_dedup_symbol_ptr_array(TP_Context *tp, CV_SymbolPtrArray *symbols) internal CV_DebugT cv_debug_t_from_data(Arena *arena, String8 data, U64 align) { - ProfBegin("Upfront parse"); - U64 count = 0; - for (U64 cursor = 0; cursor < data.size; count += 1) { - CV_Leaf leaf; - cursor += cv_read_leaf(data, cursor, align, &leaf); + CV_DebugT debug_t = { .data = data }; + + ProfBegin("Upfront parse for counts"); + for (U64 cursor = 0, prev_cursor = 0, ti = CV_MinComplexTypeIndex; cursor < data.size; ti += 1) { + CV_Leaf leaf = {0}; + TryRead(cv_read_leaf(data, cursor, align, &leaf), cursor, count_stop); + debug_t.source_counts[cv_type_index_source_from_leaf_kind(leaf.kind)] += 1; } +count_stop: ProfEnd(); - U32 *offsets = push_array_no_zero(arena, U32, count); + for EachElement(i, debug_t.source_counts) { debug_t.count += debug_t.source_counts[i]; } + + ProfBegin("store leaf offsets"); + debug_t.offsets = push_array_no_zero(arena, U32, debug_t.count); for (U64 cursor = 0, idx = 0; cursor < data.size;) { - offsets[idx++] = cursor; - + debug_t.offsets[idx++] = cursor; CV_Leaf leaf; - cursor += cv_read_leaf(data, cursor, align, &leaf); + TryRead(cv_read_leaf(data, cursor, align, &leaf), cursor, store_stop); } +store_stop: - return (CV_DebugT){ .count = count, .data = data, .offsets = offsets }; + for EachElement(i, debug_t.ti_ranges) { debug_t.ti_ranges[i] = r1u64(CV_MinComplexTypeIndex, CV_MinComplexTypeIndex + debug_t.count); } + + ProfEnd(); + return debug_t; } internal CV_Leaf -cv_debug_t_get_leaf(CV_DebugT debug_t, U64 leaf_idx) +cv_debug_t_get_leaf(CV_DebugT *debug_t, U64 leaf_idx) { CV_Leaf leaf = {0}; - if (debug_t.count > 0) { - Assert(leaf_idx < debug_t.count); - cv_read_leaf(debug_t.data, debug_t.offsets[leaf_idx], 1, &leaf); + if (debug_t->count > 0) { + Assert(leaf_idx < debug_t->count); + cv_read_leaf(debug_t->data, debug_t->offsets[leaf_idx], 1, &leaf); Assert(cv_header_struct_size_from_leaf_kind(leaf.kind) <= leaf.data.size); } return leaf; } -internal String8 -cv_debug_t_get_raw_leaf(CV_DebugT debug_t, U64 leaf_idx) +internal U64 +cv_leaf_idx_from_ti(CV_DebugT *debug_t, CV_TypeIndexSource source, CV_TypeIndex ti) { - Assert(leaf_idx < debug_t.count); - U8 *leaf_ptr = debug_t.data.str + debug_t.offsets[leaf_idx]; + Assert(contains_1u64(debug_t->ti_ranges[source], ti)); // validate TI + Assert(!contains_1u64(debug_t->pch_ti_range[source], ti)); // no PCH indirection + U64 leaf_idx = ti; + Assert(leaf_idx >= debug_t->ti_ranges[source].min); + leaf_idx -= debug_t->ti_ranges[source].min; // strip type index range + Assert(leaf_idx >= dim_1u64(debug_t->pch_ti_range[source])); + leaf_idx -= dim_1u64(debug_t->pch_ti_range[source]); // strip PCH indirection + leaf_idx += debug_t->source_offsets[source]; + Assert(leaf_idx < debug_t->count); + return leaf_idx; +} + +internal CV_TypeIndex +cv_ti_from_leaf_idx(CV_DebugT *debug_t, CV_TypeIndexSource source, U64 leaf_idx) +{ + U64 pch_count = dim_1u64(debug_t->pch_ti_range[source]); + CV_TypeIndex ti = debug_t->ti_ranges[source].min + pch_count + leaf_idx; + return ti; +} + +internal CV_Leaf +cv_leaf_from_ti(CV_DebugT *debug_t, CV_TypeIndexSource source, CV_TypeIndex ti) +{ + U64 leaf_idx = cv_leaf_idx_from_ti(debug_t, source, ti); + return cv_debug_t_get_leaf(debug_t, leaf_idx); +} + +internal String8 +cv_debug_t_get_raw_leaf(CV_DebugT *debug_t, U64 leaf_idx) +{ + Assert(leaf_idx < debug_t->count); + U8 *leaf_ptr = debug_t->data.str + debug_t->offsets[leaf_idx]; CV_LeafSize leaf_size = memory_read16(leaf_ptr); return str8(leaf_ptr, leaf_size + sizeof(leaf_size)); } internal CV_LeafHeader * -cv_debug_t_get_leaf_header(CV_DebugT debug_t, U64 leaf_idx) +cv_debug_t_get_leaf_header(CV_DebugT *debug_t, U64 leaf_idx) { CV_LeafHeader *header = 0; - if (leaf_idx < debug_t.count) { - header = (CV_LeafHeader *)(debug_t.data.str + debug_t.offsets[leaf_idx]); + if (leaf_idx < debug_t->count) { + header = (CV_LeafHeader *)(debug_t->data.str + debug_t->offsets[leaf_idx]); } return header; } +internal CV_TypeIndex +cv_debug_t_get_type_index(CV_DebugT *debug_t, CV_TypeIndexSource ti_source, U64 leaf_idx) +{ + CV_TypeIndex ti = debug_t->ti_ranges[ti_source].min + leaf_idx; + Assert(contains_1u64(debug_t->ti_ranges[ti_source], ti)); + return ti; +} + +internal U64 +cv_debug_t_get_leaf_index(CV_DebugT *debug_t, CV_TypeIndexSource ti_source, CV_TypeIndex ti) +{ + Assert(contains_1u64(debug_t->ti_ranges[ti_source], ti)); + U64 leaf_idx = ti - debug_t->ti_ranges[ti_source].min; + return leaf_idx; +} + internal B32 -cv_debug_t_is_pch(CV_DebugT debug_t) +cv_debug_t_is_pch(CV_DebugT *debug_t) { return cv_is_leaf_pch(cv_debug_t_get_leaf(debug_t, 0).kind); } internal B32 -cv_debug_t_is_type_server(CV_DebugT debug_t) +cv_debug_t_is_type_server_ref(CV_DebugT *debug_t) { return cv_is_leaf_type_server(cv_debug_t_get_leaf(debug_t, 0).kind); } -internal U64 -cv_debug_t_array_count_leaves(U64 count, CV_DebugT *debug_t) -{ - U64 total = 0; - for EachIndex(i, count) { total += debug_t[i].count; } - return total; -} - // $$Symbols internal void diff --git a/src/linker/codeview_ext/codeview.h b/src/linker/codeview_ext/codeview.h index 916609e7..73306b4b 100644 --- a/src/linker/codeview_ext/codeview.h +++ b/src/linker/codeview_ext/codeview.h @@ -253,11 +253,27 @@ typedef struct CV_DebugS String8List data_list[CV_C13SubSectionIdxKind_COUNT]; } CV_DebugS; +typedef struct CV_DebugH +{ + U64 count; + U64 *v; +} CV_DebugH; + typedef struct CV_DebugT { - U64 count; - String8 data; - U32 *offsets; + String8 data; + U64 count; + U32 *offsets; + + // type server + U64 source_counts [CV_TypeIndexSource_COUNT]; + U64 source_offsets[CV_TypeIndexSource_COUNT]; + U32 ti_base [CV_TypeIndexSource_COUNT]; + Rng1U64 ti_ranges [CV_TypeIndexSource_COUNT]; + + // PCH + Rng1U64 pch_ti_range[CV_TypeIndexSource_COUNT]; + U32 pch_obj_idx; } CV_DebugT; //////////////////////////////// @@ -393,6 +409,7 @@ internal CV_SymbolList cv_make_proc_refs(Arena *arena, CV_ModIndex imod, CV_Symb internal U64 cv_read_symbol(String8 raw_data, U64 off, U64 align, CV_Symbol *symbol_out); internal CV_Symbol cv_symbol_from_string(String8 raw_data); +internal B32 cv_is_obj_info(CV_Symbol symbol); internal CV_ObjInfo cv_obj_info_from_symbol(CV_Symbol symbol); //////////////////////////////// @@ -416,13 +433,17 @@ internal String8 cv_file_chksms_from_debug_s(CV_DebugS debug_s); //////////////////////////////// //~ .debug$T helpers -internal CV_DebugT cv_debug_t_from_data(Arena *arena, String8 data, U64 align); -internal CV_Leaf cv_debug_t_get_leaf(CV_DebugT debug_t, U64 leaf_idx); -internal String8 cv_debug_t_get_raw_leaf(CV_DebugT debug_t, U64 leaf_idx); -internal CV_LeafHeader * cv_debug_t_get_leaf_header(CV_DebugT debug_t, U64 leaf_idx); -internal B32 cv_debug_t_is_pch(CV_DebugT debug_t); -internal B32 cv_debug_t_is_type_server(CV_DebugT debug_t); -internal U64 cv_debug_t_array_count_leaves(U64 count, CV_DebugT *arr); +internal CV_DebugT cv_debug_t_from_data (Arena *arena, String8 data, U64 align); +internal U64 cv_leaf_idx_from_ti (CV_DebugT *debug_t, CV_TypeIndexSource source, CV_TypeIndex ti); +internal CV_TypeIndex cv_ti_from_leaf_idx (CV_DebugT *debug_t, CV_TypeIndexSource source, U64 leaf_idx); +internal CV_Leaf cv_debug_t_get_leaf (CV_DebugT *debug_t, U64 leaf_idx); +internal CV_Leaf cv_debug_t_get_leaf_from_ti (CV_DebugT *debug_t, CV_TypeIndexSource source, CV_TypeIndex ti); +internal String8 cv_debug_t_get_raw_leaf (CV_DebugT *debug_t, U64 leaf_idx); +internal CV_LeafHeader * cv_debug_t_get_leaf_header (CV_DebugT *debug_t, U64 leaf_idx); +internal CV_TypeIndex cv_debug_t_get_type_index (CV_DebugT *debug_t, CV_TypeIndexSource ti_source, U64 leaf_idx); +internal U64 cv_debug_t_get_leaf_index (CV_DebugT *debug_t, CV_TypeIndexSource ti_source, CV_TypeIndex ti); +internal B32 cv_debug_t_is_pch (CV_DebugT *debug_t); +internal B32 cv_debug_t_is_type_server_ref(CV_DebugT *debug_t); //////////////////////////////// //~ Sub Section helpers diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 2732944c..122db98a 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -5161,14 +5161,13 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) rdi_arch_from_coff_machine(config->machine), config->image_name, image_ctx.image_data, - input.count, - input.obj_arr, + debug_info_objs_count, + debug_info_objs, input.debug_s_arr, - input.total_symbol_input_count, + input.symbol_input_count, input.symbol_inputs, input.parsed_symbols, - merged_types.count, - merged_types.v); + merged_types); lnk_write_data_list_to_file_path(config->rad_debug_name, config->temp_rad_debug_name, rdi_data); @@ -5198,14 +5197,13 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) image_ctx.image_data, config, symtab, - input.count, - input.obj_arr, + debug_info_objs_count, + debug_info_objs, input.debug_s_arr, - input.total_symbol_input_count, + input.symbol_input_count, input.symbol_inputs, input.parsed_symbols, - merged_types.count, - merged_types.v); + merged_types); lnk_write_data_list_to_file_path(config->pdb_name, config->temp_pdb_name, pdb_data); lnk_timer_end(LNK_Timer_Pdb); diff --git a/src/linker/lnk_debug_info.c b/src/linker/lnk_debug_info.c index 434b28cb..70e272e5 100644 --- a/src/linker/lnk_debug_info.c +++ b/src/linker/lnk_debug_info.c @@ -60,7 +60,7 @@ lnk_parse_debug_s_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_O } internal -THREAD_POOL_TASK_FUNC(lnk_check_debug_t_sig_and_get_data_task) +THREAD_POOL_TASK_FUNC(lnk_strip_debug_t_sig_task) { U64 obj_idx = task_id; LNK_CheckDebugTSigTaskData *task = raw_task; @@ -68,39 +68,22 @@ THREAD_POOL_TASK_FUNC(lnk_check_debug_t_sig_and_get_data_task) String8Array data_arr = task->data_arr_arr[obj_idx]; LNK_Obj *obj = task->obj_arr[obj_idx]; - for (String8 *data_ptr = &data_arr.v[0], *data_opl = data_arr.v + data_arr.count; - data_ptr < data_opl; - ++data_ptr) { - if (data_ptr->size == 0) { - continue; - } + for EachIndex(i, data_arr.count) { + String8 *d = data_arr.v + i; + if (d->size == 0) { continue; } - if (data_ptr->size < sizeof(CV_Signature)) { - // TODO: print section index + if (d->size < sizeof(CV_Signature)) { lnk_error_obj(LNK_Error_IllData, obj, ".debug$T must have at least 4 bytes for CodeView signature"); } - CV_Signature sig = cv_signature_from_debug_s(*data_ptr); + CV_Signature sig = cv_signature_from_debug_s(*d); switch (sig) { default: { lnk_error_obj(LNK_Warning_IllData, obj, "unknown CodeView type signature in section (TODO: print section index)"); - *data_ptr = str8(0,0); - } break; - case CV_Signature_C6: { - lnk_not_implemented("TODO: C6 types"); - *data_ptr = str8(0,0); - } break; - case CV_Signature_C7: { - lnk_not_implemented("TODO: C7 types"); - *data_ptr = str8(0,0); - } break; - case CV_Signature_C11: { - lnk_not_implemented("TODO: C11 types"); - *data_ptr = str8(0,0); + *d = str8(0,0); } break; case CV_Signature_C13: { - data_ptr->str += sizeof(CV_Signature); - data_ptr->size -= sizeof(CV_Signature); + *d = str8_skip(*d, sizeof(CV_Signature)); } break; } } @@ -110,13 +93,11 @@ internal THREAD_POOL_TASK_FUNC(lnk_parse_debug_t_task) { ProfBeginFunction(); - U64 obj_idx = task_id; - LNK_ParseDebugTTaskData *task = raw_task; - String8Array data_arr = task->data_arr_arr[obj_idx]; - if (data_arr.count > 0) { - task->debug_t_arr[obj_idx] = cv_debug_t_from_data(arena, data_arr.v[0], CV_LeafAlign); + LNK_ParseDebugTTaskData *task = raw_task; + if (task->data_arr_arr[task_id].count > 0) { + task->debug_t_arr[task_id] = cv_debug_t_from_data(arena, task->data_arr_arr[task_id].v[0], CV_LeafAlign); } else { - MemoryZeroStruct(&task->debug_t_arr[obj_idx]); + MemoryZeroStruct(&task->debug_t_arr[task_id]); } ProfEnd(); } @@ -133,7 +114,7 @@ lnk_parse_debug_t_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_O LNK_CheckDebugTSigTaskData check_sig; check_sig.obj_arr = obj_arr; check_sig.data_arr_arr = data_arr_arr; - tp_for_parallel(tp, 0, obj_count, lnk_check_debug_t_sig_and_get_data_task, &check_sig); + tp_for_parallel(tp, 0, obj_count, lnk_strip_debug_t_sig_task, &check_sig); // parse debug types LNK_ParseDebugTTaskData parse; @@ -149,240 +130,119 @@ internal THREAD_POOL_TASK_FUNC(lnk_parse_cv_symbols_task) { LNK_ParseCVSymbolsTaskData *task = raw_task; - LNK_CodeViewSymbolsInput *input = &task->inputs[task_id]; + LNK_SymbolInput *input = &task->inputs[task_id]; cv_parse_symbol_sub_section(arena, input->symbol_list, 0, input->raw_symbols, CV_SymbolAlign); } -internal LNK_PchInfo * -lnk_setup_pch(Arena *arena, U64 obj_count, LNK_Obj **obj_arr, CV_DebugT *debug_t_arr, CV_DebugT *debug_p_arr, CV_SymbolListArray *parsed_symbols, String8List alt_pch_dirs) +internal +THREAD_POOL_TASK_FUNC(lnk_read_type_servers_task) { + ProfBeginFunction(); Temp scratch = scratch_begin(&arena, 1); + LNK_CodeViewInput *task = raw_task; - String8 work_dir = os_get_current_path(scratch.arena); + B32 discard_debug_info = 1; + U64 ts_idx = task_id; + LNK_TypeServer *ts = &task->ts_arr.v[ts_idx]; - HashTable *debug_p_ht = hash_table_init(scratch.arena, obj_count); - CV_LeafHeader **endprecomp_arr = push_array(scratch.arena, CV_LeafHeader *, obj_count); + // read PDB from disk + String8 msf_data = lnk_read_data_from_file_path(scratch.arena, task->io_flags, ts->ts_path); - for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) { - CV_DebugT *debug_p = &debug_p_arr[obj_idx]; - CV_DebugT *debug_t = &debug_t_arr[obj_idx]; + // check magic + if (!msf_check_magic_70(msf_data) && msf_check_magic_20(msf_data)) { goto exit; } - if (debug_t->count && debug_p->count) { - lnk_error_obj(LNK_Warning_MultipleDebugTAndDebugP, - obj_arr[obj_idx], - "multiple sections with debug types detected, obj must have either .debug$T or .debug$P (using .debug$T for type server)"); - continue; - } + // read the stream table + MSF_RawStreamTable *st = msf_raw_stream_table_from_data(scratch.arena, msf_data); + if (st == 0) { goto exit; } - if (debug_p->count) { - String8 obj_path = obj_arr[obj_idx]->path; - obj_path = path_absolute_dst_from_relative_dst_src(scratch.arena, obj_path, work_dir); - if (hash_table_search_path(debug_p_ht, obj_path)) { - lnk_error_obj(LNK_Warning_DuplicateObjPath, obj_arr[obj_idx], "duplicate obj path %S", obj_path); - } else { - hash_table_push_path_u64(scratch.arena, debug_p_ht, obj_path, obj_idx); - } + // PDB must have these streams + if (PDB_FixedStream_Tpi >= st->stream_count || PDB_FixedStream_Ipi >= st->stream_count || PDB_FixedStream_Info >= st->stream_count) { goto exit; } + + // read info stream + String8 info_data = msf_data_from_stream_number(scratch.arena, msf_data, st, PDB_FixedStream_Info); + PDB_InfoParse info_parse = {0}; + pdb_info_parse_from_data(info_data, &info_parse); + + // match GUID from obj against one in the type server + if (!MemoryMatchStruct(&info_parse.guid, &ts->ts_info.sig)) { + lnk_error(LNK_Warning_MismatchedTypeServerSignature, + "%S: signature mismatch in type server read from disk, expected %S, got %S", + ts->ts_info.name, + string_from_guid(scratch.arena, ts->ts_info.sig), + string_from_guid(scratch.arena, info_parse.guid)); + goto exit; + } + + MSF_StreamNumber type_streams[CV_TypeIndexSource_COUNT] = {}; + type_streams[CV_TypeIndexSource_TPI] = PDB_FixedStream_Tpi; + type_streams[CV_TypeIndexSource_IPI] = PDB_FixedStream_Ipi; + + Rng1U64 ti_ranges [CV_TypeIndexSource_COUNT] = {0}; + Rng1U64 leaf_ranges[CV_TypeIndexSource_COUNT] = {0}; + for EachIndex(ti_source, CV_TypeIndexSource_COUNT) { + MSF_StreamNumber sn = type_streams[ti_source]; + if (sn == 0) { continue; } + if (!pdb_extract_type_server_info(msf_data, st, sn, &ti_ranges[ti_source], &leaf_ranges[ti_source])) { goto exit; } + } + + // alloc buffer where TPI and IPI are adjecent + U64 buffer_size = 0; + for EachIndex(ti_source, CV_TypeIndexSource_COUNT) { buffer_size += dim_1u64(leaf_ranges[ti_source]); } + buffer_size = AlignPow2(buffer_size, 4) + ARENA_HEADER_SIZE; + U8 *buffer = push_array(arena, U8, buffer_size); + Arena *fixed = arena_alloc( .reserve_size = buffer_size, .commit_size = buffer_size, .optional_backing_buffer = buffer ); + + // read both streams into a contiguous buffer + String8 source_data_arr[CV_TypeIndexSource_COUNT] = {0}; + for EachIndex(ti_source, CV_TypeIndexSource_COUNT) { + MSF_StreamNumber sn = type_streams[ti_source]; + if (sn == 0) { continue; } + source_data_arr[ti_source] = msf_data_from_stream_number_ex(fixed, msf_data, st, sn, leaf_ranges[ti_source], PDB_LEAF_ALIGN); + Assert(source_data_arr[ti_source].size == dim_1u64(leaf_ranges[ti_source])); + } + // assert streams are adjecent in the buffer + for (U64 i = 1; i+1 < CV_TypeIndexSource_COUNT; i += 1) { AssertAlways(source_data_arr[i].str + source_data_arr[i].size == source_data_arr[i+1].str); } + String8 type_data = str8(buffer + ARENA_HEADER_SIZE, buffer_size - ARENA_HEADER_SIZE); + + // map type server to -> .debug$T + U64 obj_idx = task->type_server_indices.v[task_id]; + CV_DebugT *debug_t = &task->debug_t_arr[obj_idx]; + + // read types + CV_DebugT d = cv_debug_t_from_data(arena, type_data, PDB_LEAF_ALIGN); + + // @type_server .debug$T + debug_t->count = d.count; + debug_t->data = d.data; + debug_t->offsets = d.offsets; + for EachIndex(i, CV_TypeIndexSource_COUNT) { debug_t->ti_ranges[i] = ti_ranges[i]; } + for EachIndex(i, CV_TypeIndexSource_COUNT) { debug_t->ti_base[i] = IntFromPtr(source_data_arr[i].str - type_data.str); } + MemoryCopyTyped(debug_t->source_counts, d.source_counts, CV_TypeIndexSource_COUNT); + MemoryCopyTyped(debug_t->source_offsets, d.source_counts, CV_TypeIndexSource_COUNT); + u64_array_counts_to_offsets(CV_TypeIndexSource_COUNT, debug_t->source_offsets); + + discard_debug_info = 0; + exit:; + if (discard_debug_info) { + // an error occurred while loading external type server, discard + // parts debug info in dependent objs that rely on types + for EachNode(n, U64Node, task->ts_arr.v[ts_idx].obj_indices.first) { + U64 obj_idx = n->data; + + // discard symbols + String8List *symbols_ptr = cv_sub_section_ptr_from_debug_s(&task->debug_s_arr[obj_idx], CV_C13SubSectionKind_Symbols); + MemoryZeroStruct(symbols_ptr); + + // discard inline sites + String8List *inlineelines_ptr = cv_sub_section_ptr_from_debug_s(&task->debug_s_arr[obj_idx], CV_C13SubSectionKind_InlineeLines); + MemoryZeroStruct(inlineelines_ptr); } } - LNK_PchInfo* pch_arr = push_array_no_zero(arena, LNK_PchInfo, obj_count); - for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) { - CV_DebugT debug_t = debug_t_arr[obj_idx]; - if (cv_debug_t_is_pch(debug_t)) { - CV_Leaf precomp_leaf = cv_debug_t_get_leaf(debug_t, 0); - CV_PrecompInfo precomp = cv_precomp_info_from_leaf(precomp_leaf); - - String8 obj_path = path_absolute_dst_from_relative_dst_src(scratch.arena, precomp.obj_name, work_dir); - - // map obj name in LF_PRECOMP to obj index - U64 debug_p_obj_idx = max_U64; - if (!hash_table_search_path_u64(debug_p_ht, obj_path, &debug_p_obj_idx)) { - String8 obj_name = str8_skip_last_slash(obj_path); - for EachNode(alt_dir_n, String8Node, alt_pch_dirs.first) { - String8 alt_obj_path = str8f(scratch.arena, "%S/%S", alt_dir_n->string, obj_name); - if (hash_table_search_path_u64(debug_p_ht, alt_obj_path, &debug_p_obj_idx)) { - break; - } - } - } - - if (debug_p_obj_idx == max_U64) { - lnk_error_obj(LNK_Error_PrecompObjNotFound, obj_arr[obj_idx], "LF_PRECOMP references non-existent obj %S", obj_path); - lnk_exit(LNK_Error_PrecompObjNotFound); - } - - // get LF_PRECOMP - CV_DebugT debug_p = debug_p_arr[debug_p_obj_idx]; - CV_Leaf endprecomp_leaf = cv_debug_t_get_leaf(debug_p, precomp.leaf_count); - CV_LeafEndPreComp *endprecomp = (CV_LeafEndPreComp*) endprecomp_leaf.data.str; - - // error check LF_PRECOMP - if (precomp.start_index > CV_MinComplexTypeIndex) { - lnk_error_obj(LNK_Warning_AtypicalStartIndex, obj_arr[obj_idx], "atypical start index 0x%X in LF_PRECOMP", precomp.start_index); - } - if (precomp.start_index < CV_MinComplexTypeIndex) { - lnk_error_obj(LNK_Error_InvalidStartIndex, obj_arr[obj_idx], "invalid start index 0x%X in LF_PRECOMP; must be >= 0x%X", precomp.start_index, CV_MinComplexTypeIndex); - } - if (precomp.leaf_count > debug_p.count) { - lnk_error_obj(LNK_Error_InvalidPrecompLeafCount, obj_arr[obj_idx], "leaf count %u LF_PRECOMP exceeds leaf count %u in .debug$P in %S", precomp.leaf_count, debug_p.count, obj_arr[debug_p_obj_idx]->path); - } - - // error check LF_ENDPRECOMP - if (endprecomp_leaf.kind != CV_LeafKind_ENDPRECOMP) { - lnk_error_obj(LNK_Error_EndprecompNotFound, obj_arr[obj_idx], "unable to find LF_ENDPRECOMP @ 0x%X in %S", precomp.leaf_count, obj_arr[debug_p_obj_idx]->path); - } - if (endprecomp_leaf.data.size != sizeof(CV_LeafEndPreComp)) { - lnk_error_obj(LNK_Error_IllData, obj_arr[obj_idx], "invalid size 0x%X for LF_ENDPRECOMP", endprecomp_leaf.data.size); - } - if (endprecomp->sig != precomp.sig) { - lnk_error_obj(LNK_Error_PrecompSigMismatch, obj_arr[obj_idx], "signature mismatch between LF_PRECOMP(0x%X) and LF_ENDPRECOMP(0x%X); precomp obj %S", precomp.sig, endprecomp->sig, obj_arr[debug_p_obj_idx]->path); - } - { // check against S_OBJNAME sig in precompiled obj $$SYMBOLS - CV_SymbolList symbol_list = parsed_symbols[debug_p_obj_idx].v[0]; - if (symbol_list.count) { - CV_ObjInfo obj_info = cv_obj_info_from_symbol(symbol_list.first->data); - if (obj_info.sig != 0 && obj_info.sig != precomp.sig) { - lnk_error_obj(LNK_Error_PrecompSigMismatch, obj_arr[obj_idx], "signature mismatch between LF_PRECOMP(0x%X) and S_OBJNAME(0x%X) in %S", precomp.sig, obj_info.sig, obj_arr[debug_p_obj_idx]->path); - } - } else { - lnk_error_obj(LNK_Warning_PrecompObjSymbolsNotFound, obj_arr[obj_idx], "symbols not found, unable to chceck LF_PRECOMP signature against S_OBJ"); - } - } - - // see :pch_check - LNK_PchInfo *pch = &pch_arr[obj_idx]; - pch->ti_lo = precomp.start_index; - pch->ti_hi = precomp.start_index + precomp.leaf_count; - pch->debug_p_obj_idx = debug_p_obj_idx; - - // [start_index, start_index+type_index_count) - debug_t_arr[obj_idx].count -= 1; - debug_t_arr[obj_idx].offsets += 1; - - endprecomp_arr[debug_p_obj_idx] = cv_debug_t_get_leaf_header(debug_p, precomp.leaf_count); - } else { - LNK_PchInfo *pch = &pch_arr[obj_idx]; - pch->ti_lo = CV_MinComplexTypeIndex; - pch->ti_hi = CV_MinComplexTypeIndex; - pch->debug_p_obj_idx = 0; // :null_obj - } - } - - // remove LF_ENDPRECOMP - for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) { - if (endprecomp_arr[obj_idx]) { - endprecomp_arr[obj_idx]->kind = CV_LeafKind_NOTYPE; - endprecomp_arr[obj_idx]->size = sizeof(CV_LeafKind); - } - } + task->is_type_server_discarded[task_id] = discard_debug_info; + ProfEnd(); scratch_end(scratch); - return pch_arr; -} - -internal void -lnk_do_debug_info_discard(CV_DebugS *debug_s_arr, CV_SymbolListArray *parsed_symbols, U64 obj_idx) -{ - // remove symbols - for (U64 i = 0; i < parsed_symbols[obj_idx].count; ++i) { - MemoryZeroStruct(&parsed_symbols[obj_idx].v[i]); - } - - // remove inline sites - String8List *inlineelines_ptr = cv_sub_section_ptr_from_debug_s(&debug_s_arr[obj_idx], CV_C13SubSectionKind_InlineeLines); - MemoryZeroStruct(inlineelines_ptr); -} - -internal -THREAD_POOL_TASK_FUNC(lnk_msf_parsed_from_data_task) -{ - ProfBeginFunction(); - LNK_MsfParsedFromDataTask *task = raw_task; - // TODO: pick Info, TPI and IPI to flattten to make sure we don't waste compute on throw-away streams - MSF_Parsed *msf = 0; - if (task->data_arr.v[task_id].size > 0) { - msf = msf_parsed_from_data(arena, task->data_arr.v[task_id]); - } - task->msf_parse_arr[task_id] = msf; - ProfEnd(); -} - -internal MSF_Parsed ** -lnk_msf_parsed_from_data_parallel(TP_Arena *arena, TP_Context *tp, String8Array data_arr) -{ - ProfBeginFunction(); - LNK_MsfParsedFromDataTask task = {0}; - task.data_arr = data_arr; - task.msf_parse_arr = push_array_no_zero(arena->v[0], MSF_Parsed *, data_arr.count); - tp_for_parallel(tp, arena, data_arr.count, lnk_msf_parsed_from_data_task, &task); - ProfEnd(); - return task.msf_parse_arr; -} - -internal -THREAD_POOL_TASK_FUNC(lnk_get_external_leaves_task) -{ - ProfBeginFunction(); - - U64 ts_idx = task_id; - LNK_GetExternalLeavesTask *task = raw_task; - MSF_Parsed *msf_parse = task->msf_parse_arr[ts_idx]; - - task->external_ti_ranges[ts_idx] = push_array(arena, Rng1U64, CV_TypeIndexSource_COUNT); - task->external_leaves[ts_idx] = push_array(arena, CV_DebugT, CV_TypeIndexSource_COUNT); - task->is_corrupted[ts_idx] = 1; - - if (msf_parse) { - PDB_OpenTypeServerError tpi_error = PDB_OpenTypeServerError_UNKNOWN; - PDB_OpenTypeServerError ipi_error = PDB_OpenTypeServerError_UNKNOWN; - - PDB_TypeServerParse tpi_parse, ipi_parse; - if (PDB_FixedStream_Tpi < msf_parse->stream_count && PDB_FixedStream_Ipi < msf_parse->stream_count) { - tpi_error = pdb_type_server_parse_from_data(msf_parse->streams[PDB_FixedStream_Tpi], &tpi_parse); - ipi_error = pdb_type_server_parse_from_data(msf_parse->streams[PDB_FixedStream_Ipi], &ipi_parse); - } - - if (tpi_error == PDB_OpenTypeServerError_OK && ipi_error == PDB_OpenTypeServerError_OK) { - task->is_corrupted[ts_idx] = 0; - - task->external_ti_ranges[ts_idx][CV_TypeIndexSource_NULL] = rng_1u64(0,0); - task->external_ti_ranges[ts_idx][CV_TypeIndexSource_TPI ] = tpi_parse.ti_range; - task->external_ti_ranges[ts_idx][CV_TypeIndexSource_IPI ] = ipi_parse.ti_range; - - MemoryZeroStruct(&task->external_leaves[ts_idx][CV_TypeIndexSource_NULL]); - task->external_leaves[ts_idx][CV_TypeIndexSource_TPI] = cv_debug_t_from_data(arena, tpi_parse.leaf_data, PDB_LEAF_ALIGN); - task->external_leaves[ts_idx][CV_TypeIndexSource_IPI] = cv_debug_t_from_data(arena, ipi_parse.leaf_data, PDB_LEAF_ALIGN); - } else { - if (tpi_error != PDB_OpenTypeServerError_OK) { - lnk_error(LNK_Error_UnableToOpenTypeServer, "failed to open TPI in %S, reson %S", task->ts_info_arr[ts_idx].name, pdb_string_from_open_type_server_error(tpi_error)); - } - if (ipi_error != PDB_OpenTypeServerError_OK) { - lnk_error(LNK_Error_UnableToOpenTypeServer, "failed to open IPI in %S, reason %S", task->ts_info_arr[ts_idx].name, pdb_string_from_open_type_server_error(ipi_error)); - } - } - } - - ProfEnd(); -} - -internal CV_DebugT * -lnk_merge_debug_t_and_debug_p(Arena *arena, U64 obj_count, CV_DebugT *debug_t_arr, CV_DebugT *debug_p_arr) -{ - CV_DebugT *result = push_array_no_zero(arena, CV_DebugT, obj_count); - for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) { - CV_DebugT *debug_p = &debug_p_arr[obj_idx]; - CV_DebugT *debug_t = &debug_t_arr[obj_idx]; - if (debug_p->count) { - Assert(!debug_t->count); - result[obj_idx] = *debug_p; - } else if (debug_t->count) { - Assert(!debug_p->count); - result[obj_idx] = *debug_t; - } else { - MemoryZeroStruct(&result[obj_idx]); - } - } - return result; } internal LNK_CodeViewInput @@ -390,28 +250,22 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla { ProfBegin("Extract CodeView"); Temp scratch = scratch_begin(0,0); + + LNK_CodeViewInput input = { .io_flags = io_flags, .count = obj_count, .obj_arr = obj_arr, .ts_obj_range = r1u64(0,0) }; - // gather debug info sections from objs ProfBegin("Collect CodeView"); // TODO: fix memory leak, we need a Temp wrapper for pool arena - B32 collect_discarded_flag = 0; - String8List *debug_s_list_arr = lnk_collect_obj_sections(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug$S"), collect_discarded_flag); - String8List *debug_p_list_arr = lnk_collect_obj_sections(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug$P"), collect_discarded_flag); - String8List *debug_t_list_arr = lnk_collect_obj_sections(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug$T"), collect_discarded_flag); + String8List *debug_s_list_arr = lnk_collect_obj_sections(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug$S"), 0); + String8List *debug_p_list_arr = lnk_collect_obj_sections(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug$P"), 0); + String8List *debug_t_list_arr = lnk_collect_obj_sections(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug$T"), 0); ProfEnd(); if (lnk_get_log_status(LNK_Log_Debug) || PROFILE_TELEMETRY) { U64 total_debug_s_size = 0, total_debug_t_size = 0, total_debug_p_size = 0; - for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) { - for (String8Node *chunk = debug_s_list_arr[obj_idx].first; chunk != 0; chunk = chunk->next) { - total_debug_s_size += chunk->string.size; - } - for (String8Node *chunk = debug_t_list_arr[obj_idx].first; chunk != 0; chunk = chunk->next) { - total_debug_t_size += chunk->string.size; - } - for (String8Node *chunk = debug_p_list_arr[obj_idx].first; chunk != 0; chunk = chunk->next) { - total_debug_p_size += chunk->string.size; - } + for EachIndex(obj_idx, obj_count) { + for EachNode(n, String8Node, debug_s_list_arr[obj_idx].first) { total_debug_s_size += n->string.size; } + for EachNode(n, String8Node, debug_t_list_arr[obj_idx].first) { total_debug_t_size += n->string.size; } + for EachNode(n, String8Node, debug_p_list_arr[obj_idx].first) { total_debug_p_size += n->string.size; } } ProfNoteV("Total .debug$S Input Size: %M", total_debug_s_size); @@ -426,733 +280,378 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla } ProfBegin("Parse CodeView"); - CV_DebugS *debug_s_arr = lnk_parse_debug_s_sections(tp, tp_arena, obj_count, obj_arr, debug_s_list_arr); - CV_DebugT *debug_p_arr = lnk_parse_debug_t_sections(tp, tp_arena, obj_count, obj_arr, debug_p_list_arr); - CV_DebugT *debug_t_arr = lnk_parse_debug_t_sections(tp, tp_arena, obj_count, obj_arr, debug_t_list_arr); - ProfEnd(); - - ProfBegin("Sort Type Servers"); - - U64 external_count = 0, internal_count = 0; - LNK_Obj **sorted_obj_arr = push_array_no_zero(tp_arena->v[0], LNK_Obj *, obj_count); - CV_DebugS *sorted_debug_s_arr = push_array_no_zero(tp_arena->v[0], CV_DebugS, obj_count); - CV_DebugT *sorted_debug_t_arr = push_array_no_zero(tp_arena->v[0], CV_DebugT, obj_count); - CV_DebugT *sorted_debug_p_arr = push_array_no_zero(tp_arena->v[0], CV_DebugT, obj_count); - for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) { - B32 is_type_server = cv_debug_t_is_type_server(debug_t_arr[obj_idx]); - if (is_type_server) { - Assert(internal_count + external_count < obj_count); - U64 slot_idx = (obj_count - external_count - 1); - ++external_count; - - // TODO: report error: somehow obj was compiled with /Zi and /Yc - Assert(debug_p_arr[obj_idx].count == 0); - - sorted_obj_arr[slot_idx] = obj_arr[obj_idx]; - sorted_debug_s_arr[slot_idx] = debug_s_arr[obj_idx]; - sorted_debug_t_arr[slot_idx] = debug_t_arr[obj_idx]; - MemoryZeroStruct(&sorted_debug_p_arr[slot_idx]); - } else { - Assert(internal_count + external_count < obj_count); - U64 slot_idx = internal_count; - ++internal_count; - - sorted_obj_arr[slot_idx] = obj_arr[obj_idx]; - sorted_debug_s_arr[slot_idx] = debug_s_arr[obj_idx]; - sorted_debug_t_arr[slot_idx] = debug_t_arr[obj_idx]; - sorted_debug_p_arr[slot_idx] = debug_p_arr[obj_idx]; - } - } - - ProfEnd(); - - // setup pointers to arrays - LNK_Obj **internal_obj_arr = sorted_obj_arr; - LNK_Obj **external_obj_arr = sorted_obj_arr + internal_count; - CV_DebugS *internal_debug_s_arr = sorted_debug_s_arr; - CV_DebugS *external_debug_s_arr = sorted_debug_s_arr + internal_count; - CV_DebugT *internal_debug_t_arr = sorted_debug_t_arr; - CV_DebugT *external_debug_t_arr = sorted_debug_t_arr + internal_count; - CV_DebugT *internal_debug_p_arr = sorted_debug_p_arr; - CV_DebugT *external_debug_p_arr = sorted_debug_p_arr + internal_count; - - ProfBegin("Parse Symbols"); - - ProfBegin("Count Symbol Inputs"); - U64 internal_total_symbol_input_count = 0; - U64 external_total_symbol_input_count = 0; - for (U64 obj_idx = 0; obj_idx < internal_count; ++obj_idx) { - String8List raw_symbols = cv_sub_section_from_debug_s(internal_debug_s_arr[obj_idx], CV_C13SubSectionKind_Symbols); - internal_total_symbol_input_count += raw_symbols.node_count; - } - for (U64 obj_idx = 0; obj_idx < external_count; ++obj_idx) { - String8List raw_symbols = cv_sub_section_from_debug_s(external_debug_s_arr[obj_idx], CV_C13SubSectionKind_Symbols); - external_total_symbol_input_count += raw_symbols.node_count; - } + input.debug_s_arr = lnk_parse_debug_s_sections(tp, tp_arena, obj_count, obj_arr, debug_s_list_arr); + input.debug_p_arr = lnk_parse_debug_t_sections(tp, tp_arena, obj_count, obj_arr, debug_p_list_arr); + input.debug_t_arr = lnk_parse_debug_t_sections(tp, tp_arena, obj_count, obj_arr, debug_t_list_arr); + input.debug_h_arr = push_array(tp_arena->v[0], CV_DebugH, obj_count); // TODO: collect & parse .debug$H ProfEnd(); - ProfBegin("Prepare Symbol Inputs"); - U64 total_symbol_input_count = internal_total_symbol_input_count + external_total_symbol_input_count; - LNK_CodeViewSymbolsInput *symbol_inputs = push_array_no_zero(tp_arena->v[0], LNK_CodeViewSymbolsInput, total_symbol_input_count); - CV_SymbolListArray *parsed_symbols = push_array_no_zero(tp_arena->v[0], CV_SymbolListArray, obj_count); + ProfBegin("Set up symbol parsing"); + for EachIndex(obj_idx, obj_count) { input.symbol_input_count += cv_sub_section_from_debug_s(input.debug_s_arr[obj_idx], CV_C13SubSectionKind_Symbols).node_count; } + input.symbol_inputs = push_array_no_zero(tp_arena->v[0], LNK_SymbolInput, input.symbol_input_count); + input.parsed_symbols = push_array_no_zero(tp_arena->v[0], CV_SymbolListArray, obj_count); { - CV_SymbolList *reserved_lists = push_array(tp_arena->v[0], CV_SymbolList, total_symbol_input_count); + CV_SymbolList *reserved_lists = push_array(tp_arena->v[0], CV_SymbolList, input.symbol_input_count); for (U64 obj_idx = 0, input_idx = 0; obj_idx < obj_count; ++obj_idx) { - String8List raw_symbols = cv_sub_section_from_debug_s(sorted_debug_s_arr[obj_idx], CV_C13SubSectionKind_Symbols); + String8List raw_symbols = cv_sub_section_from_debug_s(input.debug_s_arr[obj_idx], CV_C13SubSectionKind_Symbols); // init parse output if (raw_symbols.node_count > 0) { - parsed_symbols[obj_idx].count = raw_symbols.node_count; - parsed_symbols[obj_idx].v = reserved_lists + input_idx; + input.parsed_symbols[obj_idx].count = raw_symbols.node_count; + input.parsed_symbols[obj_idx].v = reserved_lists + input_idx; } else { - parsed_symbols[obj_idx].count = 0; - parsed_symbols[obj_idx].v = 0; + input.parsed_symbols[obj_idx].count = 0; + input.parsed_symbols[obj_idx].v = 0; } // init worker input for (String8Node *data_n = raw_symbols.first; data_n != 0; data_n = data_n->next, ++input_idx) { - Assert(input_idx < total_symbol_input_count); - LNK_CodeViewSymbolsInput *in = &symbol_inputs[input_idx]; + Assert(input_idx < input.symbol_input_count); + LNK_SymbolInput *in = &input.symbol_inputs[input_idx]; in->obj_idx = obj_idx; in->symbol_list = &reserved_lists[input_idx]; in->raw_symbols = data_n->string; } } + + tp_for_parallel_prof(tp, tp_arena, input.symbol_input_count, lnk_parse_cv_symbols_task, &(LNK_ParseCVSymbolsTaskData){ .inputs = input.symbol_inputs }, "Symbol Parse"); } ProfEnd(); - tp_for_parallel_prof(tp, tp_arena, total_symbol_input_count, lnk_parse_cv_symbols_task, &(LNK_ParseCVSymbolsTaskData){ .inputs = symbol_inputs }, "Symbol Parse"); + // sort objs based on type: PCH, /Zi (external), /Z7 (internal) + input.debug_p_indices.v = push_array(tp_arena->v[0], U32, obj_count); + input.ext_obj_indices.v = push_array(tp_arena->v[0], U32, obj_count); + input.int_obj_indices.v = push_array(tp_arena->v[0], U32, obj_count); + for EachIndex(obj_idx, obj_count) { + CV_DebugT *debug_t = &input.debug_t_arr[obj_idx]; + CV_DebugT *debug_p = &input.debug_p_arr[obj_idx]; + U32Array *arr_ptr; - // TODO: do we rely on this behaviour? - // - // :zero_out_symbol_sub_section - ProfBegin("Zero-out Symbols Sub-sections"); - for (U64 i = 0; i < obj_count; ++i) { - CV_DebugS *debug_s = &sorted_debug_s_arr[i]; - String8List *symbols_ptr = cv_sub_section_ptr_from_debug_s(debug_s, CV_C13SubSectionKind_Symbols); - MemoryZeroStruct(symbols_ptr); + if (debug_t->count && debug_p->count) { + lnk_error_obj(LNK_Warning_MultipleDebugTAndDebugP, obj_arr[obj_idx], "multiple sections with debug types detected, obj must have either .debug$T or .debug$P (using .debug$T for type server)"); + } + + if (debug_p->count > 0) { arr_ptr = &input.debug_p_indices; } + else if (cv_debug_t_is_type_server_ref(debug_t)) { arr_ptr = &input.ext_obj_indices; } + else { arr_ptr = &input.int_obj_indices; } + arr_ptr->v[arr_ptr->count++] = obj_idx; } - ProfEnd(); - ProfEnd(); - - CV_SymbolListArray *internal_parsed_symbols = parsed_symbols; - CV_SymbolListArray *external_parsed_symbols = parsed_symbols + internal_count; - LNK_CodeViewSymbolsInput *internal_symbol_inputs = symbol_inputs; - LNK_CodeViewSymbolsInput *external_symbol_inputs = symbol_inputs + internal_count; - - LNK_PchInfo *pch_arr = lnk_setup_pch(tp_arena->v[0], - internal_count, - internal_obj_arr, - internal_debug_t_arr, - internal_debug_p_arr, - internal_parsed_symbols, - alt_pch_dirs); - - CV_DebugT *merged_debug_t_p_arr = lnk_merge_debug_t_and_debug_p(tp_arena->v[0], internal_count, internal_debug_t_arr, internal_debug_p_arr); - - ProfBegin("Analyze & Read External Type Server Files"); - String8Array ts_path_arr; - Rng1U64 **external_ti_ranges; - CV_DebugT **external_leaves; - U64 *obj_to_ts_idx_arr = push_array_no_zero(tp_arena->v[0], U64, external_count + 1); - U64List *ts_to_obj_arr = push_array(tp_arena->v[0], U64List, external_count + 1); + ProfScope("Set up type servers") { - HashTable *type_server_path_ht = hash_table_init(scratch.arena, 256); - HashTable *ignored_path_ht = hash_table_init(scratch.arena, 256); - CV_TypeServerInfoList ts_info_list = {0}; + input.obj_to_ts = push_array(tp_arena->v[0], U64, input.count); + LNK_TypeServerList ts_list = {0}; + HashTable *ts_ht = hash_table_init(scratch.arena, 256); - // push null - CV_TypeServerInfoNode *null_ts_info = push_array(scratch.arena, CV_TypeServerInfoNode, 1); - null_ts_info->data.name = str8_lit("\0"); - SLLQueuePush(ts_info_list.first, ts_info_list.last, null_ts_info); - ++ts_info_list.count; + // push null so unopened type servers are resolved to this type server + LNK_TypeServerNode *null_ts = push_array(scratch.arena, LNK_TypeServerNode, 1); + SLLQueuePush(ts_list.first, ts_list.last, null_ts); + ts_list.count += 1; + null_ts->v.ts_path = str8_lit(""); + hash_table_push_path_raw(scratch.arena, ts_ht, str8_lit(""), null_ts); - for (U64 obj_idx = 0; obj_idx < external_count; ++obj_idx) { - // first leaf always type server - CV_DebugT debug_t = external_debug_t_arr[obj_idx]; - CV_Leaf leaf = cv_debug_t_get_leaf(debug_t, 0); - CV_TypeServerInfo ts = cv_type_server_info_from_leaf(leaf); + for EachIndex(i, input.ext_obj_indices.count) { + // first leaf is always type server + U64 obj_idx = input.ext_obj_indices.v[i]; + CV_DebugT *debug_t = &input.debug_t_arr[obj_idx]; + CV_Leaf leaf = cv_debug_t_get_leaf(debug_t, 0); + CV_TypeServerInfo ts_info = cv_type_server_info_from_leaf(leaf); + String8 ts_path = lnk_find_first_file(scratch.arena, lib_dir_list, ts_info.name); - // search disk for type server - String8 type_server_path = lnk_find_first_file(scratch.arena, lib_dir_list, ts.name); - - // report no match - B32 do_debug_info_discard = 0; - if (type_server_path.size == 0) { - if (!hash_table_search_path(ignored_path_ht, ts.name)) { - hash_table_push_string_u64(scratch.arena, ignored_path_ht, ts.name, 0); - lnk_error_obj(LNK_Warning_MissingExternalTypeServer, obj_arr[obj_idx], "unable to open external type server %S", ts.name); - } - do_debug_info_discard = 1; + // insert new type server + LNK_TypeServer *ts = hash_table_search_path_raw(ts_ht, ts_path); + if (ts == 0) { + LNK_TypeServerNode *n = push_array(scratch.arena, LNK_TypeServerNode, 1); + SLLQueuePush(ts_list.first, ts_list.last, n); + ts_list.count += 1; + ts = &n->v; + ts->ts_info = ts_info; + ts->ts_idx = ts_ht->count; + ts->ts_path = push_str8_copy(tp_arena->v[0], ts_path); + hash_table_push_path_raw(scratch.arena, ts_ht, ts->ts_path, ts); } - - // external type server is missing, discard parts of debug info that need types - if (do_debug_info_discard) { - lnk_do_debug_info_discard(external_debug_s_arr, external_parsed_symbols, obj_idx); + + // signature check + if ( ! MemoryMatchStruct(&ts_info.sig, &ts->ts_info.sig)) { + lnk_error_obj(LNK_Error_ExternalTypeServerConflict, + obj_arr[obj_idx], + "type server signature conflicts with type server from '%S'", + obj_arr[ts->obj_indices.first->data]->path); continue; } - { - struct HT_Value { - CV_TypeServerInfo ts; - LNK_Obj *obj; - U64 ts_idx; - }; + // type server -> obj + u64_list_push(tp_arena->v[0], &ts->obj_indices, obj_idx); - // was this type server queued? - BucketNode *is_path_queued = hash_table_search_path(type_server_path_ht, type_server_path); - if (is_path_queued) { - struct HT_Value *present = is_path_queued->v.value_raw; - - // make sure type servers sigs match - if (MemoryMatchStruct(&ts.sig, &present->ts.sig)) { - // wire obj to type server data - obj_to_ts_idx_arr[obj_idx] = present->ts_idx; + // obj -> type server + input.obj_to_ts[obj_idx] = ts->ts_idx; + } - // wire type server to obj - u64_list_push(tp_arena->v[0], &ts_to_obj_arr[present->ts_idx], obj_idx); - } else { - lnk_error_obj(LNK_Error_ExternalTypeServerConflict, - obj_arr[obj_idx], - "external type server signature conflicts with type server loaded from '%S'", - present->obj->path); - } - } else { - U64 ts_idx = ts_info_list.count; + // list -> array + LNK_TypeServerArray ts_arr = { .v = push_array(tp_arena->v[0], LNK_TypeServer, ts_list.count) }; + for EachNode(n, LNK_TypeServerNode, ts_list.first) { ts_arr.v[ts_arr.count++] = n->v; } - // when we search matches on disk we store path on scratch, - // make path copy in case we need it for error reporting - type_server_path = push_str8_copy(tp_arena->v[0], type_server_path); + // extend arrays to include type servers + if (ts_arr.count) { + LNK_CodeViewInput prev = input; - // fill out type server info we read from obj - CV_TypeServerInfoNode *ts_info_node = push_array(scratch.arena, CV_TypeServerInfoNode, 1); - ts_info_node->data = ts; - ts_info_node->data.name = type_server_path; + input.count += ts_arr.count; + input.obj_arr = push_array(tp_arena->v[0], LNK_Obj *, input.count); + input.debug_s_arr = push_array(tp_arena->v[0], CV_DebugS, input.count); + input.debug_p_arr = push_array(tp_arena->v[0], CV_DebugT, input.count); + input.debug_t_arr = push_array(tp_arena->v[0], CV_DebugT, input.count); + input.debug_h_arr = push_array(tp_arena->v[0], CV_DebugH, input.count); + input.parsed_symbols = push_array(tp_arena->v[0], CV_SymbolListArray, input.count); + input.obj_to_ts = push_array(tp_arena->v[0], U64, input.count); - // push to type server info list - SLLQueuePush(ts_info_list.first, ts_info_list.last, ts_info_node); - ts_info_list.count += 1; - - // wire obj to type server - obj_to_ts_idx_arr[obj_idx] = ts_idx; + MemoryCopyTyped(input.obj_arr, prev.obj_arr, prev.count); + MemoryCopyTyped(input.debug_s_arr, prev.debug_s_arr, prev.count); + MemoryCopyTyped(input.debug_p_arr, prev.debug_p_arr, prev.count); + MemoryCopyTyped(input.debug_t_arr, prev.debug_t_arr, prev.count); + MemoryCopyTyped(input.parsed_symbols, prev.parsed_symbols, prev.count); + MemoryCopyTyped(input.obj_to_ts, prev.obj_to_ts, prev.count); - // wire type server to obj - u64_list_push(tp_arena->v[0], &ts_to_obj_arr[ts_idx], obj_idx); - - // fill out value - struct HT_Value *value = push_array(scratch.arena, struct HT_Value, 1); - value->ts = ts; - value->obj = obj_arr[obj_idx]; - value->ts_idx = ts_idx; - - // update hash table - hash_table_push_path_raw(scratch.arena, type_server_path_ht, type_server_path, value); - } + input.ts_obj_range = r1u64(prev.count, input.count); + + // alloc dummy objs with for each loaded type server + for EachIndex(i, ts_arr.count) { + LNK_Obj *ts_obj = push_array(tp_arena->v[0], LNK_Obj, ts_arr.count); + ts_obj->path = ts_arr.v[i].ts_path; + input.obj_arr[prev.count + i] = ts_obj; + } + + // make type server indices + input.type_server_indices.count = ts_arr.count; + input.type_server_indices.v = push_array(tp_arena->v[0], U32, ts_arr.count); + for EachIndex(i, ts_arr.count) { input.type_server_indices.v[i] = prev.count + i; } + } + + input.ts_arr = ts_arr; + input.is_type_server_discarded = push_array(tp_arena->v[0], B32, input.ts_arr.count); + tp_for_parallel_prof(tp, tp_arena, input.ts_arr.count, lnk_read_type_servers_task, &input, "read type servers"); + + // fixup null type server + input.is_type_server_discarded[0] = 0; + + // report bad type servers + String8List unopen_type_server_list = {0}; + for EachIndex(ts_idx, ts_arr.count) { + if ( ! input.is_type_server_discarded[ts_idx]) { continue; } + str8_list_pushf(scratch.arena, &unopen_type_server_list, "\t%S\n", ts_arr.v[ts_idx].ts_path); + str8_list_pushf(scratch.arena, &unopen_type_server_list, "\t\tDependent obj(s):\n"); + for EachNode(n, U64Node, input.ts_arr.v[ts_idx].obj_indices.first) { + str8_list_pushf(scratch.arena, &unopen_type_server_list, "\t\t\t%S\n", obj_arr[n->data]->path); + } + } + if (unopen_type_server_list.node_count) { + String8List error_msg_list = {0}; + str8_list_pushf(scratch.arena, &error_msg_list, "unable to open external type server(s):\n"); + str8_list_concat_in_place(&error_msg_list, &unopen_type_server_list); + lnk_error(LNK_Error_UnableToOpenTypeServer, "%S", str8_list_join(scratch.arena, &error_msg_list, 0)); + } + } + + ProfBegin("Set up PCH indirection"); + { + // hash_table + String8 work_dir = os_get_current_path(scratch.arena); + HashTable *debug_p_ht = hash_table_init(scratch.arena, obj_count); + for EachIndex(i, input.debug_p_indices.count) { + U64 obj_idx = input.debug_p_indices.v[i]; + String8 obj_path = path_absolute_dst_from_relative_dst_src(scratch.arena, obj_arr[obj_idx]->path, work_dir); + if (hash_table_search_path(debug_p_ht, obj_path)) { + lnk_error_obj(LNK_Warning_DuplicateObjPath, obj_arr[obj_idx], "duplicate obj path %S", obj_path); + } else { + hash_table_push_path_u64(scratch.arena, debug_p_ht, obj_path, obj_idx); } } - // type server info list -> array - ts_path_arr.count = ts_info_list.count; - ts_path_arr.v = push_array(tp_arena->v[0], String8, ts_info_list.count); - CV_TypeServerInfo *ts_info_arr = push_array(scratch.arena, CV_TypeServerInfo, ts_info_list.count); - { - U64 idx = 0; - for (CV_TypeServerInfoNode *n = ts_info_list.first; n != 0; n = n->next, ++idx) { - ts_path_arr.v[idx] = n->data.name; - ts_info_arr[idx] = n->data; + for EachIndex(i, input.int_obj_indices.count) { + U64 obj_idx = input.int_obj_indices.v[i]; + LNK_Obj *obj = obj_arr[obj_idx]; + CV_DebugT *debug_t = &input.debug_t_arr[obj_idx]; + + // skip non-PCH objs + if ( ! cv_debug_t_is_pch(debug_t)) { continue; } + + // get PCH ref info + CV_PrecompInfo precomp = cv_precomp_info_from_leaf(cv_debug_t_get_leaf(debug_t, 0)); + + // find PCH obj + String8 obj_path = path_absolute_dst_from_relative_dst_src(scratch.arena, precomp.obj_name, work_dir); + U64 debug_p_obj_idx = max_U64; + if (!hash_table_search_path_u64(debug_p_ht, obj_path, &debug_p_obj_idx)) { + // try alternative directory for the PCH + String8 obj_name = str8_skip_last_slash(obj_path); + for EachNode(alt_dir_n, String8Node, alt_pch_dirs.first) { + String8 alt_obj_path = str8f(scratch.arena, "%S/%S", alt_dir_n->string, obj_name); + if (hash_table_search_path_u64(debug_p_ht, alt_obj_path, &debug_p_obj_idx)) { break; } + } + if (debug_p_obj_idx == max_U64) { + lnk_error_obj(LNK_Error_PrecompObjNotFound, obj, "LF_PRECOMP references non-existent obj %S", obj_path); + lnk_exit(LNK_Error_PrecompObjNotFound); + } } + + // get PCH leaf data + CV_DebugT *debug_p = &input.debug_p_arr[debug_p_obj_idx]; + + // error check LF_PRECOMP + if (precomp.start_index > CV_MinComplexTypeIndex) { lnk_error_obj(LNK_Warning_AtypicalStartIndex, obj, "atypical start index 0x%x in LF_PRECOMP", precomp.start_index); } + if (precomp.start_index < CV_MinComplexTypeIndex) { lnk_error_obj(LNK_Error_InvalidStartIndex, obj, "invalid start index 0x%x in LF_PRECOMP; must be >= 0x%x", precomp.start_index, CV_MinComplexTypeIndex); continue; } + if (precomp.leaf_count > debug_p->count) { lnk_error_obj(LNK_Error_InvalidPrecompLeafCount, obj, "leaf count %u LF_PRECOMP exceeds leaf count %u in .debug$P in %S", precomp.leaf_count, debug_p->count, obj_arr[debug_p_obj_idx]->path); continue; } + + // get LF_PRECOMP + CV_Leaf endprecomp_leaf = cv_debug_t_get_leaf(debug_p, precomp.leaf_count-1); + CV_LeafEndPreComp *endprecomp = str8_deserial_get_raw_ptr(endprecomp_leaf.data, 0, sizeof(*endprecomp)); + + // error check LF_ENDPRECOMP + if (endprecomp_leaf.kind != CV_LeafKind_ENDPRECOMP) { lnk_error_obj(LNK_Error_EndprecompNotFound, obj, "missing LF_ENDPRECOMP [0x%x] in %S", precomp.leaf_count, obj_arr[debug_p_obj_idx]->path); continue; } + if (endprecomp_leaf.data.size != sizeof(CV_LeafEndPreComp)) { lnk_error_obj(LNK_Error_IllData, obj, "invalid size 0x%x for LF_ENDPRECOMP", endprecomp_leaf.data.size); continue; } + if (endprecomp->sig != precomp.sig) { lnk_error_obj(LNK_Error_PrecompSigMismatch, obj, "signature mismatch between LF_PRECOMP(0x%x) and LF_ENDPRECOMP(0x%x); precomp obj %S", precomp.sig, endprecomp->sig, obj_arr[debug_p_obj_idx]->path); continue; } + + { // PCH and OBJ signatures must match + String8List symbols = cv_sub_section_from_debug_s(input.debug_s_arr[debug_p_obj_idx], CV_C13SubSectionKind_Symbols); + CV_SymbolList symbol_list = {0}; + cv_parse_symbol_sub_section_capped(scratch.arena, &symbol_list, 0, str8_list_first(&symbols), CV_SymbolAlign, 1); + CV_Symbol symbol = symbol_list.count ? symbol_list.first->data : (CV_Symbol){0}; + if (cv_is_obj_info(symbol)) { + CV_ObjInfo obj_info = cv_obj_info_from_symbol(symbol); + if (obj_info.sig != 0 && obj_info.sig != precomp.sig) { + lnk_error_obj(LNK_Error_PrecompSigMismatch, obj, "signature mismatch between LF_PRECOMP(0x%X) and S_OBJNAME(0x%X) in %S", precomp.sig, obj_info.sig, obj_arr[debug_p_obj_idx]->path); + continue; + } + } else { + lnk_error_obj(LNK_Error_PrecompSigMismatch, obj, "missing S_OBJNAME, unable to validate PCH signature"); + continue; + } + } + + for (U64 i = 1; i < CV_TypeIndexSource_COUNT; i += 1) { debug_t->pch_ti_range[i] = r1u64(precomp.start_index, precomp.start_index + precomp.leaf_count); } + debug_t->pch_obj_idx = debug_p_obj_idx; + + // remove CV_LeafKind_PRECOMP + debug_t->count -= 1; + debug_t->offsets += 1; } - // read type servers from disk in parallel - { - ProfBegin("Read External Type Servers"); - String8Array msf_data_arr = lnk_read_data_from_file_path_parallel(tp, scratch.arena, 0, ts_path_arr); - ProfEnd(); - - MSF_Parsed **msf_parse_arr = lnk_msf_parsed_from_data_parallel(tp_arena, tp, msf_data_arr); - - ProfBegin("Error check type servers"); - for (U64 ts_idx = 0; ts_idx < msf_data_arr.count; ++ts_idx) { - MSF_Parsed *msf_parse = msf_parse_arr[ts_idx]; - - B32 do_debug_info_discard = 0; - - if (!msf_parse) { - do_debug_info_discard = 1; - } else { - PDB_InfoParse info_parse = {0}; - pdb_info_parse_from_data(msf_parse->streams[PDB_FixedStream_Info], &info_parse); - if (!MemoryMatchStruct(&info_parse.guid, &ts_info_arr[ts_idx].sig)) { - Temp scratch = scratch_begin(0,0); - String8 expected_sig_str = string_from_guid(scratch.arena, ts_info_arr[ts_idx].sig); - String8 on_disk_sig_str = string_from_guid(scratch.arena, info_parse.guid); - lnk_error(LNK_Warning_MismatchedTypeServerSignature, "%S: signature mismatch in type server read from disk, expected %S, got %S", - ts_info_arr[ts_idx].name, expected_sig_str, on_disk_sig_str); - scratch_end(scratch); - - do_debug_info_discard = 1; - } - } - - if (do_debug_info_discard) { - U64List obj_idx_list = ts_to_obj_arr[ts_idx]; - for (U64Node *obj_idx_n = obj_idx_list.first; obj_idx_n != 0; obj_idx_n = obj_idx_n->next) { - lnk_do_debug_info_discard(external_debug_s_arr, external_parsed_symbols, obj_idx_n->data); - } + // remove CV_LeafKind_ENDPRECOMP from .debug$P + for EachIndex(i, input.debug_p_indices.count) { + U64 debug_p_idx = input.debug_p_indices.v[i]; + CV_DebugT *debug_p = &input.debug_p_arr[debug_p_idx]; + for EachIndex(i, debug_p->count) { + U64 lf_idx = debug_p->count - (i + 1); + CV_LeafHeader *lf = cv_debug_t_get_leaf_header(debug_p, lf_idx); + if (lf->kind == CV_LeafKind_ENDPRECOMP) { + memory_write16(&lf->kind, CV_LeafKind_NOTYPE); + memory_write16(&lf->size, 0); + break; } } - ProfEnd(); - - ProfBeginDynamic("Open External Type Servers [Count %llu]", ts_path_arr.count); - LNK_GetExternalLeavesTask task = {0}; - task.ts_info_arr = ts_info_arr; - task.msf_parse_arr = msf_parse_arr; - task.external_ti_ranges = push_array_no_zero(tp_arena->v[0], Rng1U64 *, msf_data_arr.count); - task.external_leaves = push_array_no_zero(tp_arena->v[0], CV_DebugT *, msf_data_arr.count); - task.is_corrupted = push_array_no_zero(scratch.arena, B8, msf_data_arr.count); - tp_for_parallel(tp, tp_arena, msf_data_arr.count, lnk_get_external_leaves_task, &task); - ProfEnd(); - - String8List unopen_type_server_list = {0}; - - // discard debug info that depends on the missing type server - for (U64 ts_idx = 1; ts_idx < msf_data_arr.count; ++ts_idx) { - if (task.is_corrupted[ts_idx]) { - U64List obj_idx_list = ts_to_obj_arr[ts_idx]; - for (U64Node *node = obj_idx_list.first; node != 0; node = node->next) { - lnk_do_debug_info_discard(external_debug_s_arr, external_parsed_symbols, node->data); - } - } - } - - // format error - for (U64 ts_idx = 1; ts_idx < msf_data_arr.count; ++ts_idx) { - if (task.is_corrupted[ts_idx]) { - U64List obj_idx_list = ts_to_obj_arr[ts_idx]; - str8_list_pushf(scratch.arena, &unopen_type_server_list, "\t%S\n", ts_path_arr.v[ts_idx]); - str8_list_pushf(scratch.arena, &unopen_type_server_list, "\t\tDependent obj(s):\n"); - for (U64Node *obj_idx_node = obj_idx_list.first; obj_idx_node != 0; obj_idx_node = obj_idx_node->next) { - String8 obj_path = external_obj_arr[obj_idx_node->data]->path; - str8_list_pushf(scratch.arena, &unopen_type_server_list, "\t\t\t%S\n", obj_path); - } - } - } - if (unopen_type_server_list.node_count) { - String8List error_msg_list = { 0 }; - str8_list_pushf(scratch.arena, &error_msg_list, "unable to open external type server(s):\n"); - str8_list_concat_in_place(&error_msg_list, &unopen_type_server_list); - String8 error_msg = str8_list_join(scratch.arena, &error_msg_list, 0); - lnk_error(LNK_Error_UnableToOpenTypeServer, "%S", error_msg); - } - - // output - external_ti_ranges = task.external_ti_ranges; - external_leaves = task.external_leaves; } } ProfEnd(); - // fill out result - LNK_CodeViewInput cv = {0}; - cv.count = obj_count; - cv.internal_count = internal_count; - cv.external_count = external_count; - cv.type_server_count = ts_path_arr.count; - cv.type_server_path_arr = ts_path_arr.v; - cv.ts_to_obj_arr = ts_to_obj_arr; - cv.obj_arr = sorted_obj_arr; - cv.pch_arr = pch_arr; - cv.debug_s_arr = sorted_debug_s_arr; - cv.debug_p_arr = sorted_debug_p_arr; - cv.debug_t_arr = sorted_debug_t_arr; - cv.merged_debug_t_p_arr = merged_debug_t_p_arr; - cv.total_symbol_input_count = total_symbol_input_count; - cv.symbol_inputs = symbol_inputs; - cv.parsed_symbols = parsed_symbols; - cv.internal_obj_arr = internal_obj_arr; - cv.external_obj_arr = external_obj_arr; - cv.internal_debug_s_arr = internal_debug_s_arr; - cv.external_debug_s_arr = external_debug_s_arr; - cv.internal_debug_t_arr = internal_debug_t_arr; - cv.external_debug_t_arr = external_debug_t_arr; - cv.internal_debug_p_arr = internal_debug_p_arr; - cv.external_debug_p_arr = external_debug_p_arr; - cv.internal_total_symbol_input_count = internal_total_symbol_input_count; - cv.internal_symbol_inputs = internal_symbol_inputs; - cv.internal_parsed_symbols = internal_parsed_symbols; - cv.external_total_symbol_input_count = external_total_symbol_input_count; - cv.external_symbol_inputs = external_symbol_inputs; - cv.external_parsed_symbols = external_parsed_symbols; - cv.external_ti_ranges = external_ti_ranges; - cv.external_leaves = external_leaves; - cv.external_obj_to_ts_idx_arr = obj_to_ts_idx_arr; - cv.external_obj_range = rng_1u64(internal_count, internal_count + external_count); - + // PCH and /Z7 objs have default min type index set to CV_MinComplexTypeIndex + // but type servers can bump up the lower bound. In practice nobody does this. + // However, to cover all our bases loop through type servers and compute + // max lower bound. + for EachIndex(ti_source, CV_TypeIndexSource_COUNT) { input.min_type_indices[ti_source] = CV_MinComplexTypeIndex; } + for EachInRange(ts_idx, input.ts_obj_range) { + CV_DebugT *debug_t = &input.debug_t_arr[ts_idx]; + for EachIndex(ti_source, CV_TypeIndexSource_COUNT) { + input.min_type_indices[ti_source] = Max(input.min_type_indices[ti_source], debug_t->ti_ranges[ti_source].min); + } + } + + // :zero_out_symbol_sub_section + ProfBegin("Zero-out Symbols Sub-sections"); + for EachIndex(i, obj_count) { MemoryZeroStruct(cv_sub_section_ptr_from_debug_s(&input.debug_s_arr[i], CV_C13SubSectionKind_Symbols)); } + ProfEnd(); + scratch_end(scratch); ProfEnd(); - return cv; + return input; } internal LNK_LeafRef -lnk_leaf_ref(U32 enc_loc_idx, U32 enc_leaf_idx) +lnk_leaf_ref_from_ti(LNK_CodeViewInput *input, U32 obj_idx, CV_TypeIndexSource source, CV_TypeIndex ti) { - LNK_LeafRef ref; - ref.enc_loc_idx = enc_loc_idx; - ref.enc_leaf_idx = enc_leaf_idx; - return ref; -} - -internal LNK_LeafRef -lnk_obj_leaf_ref(U32 obj_idx, U32 leaf_idx) -{ - return lnk_leaf_ref(obj_idx, leaf_idx); -} - -internal LNK_LeafRef -lnk_ts_leaf_ref(CV_TypeIndexSource ti_source, U32 ts_idx, U32 leaf_idx) -{ - ts_idx |= LNK_LeafRefFlag_LocIdxExternal; - - if (ti_source == CV_TypeIndexSource_IPI) { - leaf_idx |= LNK_LeafRefFlag_LeafIdxIPI; - } - - return lnk_leaf_ref(ts_idx, leaf_idx); -} - -internal int -lnk_leaf_ref_is_before(void *raw_a, void *raw_b) -{ - LNK_LeafRef **a = raw_a; - LNK_LeafRef **b = raw_b; - int is_before; - if ((*a)->enc_loc_idx == (*b)->enc_loc_idx) { - is_before = (*a)->enc_leaf_idx < (*b)->enc_leaf_idx; + LNK_LeafRef leaf_ref; + CV_DebugT *debug_t = &input->debug_t_arr[obj_idx]; + if (contains_1u64(debug_t->pch_ti_range[source], ti)) { + leaf_ref = (LNK_LeafRef){ + .obj_idx = debug_t->pch_obj_idx, + .leaf_idx = cv_leaf_idx_from_ti(&input->debug_t_arr[debug_t->pch_obj_idx], source, ti) + }; } else { - is_before = (*a)->enc_loc_idx < (*b)->enc_loc_idx; + U64 ts_idx = input->obj_to_ts[obj_idx]; + if (ts_idx != 0) { + U64 ts_obj_idx = input->ts_obj_range.min + ts_idx; + CV_DebugT *ts_debug_t = &input->debug_t_arr[ts_obj_idx]; + leaf_ref = (LNK_LeafRef){ + .obj_idx = ts_obj_idx, + .leaf_idx = cv_leaf_idx_from_ti(ts_debug_t, source, ti) + }; + } else { + leaf_ref = (LNK_LeafRef){ + .obj_idx = obj_idx, + .leaf_idx = cv_leaf_idx_from_ti(debug_t, source, ti) + }; + } } - return is_before; -} - -internal LNK_LeafLocType -lnk_loc_type_from_leaf_ref(LNK_LeafRef leaf_ref) -{ - if (leaf_ref.enc_loc_idx & LNK_LeafRefFlag_LocIdxExternal) { - return LNK_LeafLocType_External; - } - return LNK_LeafLocType_Internal; + return leaf_ref; } internal int lnk_leaf_ref_compare(LNK_LeafRef a, LNK_LeafRef b) { int cmp = 0; - if (a.enc_loc_idx < b.enc_loc_idx) { + if (a.obj_idx < b.obj_idx) { cmp = -1; - } else if (a.enc_loc_idx > b.enc_loc_idx) { + } else if (a.obj_idx > b.obj_idx) { cmp = +1; } else { - if (a.enc_leaf_idx < b.enc_leaf_idx) { + if (a.leaf_idx < b.leaf_idx) { cmp = -1; - } else if (a.enc_leaf_idx > b.enc_leaf_idx) { + } else if (a.leaf_idx > b.leaf_idx) { cmp = +1; } } return cmp; } -internal LNK_LeafLocType -lnk_loc_type_from_obj_idx(LNK_CodeViewInput *input, U64 obj_idx) +internal int +lnk_leaf_ref_is_before(void *raw_a, void *raw_b) { - if (input->external_obj_range.min <= obj_idx && obj_idx < input->external_obj_range.max) { - return LNK_LeafLocType_External; - } - return LNK_LeafLocType_Internal; -} - -internal U64 -lnk_loc_idx_from_obj_idx(LNK_CodeViewInput *input, U64 obj_idx) -{ - if (input->external_obj_range.min <= obj_idx && obj_idx < input->external_obj_range.max) { - return input->external_obj_to_ts_idx_arr[obj_idx - input->external_obj_range.min]; - } - return obj_idx; -} - -internal CV_TypeIndex -lnk_ti_lo_from_leaf_ref(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref) -{ - CV_TypeIndex ti_lo; - - LNK_LeafLocType loc_type = lnk_loc_type_from_leaf_ref(leaf_ref); - switch (loc_type) { - case LNK_LeafLocType_Internal: { - ti_lo = CV_MinComplexTypeIndex; - } break; - case LNK_LeafLocType_External: { - U64 ts_idx = leaf_ref.enc_loc_idx & ~LNK_LeafRefFlag_LocIdxExternal; - CV_TypeIndexSource ti_source = (leaf_ref.enc_loc_idx & LNK_LeafRefFlag_LeafIdxIPI) ? CV_TypeIndexSource_IPI : CV_TypeIndexSource_TPI; - ti_lo = input->external_ti_ranges[ts_idx][ti_source].min; - } break; - default: ti_lo = 0; break; - } - - return ti_lo; -} - -internal CV_TypeIndex -lnk_ti_lo_from_loc(LNK_CodeViewInput *input, LNK_LeafLocType loc_type, U64 loc_idx, CV_TypeIndexSource ti_source) -{ - CV_TypeIndex ti_lo = 0; - if (loc_type == LNK_LeafLocType_Internal) { - ti_lo = CV_MinComplexTypeIndex; - } else if (loc_type == LNK_LeafLocType_External) { - ti_lo = input->external_ti_ranges[loc_idx][ti_source].min; - } - return ti_lo; -} - -internal String8 -lnk_data_from_leaf_ref(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref) -{ - String8 data; - - LNK_LeafLocType loc_type = lnk_loc_type_from_leaf_ref(leaf_ref); - switch (loc_type) { - case LNK_LeafLocType_Internal: { - U32 obj_idx = leaf_ref.enc_loc_idx & ~LNK_LeafRefFlag_LocIdxExternal; - U32 leaf_idx = leaf_ref.enc_leaf_idx; - CV_DebugT debug_t = input->merged_debug_t_p_arr[obj_idx]; - data = cv_debug_t_get_raw_leaf(debug_t, leaf_idx); - } break; - - case LNK_LeafLocType_External: { - U64 ts_idx = leaf_ref.enc_loc_idx & ~LNK_LeafRefFlag_LocIdxExternal; - U64 leaf_idx = leaf_ref.enc_leaf_idx & ~LNK_LeafRefFlag_LeafIdxIPI; - CV_TypeIndexSource ti_source = leaf_ref.enc_leaf_idx & LNK_LeafRefFlag_LeafIdxIPI ? CV_TypeIndexSource_IPI : CV_TypeIndexSource_TPI; - CV_DebugT debug_t = input->external_leaves[ts_idx][ti_source]; - data = cv_debug_t_get_raw_leaf(debug_t, leaf_idx); - } break; - - default: data = str8(0,0); break; - } - - return data; -} - -internal CV_TypeIndex -lnk_type_index_from_leaf_ref(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref) -{ - CV_TypeIndex type_index = 0; - LNK_LeafLocType loc_type = lnk_loc_type_from_leaf_ref(leaf_ref); - switch (loc_type) { - case LNK_LeafLocType_Internal: { - LNK_PchInfo pch_info = input->pch_arr[leaf_ref.enc_loc_idx]; - type_index = pch_info.ti_hi + leaf_ref.enc_leaf_idx; - } break; - case LNK_LeafLocType_External: { - CV_TypeIndex lo = lnk_ti_lo_from_leaf_ref(input, leaf_ref); - type_index = lo + leaf_ref.enc_leaf_idx & ~LNK_LeafRefFlag_LeafIdxIPI; - } break; - default: InvalidPath; - } - return type_index; -} - -internal U64 -lnk_hash_from_leaf_ref(LNK_LeafHashes *hashes, LNK_LeafRef leaf_ref) -{ - LNK_LeafLocType loc_type; - CV_TypeIndexSource ti_source; - if (leaf_ref.enc_loc_idx & LNK_LeafRefFlag_LocIdxExternal) { - loc_type = LNK_LeafLocType_External; - ti_source = (leaf_ref.enc_leaf_idx & LNK_LeafRefFlag_LeafIdxIPI) ? CV_TypeIndexSource_IPI : CV_TypeIndexSource_TPI; - } else { - loc_type = LNK_LeafLocType_Internal; - ti_source = CV_TypeIndexSource_TPI; - } - - U32 loc_idx = leaf_ref.enc_loc_idx & ~LNK_LeafRefFlag_LocIdxExternal; - U32 leaf_idx = leaf_ref.enc_leaf_idx & ~LNK_LeafRefFlag_LeafIdxIPI; - U64 hash = hashes->v[loc_type][loc_idx][ti_source][leaf_idx]; - - return hash; -} - -internal LNK_LeafRef -lnk_leaf_ref_from_loc_idx_and_ti(LNK_CodeViewInput *input, - LNK_LeafLocType loc_type, - CV_TypeIndexSource ti_source, - U64 loc_idx, - CV_TypeIndex obj_ti) -{ - LNK_LeafRef leaf_ref; - - switch (loc_type) { - case LNK_LeafLocType_External: { - U64 ts_idx = loc_idx; - - CV_TypeIndex ti_lo = input->external_ti_ranges[ts_idx][ti_source].min; - Assert(obj_ti >= ti_lo); - - // encode leaf index for type server - leaf_ref = lnk_ts_leaf_ref(ti_source, ts_idx, obj_ti - ti_lo); - } break; - - case LNK_LeafLocType_Internal: { - U64 obj_idx = loc_idx; - - LNK_PchInfo pch = input->pch_arr[obj_idx]; - if (obj_ti < pch.ti_lo) { - CV_TypeIndex ti_lo = CV_MinComplexTypeIndex; - Assert(obj_ti >= ti_lo); - leaf_ref = lnk_obj_leaf_ref(obj_idx, obj_ti - ti_lo); - } - // PCH indirection - else if (obj_ti < pch.ti_hi) { - // we don't support nested precompiled types - Assert(input->pch_arr[pch.debug_p_obj_idx].debug_p_obj_idx == /* null_obj: */ 0); - Assert(input->pch_arr[pch.debug_p_obj_idx].ti_lo == input->pch_arr[pch.debug_p_obj_idx].ti_hi); - leaf_ref = lnk_obj_leaf_ref(pch.debug_p_obj_idx, obj_ti - pch.ti_lo); - } else { - leaf_ref = lnk_obj_leaf_ref(obj_idx, pch.ti_lo + (obj_ti - pch.ti_hi) - CV_MinComplexTypeIndex); - } - } break; - - default: leaf_ref = lnk_leaf_ref(0, 0); break; - } - - return leaf_ref; + return lnk_leaf_ref_compare(**(LNK_LeafRef **)raw_a, **(LNK_LeafRef **)raw_b) < 0; } internal B32 -lnk_match_leaf_ref(LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafRef a, LNK_LeafRef b) +lnk_match_leaf_ref(LNK_CodeViewInput *input, LNK_LeafRef a, LNK_LeafRef b) { - B32 are_same = 0; - - U64 a_hash = lnk_hash_from_leaf_ref(hashes, a); - U64 b_hash = lnk_hash_from_leaf_ref(hashes, b); - + B32 is_match = 0; + CV_DebugT *a_debug_t = &input->debug_t_arr[a.obj_idx]; + CV_DebugT *b_debug_t = &input->debug_t_arr[b.obj_idx]; + U64 a_hash = input->debug_h_arr[a.obj_idx].v[a.leaf_idx]; + U64 b_hash = input->debug_h_arr[b.obj_idx].v[b.leaf_idx]; if (a_hash == b_hash) { - CV_Leaf a_leaf; - cv_read_leaf(lnk_data_from_leaf_ref(input, a), 0, 1, &a_leaf); - - CV_Leaf b_leaf; - cv_read_leaf(lnk_data_from_leaf_ref(input, b), 0, 1, &b_leaf); - + CV_Leaf a_leaf = cv_debug_t_get_leaf(a_debug_t, a.leaf_idx); + CV_Leaf b_leaf = cv_debug_t_get_leaf(b_debug_t, b.leaf_idx); Assert(a_leaf.kind == b_leaf.kind); - -#if 0 - { - Temp scratch = scratch_begin(0,0); - CV_TypeIndexInfoList ti_info_list = cv_get_leaf_type_index_offsets(scratch.arena, a_leaf.kind, a_leaf.data); - String8Array a_raw_data_arr = cv_get_data_around_type_indices(scratch.arena, ti_info_list, a_leaf.data); - String8Array b_raw_data_arr = cv_get_data_around_type_indices(scratch.arena, ti_info_list, b_leaf.data); - for (U64 i = 0; i < a_raw_data_arr.count; ++i) { - String8 a_chunk = a_raw_data_arr.v[i]; - String8 b_chunk = b_raw_data_arr.v[i]; - Assert(str8_match(a_chunk, b_chunk, 0)); - } - scratch_end(scratch); - } -#endif - - are_same = 1; + is_match = 1; } - - return are_same; + return is_match; } -#if 0 -internal B32 -lnk_match_leaf_ref_deep(Arena *arena, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafRef a, LNK_LeafRef b) -{ - B32 are_equal = 0; - - U64 a_hash = lnk_hash_from_leaf_ref(hashes, a); - U64 b_hash = lnk_hash_from_leaf_ref(hashes, b); - - if (a_hash == b_hash) { - String8 a_raw_leaf = lnk_data_from_leaf_ref(input, a); - String8 b_raw_leaf = lnk_data_from_leaf_ref(input, b); - - CV_LeafHeader *a_header = (CV_LeafHeader *) a_raw_leaf.str; - CV_LeafHeader *b_header = (CV_LeafHeader *) b_raw_leaf.str; - - if (a_header->kind == b_header->kind && a_header->size == b_header->size) { - CV_Leaf a_leaf = cv_leaf_from_string(a_raw_leaf); - CV_Leaf b_leaf = cv_leaf_from_string(b_raw_leaf); - - Temp temp = temp_begin(arena); - - CV_TypeIndexInfoList ti_info_list = cv_get_leaf_type_index_offsets(temp.arena, a_leaf.kind, a_leaf.data); - String8Array a_raw_data_arr = cv_get_data_around_type_indices(temp.arena, ti_info_list, a_leaf.data); - String8Array b_raw_data_arr = cv_get_data_around_type_indices(temp.arena, ti_info_list, b_leaf.data); - - are_equal = 1; - - for (U64 i = 0; i < a_raw_data_arr.count; ++i) { - String8 a_chunk = a_raw_data_arr.v[i]; - String8 b_chunk = b_raw_data_arr.v[i]; - Assert(a_chunk.size == b_chunk.size); - are_equal = str8_match(a_chunk, b_chunk, 0); - if (!are_equal) { - goto skip_type_index_compare; - } - } - - CV_TypeIndex a_ti_lo = lnk_ti_lo_from_leaf_ref(input, a); - CV_TypeIndex b_ti_lo = lnk_ti_lo_from_leaf_ref(input, b); - AssertAlways(a_ti_lo == b_ti_lo); - - for (CV_TypeIndexInfo *ti_info = ti_info_list.first; ti_info != 0; ti_info = ti_info->next) { - CV_TypeIndex *a_ti_ptr = (CV_TypeIndex *) (a_leaf.data.str + ti_info->offset); - CV_TypeIndex *b_ti_ptr = (CV_TypeIndex *)(b_leaf.data.str + ti_info->offset); - - if (*a_ti_ptr >= a_ti_lo && *b_ti_ptr >= b_ti_lo) { - LNK_LeafLocType a_loc_type = (a.enc_loc_idx & LNK_LeafRefFlag_LocIdxExternal) >> 31; - LNK_LeafLocType b_loc_type = (b.enc_loc_idx & LNK_LeafRefFlag_LocIdxExternal) >> 31; - - U64 a_loc_idx = a.enc_loc_idx & ~LNK_LeafRefFlag_LocIdxExternal; - U64 b_loc_idx = b.enc_loc_idx & ~LNK_LeafRefFlag_LocIdxExternal; - - LNK_LeafRef a_sub_leaf_ref = lnk_leaf_ref_from_loc_idx_and_ti(input, a_loc_type, ti_info->source, a_loc_idx, *a_ti_ptr); - LNK_LeafRef b_sub_leaf_ref = lnk_leaf_ref_from_loc_idx_and_ti(input, b_loc_type, ti_info->source, b_loc_idx, *b_ti_ptr); - - are_equal = lnk_match_leaf_ref_deep(arena, input, hashes, a_sub_leaf_ref, b_sub_leaf_ref); - if (!are_equal) { - break; - } - } - // compare simple leaves - else { - are_equal = *a_ti_ptr == *b_ti_ptr; - if (!are_equal) { - break; - } - } - } - -skip_type_index_compare:; - temp_end(temp); - } - } - - return are_equal; -} -#endif - internal U64 -lnk_hash_cv_leaf(LNK_CodeViewInput *input, - LNK_LeafHashes *hashes, - LNK_LeafLocType loc_type, - U32 loc_idx, - Rng1U64 *ti_ranges, - CV_TypeIndex curr_ti, - CV_Leaf leaf, - CV_TypeIndexInfoList ti_info_list) +lnk_hash_cv_leaf(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref, CV_TypeIndexInfoList ti_info_list, B32 discard_cycles) { + CV_DebugT *debug_t = &input->debug_t_arr[leaf_ref.obj_idx]; + CV_Leaf leaf = cv_debug_t_get_leaf(debug_t, leaf_ref.leaf_idx); + CV_TypeIndexSource curr_ti_source = cv_type_index_source_from_leaf_kind(leaf.kind); + CV_TypeIndex curr_ti = cv_ti_from_leaf_idx(debug_t, curr_ti_source, leaf_ref.leaf_idx); + // init hasher blake3_hasher hasher; blake3_hasher_init(&hasher); @@ -1181,154 +680,130 @@ lnk_hash_cv_leaf(LNK_CodeViewInput *input, } // mix-in sub leaf hashes - for EachNode(ti_n, CV_TypeIndexInfo, ti_info_list.first) { - CV_TypeIndex sub_ti = memory_read32(leaf.data.str + ti_n->offset); - - // is type index complex? - if (sub_ti >= ti_ranges[ti_n->source].min) { - // Mostly leaves are laid out as DAG and we can get to sub leaf hash through index lookup, - // however MASM doesn't follow DAG rule, for example: - // - // Engine\Source\Developer\Windows\LiveCoding\Private\External\LC_JumpToSelf.asm - // .debug$T (No. 4): - // LF_PROCEDURE (0x1000) [0008-0014] - // Return type: 3 - // Call Convention: Near C - // Function Attribs: NULL - // Argumnet Count: 0 - // Argument List Type: 1001 - // LF_ARGLIST (0x1001) [0018-001C] - // Types 0 - // LF_LABEL (0x1002) [0020-0024] - // $UNDEFINED: E - // - // Note: LF_ARGLIST(0x1001) > LF_PROCEDURE(0x1000) - // - // Luckily we don't have many leaves that break DAG rule and we can skip without - // much memory and perf penalty (In Ancient Game we skip 7 leaves) - if (sub_ti < curr_ti) { - LNK_LeafRef sub_leaf_ref = lnk_leaf_ref_from_loc_idx_and_ti(input, loc_type, ti_n->source, loc_idx, sub_ti); - - // query sub hash - U64 sub_hash = lnk_hash_from_leaf_ref(hashes, sub_leaf_ref); - - // make sure sub hash was computed (:zero_hash_array) - Assert(sub_hash != 0); - - // mix-in sub hash - blake3_hasher_update(&hasher, &sub_hash, sizeof(sub_hash)); - } else { - Temp scratch = scratch_begin(0,0); - String8 leaf_kind_str = cv_string_from_leaf_kind(leaf.kind); - String8 leaf_info = push_str8f(scratch.arena, "LF_%S(type_index: 0x%x) forward refs member type index 0x%x (leaf struct offset: 0x%llx)", leaf_kind_str, curr_ti, sub_ti, ti_n->offset); - if (loc_type == LNK_LeafLocType_Internal) { - lnk_error_obj(LNK_Error_InvalidTypeIndex, input->internal_obj_arr[loc_idx], "%S", leaf_info); - } else if (loc_type == LNK_LeafLocType_External) { - lnk_error(LNK_Error_InvalidTypeIndex, "%S: %S", input->type_server_path_arr[loc_idx], leaf_info); - } else { - InvalidPath; - } - scratch_end(scratch); - } - } + for EachNode(sub_ti_n, CV_TypeIndexInfo, ti_info_list.first) { + CV_TypeIndex *sub_ti_ptr = str8_deserial_get_raw_ptr(leaf.data, sub_ti_n->offset, sizeof(*sub_ti_ptr)); + CV_TypeIndex sub_ti = memory_read32(sub_ti_ptr); + // simple indices are stable across compile units - else { + if (sub_ti < debug_t->ti_ranges[sub_ti_n->source].min) { blake3_hasher_update(&hasher, &sub_ti, sizeof(sub_ti)); + continue; } + + if (sub_ti >= debug_t->ti_ranges[sub_ti_n->source].max) { + Temp scratch = scratch_begin(0,0); + + String8 out_of_bounds_data = push_str8f(scratch.arena, "out_of_bounds_ti_%u_in_%u", sub_ti, leaf_ref.obj_idx); + blake3_hasher_update(&hasher, out_of_bounds_data.str, out_of_bounds_data.size); + + String8 leaf_kind_str = cv_string_from_leaf_kind(leaf.kind); + String8 error_msg = push_str8f(scratch.arena, "LF_%S(type_index: 0x%x) out of bounds type index 0x%x (leaf struct offset: 0x%llx)", leaf_kind_str, curr_ti, sub_ti, sub_ti_n->offset); + lnk_error_obj(LNK_Error_InvalidTypeIndex, input->obj_arr[leaf_ref.obj_idx], "%S", error_msg); + + scratch_end(scratch); + continue; + } + + // discard type with a cyclic-ref + B32 is_type_graph_cyclic = discard_cycles && sub_ti > 0 && sub_ti > curr_ti; + if (is_type_graph_cyclic) { + // discard type + U32 leaf_idx = curr_ti - debug_t->ti_ranges[curr_ti_source].min; + U8 *leaf_header = debug_t->data.str + debug_t->offsets[leaf_idx]; + memory_write16(leaf_header + OffsetOf(CV_LeafHeader, kind), CV_LeafKind_NOTYPE); + memory_write16(leaf_header + OffsetOf(CV_LeafHeader, size), 0); + + // log error + Temp scratch = scratch_begin(0,0); + String8 leaf_kind_str = cv_string_from_leaf_kind(leaf.kind); + String8 error_msg = push_str8f(scratch.arena, "LF_%S(type_index: 0x%x) forward refs member type index 0x%x (leaf struct offset: 0x%llx)", leaf_kind_str, curr_ti, sub_ti, sub_ti_n->offset); + lnk_error_obj(LNK_Error_InvalidTypeIndex, input->obj_arr[leaf_ref.obj_idx], "%S", error_msg); + scratch_end(scratch); + + continue; + } + + // type index -> hash + LNK_LeafRef sub_ref = lnk_leaf_ref_from_ti(input, leaf_ref.obj_idx, sub_ti_n->source, sub_ti); + U64 sub_hash = input->debug_h_arr[sub_ref.obj_idx].v[sub_ref.leaf_idx]; + + // mix-in sub-type hash + blake3_hasher_update(&hasher, &sub_hash, sizeof(sub_hash)); } U64 hash; blake3_hasher_finalize(&hasher, (U8 *) &hash, sizeof(hash)); + Assert(hash != 0); + Assert(input->debug_h_arr[leaf_ref.obj_idx].v[leaf_ref.leaf_idx] == 0 || + input->debug_h_arr[leaf_ref.obj_idx].v[leaf_ref.leaf_idx] == 1); + input->debug_h_arr[leaf_ref.obj_idx].v[leaf_ref.leaf_idx] = hash; return hash; } internal void lnk_hash_cv_leaf_deep(Arena *arena, LNK_CodeViewInput *input, - Rng1U64 *ti_ranges, - CV_DebugT *leaves, - LNK_LeafHashes *hashes, - CV_TypeIndexSource ti_source, - CV_TypeIndex ti, - LNK_LeafLocType loc_type, - U32 loc_idx, - CV_TypeIndexInfoList ti_info_list, - CV_Leaf leaf) + LNK_LeafRef root_leaf_ref, + CV_TypeIndexInfoList root_ti_info_list) { Temp temp = temp_begin(arena); - struct stack_s { - struct stack_s *next; + typedef struct HashStack { + struct HashStack *next; + LNK_LeafRef leaf_ref; CV_TypeIndexInfoList ti_info_list; CV_TypeIndexInfo *ti_info; CV_Leaf leaf; CV_TypeIndex ti; CV_TypeIndexSource ti_source; - }; + } HashStack; // set up root frame - struct stack_s *root_frame = push_array(temp.arena, struct stack_s, 1); - root_frame->ti_info_list = ti_info_list; - root_frame->ti_info = ti_info_list.first; - root_frame->leaf = leaf; - root_frame->ti = ti; - root_frame->ti_source = ti_source; + CV_DebugT *root_debug_t = &input->debug_t_arr[root_leaf_ref.obj_idx]; + HashStack *root_frame = push_array(temp.arena, HashStack, 1); + root_frame->leaf_ref = root_leaf_ref; + root_frame->ti_info_list = root_ti_info_list; + root_frame->ti_info = root_ti_info_list.first; + root_frame->leaf = cv_debug_t_get_leaf(root_debug_t, root_leaf_ref.leaf_idx); + root_frame->ti_source = cv_type_index_source_from_leaf_kind(root_frame->leaf.kind); + root_frame->ti = cv_ti_from_leaf_idx(root_debug_t, root_frame->ti_source, root_leaf_ref.leaf_idx); - U64 **curr_hashes = hashes->v[loc_type][loc_idx]; - - struct stack_s *stack = root_frame; + HashStack *stack = root_frame; while (stack) { while (stack->ti_info) { - CV_TypeIndexInfo *curr_ti_info = stack->ti_info; + CV_TypeIndexInfo *ti_info = stack->ti_info; // advance iterator stack->ti_info = stack->ti_info->next; // get type index info - CV_TypeIndex *ti_ptr = (CV_TypeIndex *) (stack->leaf.data.str + curr_ti_info->offset); + CV_TypeIndex *ti_ptr = str8_deserial_get_raw_ptr(stack->leaf.data, ti_info->offset, sizeof(*ti_ptr)); CV_TypeIndex ti = memory_read32(ti_ptr); - // is index complex? - if (ti >= ti_ranges[curr_ti_info->source].min) { - // TODO: handle malformed index - AssertAlways(ti < ti_ranges[curr_ti_info->source].max); - U64 leaf_idx = (ti - ti_ranges[curr_ti_info->source].min); + // skip out of bounds indices + if ( ! contains_1u64(input->debug_t_arr[root_leaf_ref.obj_idx].ti_ranges[ti_info->source], ti)) { continue; } - // was leaf hashed? - if (curr_hashes[curr_ti_info->source][leaf_idx] == 0) { // :zero_hash_array - CV_Leaf leaf = cv_debug_t_get_leaf(leaves[curr_ti_info->source], leaf_idx); + // skip hashed types + LNK_LeafRef leaf_ref = lnk_leaf_ref_from_ti(input, root_leaf_ref.obj_idx, ti_info->source, ti); + if (input->debug_h_arr[leaf_ref.obj_idx].v[leaf_ref.leaf_idx] != 0) { continue; } + input->debug_h_arr[leaf_ref.obj_idx].v[leaf_ref.leaf_idx] = 1; - // find index offsets - CV_TypeIndexInfoList sub_ti_info_list = cv_get_leaf_type_index_offsets(temp.arena, leaf.kind, leaf.data); - - // do we have sub leaves? - if (sub_ti_info_list.count) { - // fill out new frame - struct stack_s *frame = push_array(temp.arena, struct stack_s, 1); - frame->ti_info_list = sub_ti_info_list; - frame->ti_info = sub_ti_info_list.first; - frame->leaf = leaf; - frame->ti = ti; - frame->ti_source = curr_ti_info->source; - - // recurse to sub leaf - SLLStackPush(stack, frame); - break; - } else { - curr_hashes[curr_ti_info->source][leaf_idx] = lnk_hash_cv_leaf(input, hashes, loc_type, loc_idx, ti_ranges, CV_TypeIndex_Max, leaf, sub_ti_info_list); - } - } - } + // recurse down to sub types + HashStack *frame = push_array(temp.arena, HashStack, 1); + frame->leaf_ref = leaf_ref; + frame->leaf = cv_debug_t_get_leaf(&input->debug_t_arr[leaf_ref.obj_idx], leaf_ref.leaf_idx); + frame->ti_info_list = cv_get_leaf_type_index_offsets(temp.arena, frame->leaf.kind, frame->leaf.data); + frame->ti_info = frame->ti_info_list.first; + frame->ti = ti; + frame->ti_source = ti_info->source; + SLLStackPush(stack, frame); + break; } // no more type indices, pop frame - if (!stack->ti_info) { - // sub leaves are hashed we can now hash parent leaf - U64 leaf_idx = stack->ti - ti_ranges[stack->ti_source].min; - curr_hashes[stack->ti_source][leaf_idx] = lnk_hash_cv_leaf(input, hashes, loc_type, loc_idx, ti_ranges, CV_TypeIndex_Max, stack->leaf, stack->ti_info_list); - Assert(curr_hashes[stack->ti_source][leaf_idx] != 0); - + if ( ! stack->ti_info) { + lnk_hash_cv_leaf(input, stack->leaf_ref, stack->ti_info_list, 0); SLLStackPop(stack); } } @@ -1337,78 +812,20 @@ lnk_hash_cv_leaf_deep(Arena *arena, } internal LNK_LeafRef * -lnk_leaf_hash_table_insert_or_update(LNK_LeafHashTable *leaf_ht, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, U64 new_hash, LNK_LeafRef *new_bucket) -{ - LNK_LeafRef *result = 0; - B32 is_inserted_or_updated = 0; - - U64 best_idx = new_hash % leaf_ht->cap; - U64 idx = best_idx; - - do { -retry:; - LNK_LeafRef *curr_bucket = leaf_ht->bucket_arr[idx]; - - if (curr_bucket == 0) { - LNK_LeafRef *compare_bucket = ins_atomic_ptr_eval_cond_assign(&leaf_ht->bucket_arr[idx], new_bucket, curr_bucket); - - if (compare_bucket == curr_bucket) { - // success, bucket was inserted - is_inserted_or_updated = 1; - break; - } - - // another thread took the bucket... - goto retry; - } else if (lnk_match_leaf_ref(input, hashes, *curr_bucket, *new_bucket)) { - int leaf_cmp = lnk_leaf_ref_compare(*curr_bucket, *new_bucket); - - if (leaf_cmp <= 0) { - // are we inserting bucket that was already inserterd? - Assert(leaf_cmp < 0); - - result = new_bucket; - - is_inserted_or_updated = 1; - - // don't need to update, more recent leaf is in the bucket - break; - } - - LNK_LeafRef *compare_bucket = ins_atomic_ptr_eval_cond_assign(&leaf_ht->bucket_arr[idx], new_bucket, curr_bucket); - if (compare_bucket == curr_bucket) { - result = compare_bucket; - - is_inserted_or_updated = 1; - break; - } - - // another thread took the bucket... - goto retry; - } - - // advance - idx = ((idx + 1) == leaf_ht->cap ? 0 : (idx + 1)); - } while (idx != best_idx); - - Assert(is_inserted_or_updated); - return result; -} - -internal LNK_LeafRef * -lnk_leaf_hash_table_search(LNK_LeafHashTable *ht, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafRef leaf_ref) +lnk_leaf_hash_table_search(LNK_LeafHashTable *ht, LNK_CodeViewInput *input, LNK_LeafRef leaf_ref) { LNK_LeafRef *match = 0; - U64 hash = lnk_hash_from_leaf_ref(hashes, leaf_ref); - U64 best_bucket_idx = hash % ht->cap; - U64 bucket_idx = best_bucket_idx; - + CV_DebugT *debug_t = &input->debug_t_arr[leaf_ref.obj_idx]; + CV_DebugH *debug_h = &input->debug_h_arr[leaf_ref.obj_idx]; + U64 hash = debug_h->v[leaf_ref.leaf_idx]; + U64 best_bucket_idx = hash % ht->cap; + U64 bucket_idx = best_bucket_idx; do { LNK_LeafRef *bucket = ht->bucket_arr[bucket_idx]; if (bucket == 0) { break; } - if (lnk_match_leaf_ref(input, hashes, *bucket, leaf_ref)) { + if (lnk_match_leaf_ref(input, *bucket, leaf_ref)) { match = bucket; break; } @@ -1419,70 +836,20 @@ lnk_leaf_hash_table_search(LNK_LeafHashTable *ht, LNK_CodeViewInput *input, LNK_ return match; } -internal -THREAD_POOL_TASK_FUNC(lnk_count_per_source_leaf_task) -{ - ProfBeginFunction(); - - LNK_CvImportTypes *task = raw_task; - - U64 counts[CV_TypeIndexSource_COUNT] = {0}; - for EachNode(leaf_range, LNK_LeafRange, task->leaf_ranges_per_task[task_id].first) { - CV_DebugT debug_t = *leaf_range->debug_t; - for EachInRange(leaf_idx, leaf_range->range) { - CV_LeafHeader *leaf_header = cv_debug_t_get_leaf_header(debug_t, leaf_idx); - CV_LeafKind kind = memory_read16(MemberFromPtr(CV_LeafHeader, leaf_header, kind)); - CV_TypeIndexSource leaf_source = cv_type_index_source_from_leaf_kind(kind); - counts[leaf_source] += 1; - } - } - - for EachElement(i, counts) { - ins_atomic_u64_add_eval(&task->internal_per_source_count[i], counts[i]); - } - - ProfEnd(); -} - internal THREAD_POOL_TASK_FUNC(lnk_hash_debug_t_task) { ProfBeginFunction(); - - U64 obj_idx = task_id; - LNK_CvImportTypes *task = raw_task; - - Arena *fixed_arena = task->fixed_arenas[worker_id]; - CV_DebugT debug_t = task->debug_t_arr[obj_idx]; - U64 *out_hashes = task->hashes->v[LNK_LeafLocType_Internal][obj_idx][CV_TypeIndexSource_TPI]; - - Rng1U64 ti_ranges[CV_TypeIndexSource_COUNT]; - for EachElement(ti_source, ti_ranges) { - ti_ranges[ti_source] = rng_1u64(task->input->pch_arr[obj_idx].ti_lo, task->input->pch_arr[obj_idx].ti_hi + debug_t.count); - } - - for EachIndex(leaf_idx, debug_t.count) { - Temp temp = temp_begin(fixed_arena); - - // :debug_zero_hash_assert make sure we don't write same hash more than once - //Assert(MemoryIsZeroStruct(&out_hash_arr.v[leaf_idx])); - - CV_TypeIndex curr_ti = lnk_type_index_from_leaf_ref(task->input, lnk_leaf_ref(obj_idx, leaf_idx)); - CV_Leaf leaf = cv_debug_t_get_leaf(debug_t, leaf_idx); - CV_TypeIndexInfoList ti_info_list = cv_get_leaf_type_index_offsets(temp.arena, leaf.kind, leaf.data); - - out_hashes[leaf_idx] = lnk_hash_cv_leaf(task->input, - task->hashes, - LNK_LeafLocType_Internal, - obj_idx, - ti_ranges, - curr_ti, - leaf, - ti_info_list); - + LNK_MergeTypes *task = raw_task; + U32 obj_idx = task->indices.v[task_id]; + CV_DebugT *debug_t = &task->input->debug_t_arr[obj_idx]; + for EachIndex(leaf_idx, debug_t->count) { + Temp temp = temp_begin(task->fixed_arenas[worker_id]); + CV_Leaf leaf = cv_debug_t_get_leaf(debug_t, leaf_idx); + CV_TypeIndexInfoList ti_list = cv_get_leaf_type_index_offsets(temp.arena, leaf.kind, leaf.data); + lnk_hash_cv_leaf(task->input, (LNK_LeafRef){ obj_idx, leaf_idx }, ti_list, 1); temp_end(temp); } - ProfEnd(); } @@ -1490,92 +857,89 @@ internal THREAD_POOL_TASK_FUNC(lnk_hash_debug_t_deep_task) { ProfBeginFunction(); - - LNK_CvImportTypes *task = raw_task; - U64 ts_idx = task_id; - CV_TypeIndexSource ti_source = task->ti_source; - CV_DebugT debug_t = task->input->external_leaves [ts_idx][ti_source]; - Rng1U64 *ti_ranges = task->input->external_ti_ranges[ts_idx]; - CV_DebugT *leaves = task->input->external_leaves [ts_idx]; - - for EachIndex(leaf_idx, debug_t.count) { - U64 hash = task->hashes->v[LNK_LeafLocType_External][ts_idx] [ti_source][leaf_idx]; - if (hash != 0) { continue; } - Temp temp = temp_begin(task->fixed_arenas[worker_id]); - CV_Leaf leaf = cv_debug_t_get_leaf(debug_t, leaf_idx); - CV_TypeIndexInfoList ti_info_list = cv_get_leaf_type_index_offsets(temp.arena, leaf.kind, leaf.data); - CV_TypeIndex ti = ti_ranges[ti_source].min + leaf_idx; - lnk_hash_cv_leaf_deep(temp.arena, task->input, ti_ranges, leaves, task->hashes, ti_source, ti, LNK_LeafLocType_External, ts_idx, ti_info_list, leaf); + LNK_MergeTypes *task = raw_task; + U64 obj_idx = task->indices.v[task_id]; + CV_DebugT *debug_t = &task->input->debug_t_arr[obj_idx]; + for EachIndex(leaf_idx, debug_t->count) { + if (task->input->debug_h_arr[obj_idx].v[leaf_idx] != 0) { continue; } + Temp temp = temp_begin(task->fixed_arenas[worker_id]); + CV_Leaf leaf = cv_debug_t_get_leaf(debug_t, leaf_idx); + CV_TypeIndexInfoList ti_list = cv_get_leaf_type_index_offsets(temp.arena, leaf.kind, leaf.data); + lnk_hash_cv_leaf_deep(temp.arena, task->input, (LNK_LeafRef){ obj_idx, leaf_idx }, ti_list); temp_end(temp); } - ProfEnd(); } internal -THREAD_POOL_TASK_FUNC(lnk_leaf_dedup_internal_task) +THREAD_POOL_TASK_FUNC(lnk_leaf_dedup_task) { - LNK_CvImportTypes *task = raw_task; - U64 obj_idx = task_id; - CV_DebugT debug_t = task->debug_t_arr[obj_idx]; - - ProfBeginDynamic("Leaf Dedup Task 0x%X [Leaf Count %u]", obj_idx, task->debug_t_arr[obj_idx].count); - - LNK_LeafRef *bucket = 0; - for EachIndex(leaf_idx, debug_t.count) { - CV_LeafHeader *leaf_header = cv_debug_t_get_leaf_header(debug_t, leaf_idx); - CV_LeafKind leaf_kind = memory_read16(MemberFromPtr(CV_LeafHeader, leaf_header, kind)); - CV_TypeIndexSource ti_source = cv_type_index_source_from_leaf_kind(leaf_kind); - LNK_LeafHashTable *leaf_ht = &task->leaf_ht_arr[ti_source]; - - LNK_LeafRef leaf_ref = lnk_obj_leaf_ref(obj_idx, leaf_idx); - U64 leaf_hash = lnk_hash_from_leaf_ref(task->hashes, leaf_ref); - - if (bucket == 0) { - bucket = push_array_no_zero(arena, LNK_LeafRef, 1); - } - *bucket = leaf_ref; - - LNK_LeafRef *inserted_or_updated = lnk_leaf_hash_table_insert_or_update(leaf_ht, task->input, task->hashes, leaf_hash, bucket); - - if (inserted_or_updated != bucket) { - bucket = 0; - } - } - - ProfEnd(); -} - -internal -THREAD_POOL_TASK_FUNC(lnk_leaf_dedup_external_task) -{ - ProfBeginFunction(); - - LNK_CvImportTypes *task = raw_task; - U64 ts_idx = task_id; - - LNK_CodeViewInput *input = task->input; - LNK_LeafHashTable *leaf_ht = &task->leaf_ht_arr[task->ti_source]; - U64 *hashes = task->hashes->external_hashes[ts_idx][task->ti_source]; - U64 leaf_count = dim_1u64(input->external_ti_ranges[ts_idx][task->ti_source]); + LNK_MergeTypes *task = raw_task; + U64 obj_idx = task->indices.v[task_id]; + CV_DebugT *debug_t = &task->input->debug_t_arr[obj_idx]; + CV_DebugH *debug_h = &task->input->debug_h_arr[obj_idx]; + ProfBeginDynamic("dedup in obj 0x%x (%S) leaf count %u", obj_idx, task->input->obj_arr[obj_idx]->path, debug_t->count); LNK_LeafRef *bucket = 0; + for EachIndex(leaf_idx, debug_t->count) { + // read leaf header + CV_LeafHeader *header = cv_debug_t_get_leaf_header(debug_t, leaf_idx); - for EachIndex(leaf_idx, leaf_count) { - if (hashes[leaf_idx] != 0) { // :zero_hash_check - LNK_LeafRef leaf_ref = lnk_ts_leaf_ref(task->ti_source, ts_idx, leaf_idx); - U64 leaf_hash = lnk_hash_from_leaf_ref(task->hashes, leaf_ref); + // read leaf kind + CV_LeafKind kind = memory_read16(MemberFromPtr(CV_LeafHeader, header, kind)); - if (bucket == 0) { - bucket = push_array_no_zero(arena, LNK_LeafRef, 1); - } - *bucket = leaf_ref; + // alloc new bucket and assign type ref + if (bucket == 0) { bucket = push_array_no_zero(arena, LNK_LeafRef, 1); } + *bucket = (LNK_LeafRef){ .obj_idx = obj_idx, .leaf_idx = leaf_idx }; - LNK_LeafRef *inserted_or_updated = lnk_leaf_hash_table_insert_or_update(leaf_ht, task->input, task->hashes, leaf_hash, bucket); + CV_TypeIndexSource leaf_source = cv_type_index_source_from_leaf_kind(kind); + LNK_LeafHashTable *leaf_ht = &task->leaf_ht_arr[leaf_source]; - if (inserted_or_updated != bucket) { - bucket = 0; - } + { + B32 is_inserted_or_updated = 0; + U64 best_idx = debug_h->v[leaf_idx] % leaf_ht->cap; + U64 idx = best_idx; + U64 loop_count = 0; + do { + retry:; + LNK_LeafRef *curr_bucket = leaf_ht->bucket_arr[idx]; + if (curr_bucket == 0) { + LNK_LeafRef *compare_bucket = ins_atomic_ptr_eval_cond_assign(&leaf_ht->bucket_arr[idx], bucket, curr_bucket); + if (compare_bucket == curr_bucket) { + // success, bucket was inserted + bucket = 0; + is_inserted_or_updated = 1; + break; + } + // another thread took the bucket... + goto retry; + } else if (lnk_match_leaf_ref(task->input, *curr_bucket, *bucket)) { + int leaf_cmp = lnk_leaf_ref_compare(*curr_bucket, *bucket); + + if (leaf_cmp <= 0) { + // are we inserting bucket that was already inserterd? + Assert(leaf_cmp < 0); + is_inserted_or_updated = 1; + // don't need to update, more recent leaf is in the bucket + break; + } + + LNK_LeafRef *compare_bucket = ins_atomic_ptr_eval_cond_assign(&leaf_ht->bucket_arr[idx], bucket, curr_bucket); + if (compare_bucket == curr_bucket) { + // reuse replaced bucket + bucket = compare_bucket; + is_inserted_or_updated = 1; + break; + } + + // another thread took the bucket... + goto retry; + } + + // advance + idx = ((idx + 1) == leaf_ht->cap ? 0 : (idx + 1)); + } while (idx != best_idx); + Assert(is_inserted_or_updated); } } @@ -1587,7 +951,7 @@ THREAD_POOL_TASK_FUNC(lnk_count_present_buckets_task) { ProfBeginFunction(); - LNK_CvImportTypes *task = raw_task; + LNK_MergeTypes *task = raw_task; LNK_LeafHashTable *ht = &task->leaf_ht_arr[task->ti_source]; for EachInRange(bucket_idx, task->ranges[task_id]) { @@ -1604,7 +968,7 @@ THREAD_POOL_TASK_FUNC(lnk_get_present_buckets_task) { ProfBeginFunction(); - LNK_CvImportTypes *task = raw_task; + LNK_MergeTypes *task = raw_task; U64 cursor = task->offsets[task->ti_source][task_id]; LNK_LeafHashTable *ht = &task->leaf_ht_arr[task->ti_source]; @@ -1624,62 +988,58 @@ THREAD_POOL_TASK_FUNC(lnk_leaf_ref_histo_task) { ProfBeginFunction(); - LNK_LeafRadixSortTask *task = raw_task; - Rng1U64 range = task->ranges[task_id]; - U32 *counts_ptr = task->counts_arr[task_id]; + LNK_MergeTypes *task = raw_task; + Rng1U64 range = task->ranges[task_id]; + U32 *counts_ptr = task->counts_arr[task_id]; - U32 loc_idx_bit_count_0 = task->loc_idx_bit_count_0; - U32 loc_idx_bit_count_1 = task->loc_idx_bit_count_1; - U32 loc_idx_bit_count_2 = task->loc_idx_bit_count_2; + U32 obj_idx_bit_count_0 = task->obj_idx_bit_count_0; + U32 obj_idx_bit_count_1 = task->obj_idx_bit_count_1; + U32 obj_idx_bit_count_2 = task->obj_idx_bit_count_2; MemoryZeroTyped(task->counts_arr[task_id], task->counts_max); switch (task->pass_idx) { case 0: { - for (U64 i = range.min; i < range.max; ++i) { + for EachInRange(i, range) { LNK_LeafRef *bucket = task->src[i]; - U64 leaf_digit0 = BitExtract(bucket->enc_leaf_idx, 10, 0); - ++counts_ptr[leaf_digit0]; + U64 leaf_digit0 = BitExtract(bucket->leaf_idx, 10, 0); + counts_ptr[leaf_digit0] += 1; } } break; case 1: { - for (U64 i = range.min; i < range.max; ++i) { + for EachInRange(i, range) { LNK_LeafRef *bucket = task->src[i]; - U64 leaf_digit1 = BitExtract(bucket->enc_leaf_idx, 11, 10); - ++counts_ptr[leaf_digit1]; + U64 leaf_digit1 = BitExtract(bucket->leaf_idx, 11, 10); + counts_ptr[leaf_digit1] += 1; } } break; case 2: { - for (U64 i = range.min; i < range.max; ++i) { + for EachInRange(i, range) { LNK_LeafRef *bucket = task->src[i]; - U64 leaf_digit2 = BitExtract(bucket->enc_leaf_idx, 11, 21 - 1); // don't take into account IPI flag - ++counts_ptr[leaf_digit2]; + U64 leaf_digit2 = BitExtract(bucket->leaf_idx, 11, 21); + counts_ptr[leaf_digit2] += 1; } } break; case 3: { - for (U64 i = range.min; i < range.max; ++i) { + for EachInRange(i, range) { LNK_LeafRef *bucket = task->src[i]; - U64 digit0 = BitExtract(bucket->enc_loc_idx, loc_idx_bit_count_0, 0); - ++counts_ptr[digit0]; + U64 digit0 = BitExtract(bucket->obj_idx, obj_idx_bit_count_0, 0); + counts_ptr[digit0] += 1; } } break; case 4: { - for (U64 i = range.min; i < range.max; ++i) { + for EachInRange(i, range) { LNK_LeafRef *bucket = task->src[i]; - U64 digit1 = BitExtract(bucket->enc_loc_idx, loc_idx_bit_count_1, loc_idx_bit_count_0); - ++counts_ptr[digit1]; + U64 digit1 = BitExtract(bucket->obj_idx, obj_idx_bit_count_1, obj_idx_bit_count_0); + counts_ptr[digit1] += 1; } } break; case 5: { - for (U64 i = range.min; i < range.max; ++i) { + for EachInRange(i, range) { LNK_LeafRef *bucket = task->src[i]; - U64 digit2 = BitExtract(bucket->enc_loc_idx, loc_idx_bit_count_2, loc_idx_bit_count_0 + loc_idx_bit_count_1); - - U64 loc_bit = !!(bucket->enc_loc_idx & LNK_LeafRefFlag_LocIdxExternal); - digit2 |= loc_bit << loc_idx_bit_count_2; - - ++counts_ptr[digit2]; + U64 digit2 = BitExtract(bucket->obj_idx, obj_idx_bit_count_2, obj_idx_bit_count_0 + obj_idx_bit_count_1); + counts_ptr[digit2] += 1; } } break; default: InvalidPath; @@ -1693,12 +1053,12 @@ THREAD_POOL_TASK_FUNC(lnk_loc_idx_radix_sort_task) { ProfBeginFunction(); - LNK_LeafRadixSortTask *task = raw_task; - Rng1U64 range = task->ranges[task_id]; - U32 *counts_ptr = task->counts_arr[task_id]; - U32 loc_idx_bit_count_0 = task->loc_idx_bit_count_0; - U32 loc_idx_bit_count_1 = task->loc_idx_bit_count_1; - U32 loc_idx_bit_count_2 = task->loc_idx_bit_count_2; + LNK_MergeTypes *task = raw_task; + Rng1U64 range = task->ranges[task_id]; + U32 *counts_ptr = task->counts_arr[task_id]; + U32 obj_idx_bit_count_0 = task->obj_idx_bit_count_0; + U32 obj_idx_bit_count_1 = task->obj_idx_bit_count_1; + U32 obj_idx_bit_count_2 = task->obj_idx_bit_count_2; switch (task->pass_idx) { // @@ -1706,27 +1066,27 @@ THREAD_POOL_TASK_FUNC(lnk_loc_idx_radix_sort_task) // case 0: { ProfBegin("Leaf Sort Low"); - for (U64 i = range.min; i < range.max; ++i) { + for EachInRange(i, range) { LNK_LeafRef *bucket = task->src[i]; - U64 leaf_digit0 = BitExtract(bucket->enc_leaf_idx, 10, 0); + U64 leaf_digit0 = BitExtract(bucket->leaf_idx, 10, 0); task->dst[counts_ptr[leaf_digit0]++] = bucket; } ProfEnd(); } break; case 1: { ProfBegin("Leaf Sort Mid"); - for (U64 i = range.min; i < range.max; ++i) { + for EachInRange(i, range) { LNK_LeafRef *bucket = task->src[i]; - U64 leaf_digit1 = BitExtract(bucket->enc_leaf_idx, 11, 10); + U64 leaf_digit1 = BitExtract(bucket->leaf_idx, 11, 10); task->dst[counts_ptr[leaf_digit1]++] = bucket; } ProfEnd(); } break; case 2: { ProfBegin("Leaf Sort High"); - for (U64 i = range.min; i < range.max; ++i) { + for EachInRange(i, range) { LNK_LeafRef *bucket = task->src[i]; - U64 leaf_digit2 = BitExtract(bucket->enc_leaf_idx, 11, 21 - 1); // don't take into account IPI flag + U64 leaf_digit2 = BitExtract(bucket->leaf_idx, 11, 21); task->dst[counts_ptr[leaf_digit2]++] = bucket; } ProfEnd(); @@ -1736,32 +1096,28 @@ THREAD_POOL_TASK_FUNC(lnk_loc_idx_radix_sort_task) // Sort items on obj and type server index // case 3: { - ProfBegin("Loc Sort Low"); - for (U64 i = range.min; i < range.max; ++i) { + ProfBegin("Obj Sort Low"); + for EachInRange(i, range) { LNK_LeafRef *bucket = task->src[i]; - U64 digit0 = BitExtract(bucket->enc_loc_idx, loc_idx_bit_count_0, 0); + U64 digit0 = BitExtract(bucket->obj_idx, obj_idx_bit_count_0, 0); task->dst[counts_ptr[digit0]++] = bucket; } ProfEnd(); } break; case 4: { - ProfBegin("Loc Sort Mid"); - for (U64 i = range.min; i < range.max; ++i) { + ProfBegin("Obj Sort Mid"); + for EachInRange(i, range) { LNK_LeafRef *bucket = task->src[i]; - U64 digit1 = BitExtract(bucket->enc_loc_idx, loc_idx_bit_count_1, loc_idx_bit_count_0); + U64 digit1 = BitExtract(bucket->obj_idx, obj_idx_bit_count_1, obj_idx_bit_count_0); task->dst[counts_ptr[digit1]++] = bucket; } ProfEnd(); } break; case 5: { - ProfBegin("Loc Sort High"); - for (U64 i = range.min; i < range.max; ++i) { + ProfBegin("Obj Sort High"); + for EachInRange(i, range) { LNK_LeafRef *bucket = task->src[i]; - U64 digit2 = BitExtract(bucket->enc_loc_idx, loc_idx_bit_count_2, loc_idx_bit_count_0 + loc_idx_bit_count_1); - - U64 loc_bit = !!(bucket->enc_loc_idx & LNK_LeafRefFlag_LocIdxExternal); - digit2 |= loc_bit << loc_idx_bit_count_2; - + U64 digit2 = BitExtract(bucket->obj_idx, obj_idx_bit_count_2, obj_idx_bit_count_0 + obj_idx_bit_count_1); Assert(counts_ptr[digit2] != max_U32); task->dst[counts_ptr[digit2]++] = bucket; } @@ -1775,96 +1131,12 @@ THREAD_POOL_TASK_FUNC(lnk_loc_idx_radix_sort_task) } internal void -lnk_leaf_ref_array_sort(TP_Context *tp, LNK_LeafRefArray arr, U64 obj_count, U64 type_server_count) +lnk_leaf_ref_array_sort(TP_Context *tp, LNK_CodeViewInput *input, LNK_LeafRefArray arr, U64 debug_t_count) { Temp scratch = scratch_begin(0,0); -#if PROFILE_TELEMETRY - String8 leaf_count_string = str8_from_count(scratch.arena, arr.count); - String8 obj_count_string = str8_from_count(scratch.arena, obj_count); - String8 type_server_count_string = str8_from_count(scratch.arena, type_server_count); - ProfBeginDynamic("Leaf Sort [Leaf Count: %.*s, Obj Count: %.*s, Type Server Count: %.*s]", str8_varg(leaf_count_string), str8_varg(obj_count_string), str8_varg(type_server_count_string)); -#endif + ProfBeginDynamic("Leaf Sort [Leaf Count: %.*s]", str8_varg(str8_from_count(scratch.arena, arr.count))); - if (arr.count > 140000) { - ProfBegin("Radix"); - - U32 loc_idx_max_bits = 32 - clz32(Max(obj_count, type_server_count)); - - LNK_LeafRadixSortTask task = {0}; - task.loc_idx_bit_count_0 = Clamp(0, (S32)loc_idx_max_bits - 21, 11); - task.loc_idx_bit_count_1 = Clamp(0, (S32)loc_idx_max_bits - 10, 11); - task.loc_idx_bit_count_2 = Clamp(0, (S32)loc_idx_max_bits, 10); - task.counts_max = (1 << 11); - task.loc_idx_max = arr.count; - task.ranges = tp_divide_work(scratch.arena, arr.count, tp->worker_count); - task.dst = push_array_no_zero(scratch.arena, LNK_LeafRef *, arr.count); - task.src = arr.v; - - ProfBegin("Push Counts"); - task.counts_arr = push_array_no_zero(scratch.arena, U32 *, tp->worker_count); - for (U64 i = 0; i < tp->worker_count; ++i) { - // zero-out happens in histogram step - task.counts_arr[i] = push_array_no_zero(scratch.arena, U32, task.counts_max); - } - ProfEnd(); - - for (task.pass_idx = 0; task.pass_idx < 6; ++task.pass_idx) { - ProfBeginDynamic("Pass: %u", task.pass_idx); - - ProfBegin("Histo"); - tp_for_parallel(tp, 0, tp->worker_count, lnk_leaf_ref_histo_task, &task); - ProfEnd(); - - B32 is_range_not_empty = 0; - for (U64 task_id = 0; task_id < tp->worker_count; ++task_id) { - is_range_not_empty = task.counts_arr[task_id][0] != dim_1u64(task.ranges[task_id]); - if (is_range_not_empty) { - break; - } - } - - ProfBegin("Counts -> Offsets"); - { - U64 digit_cursor = 0; - for (U64 digit_idx = 0; digit_idx < task.counts_max; ++digit_idx) { - for (U64 task_id = 0; task_id < tp->worker_count; ++task_id) { - U64 count = task.counts_arr[task_id][digit_idx]; - task.counts_arr[task_id][digit_idx] = digit_cursor; - digit_cursor += count; - } - } - Assert(digit_cursor == arr.count); - } - ProfEnd(); - - ProfBegin("Sort"); - tp_for_parallel(tp, 0, tp->worker_count, lnk_loc_idx_radix_sort_task, &task); - Swap(LNK_LeafRef **, task.src, task.dst); - ProfEnd(); - - ProfEnd(); - } - - if (task.src != arr.v) { - MemoryCopyTyped(arr.v, task.dst, arr.count); - } - -#if 0 - for (U64 i = 1; i < arr.count; ++i) { - AssertAlways(arr.v[i-1]->leaf_ref.enc_loc_idx <= arr.v[i]->leaf_ref.enc_loc_idx); - if (arr.v[i-1]->leaf_ref.enc_loc_idx == arr.v[i]->leaf_ref.enc_loc_idx) { - AssertAlways(arr.v[i-1]->leaf_ref.enc_leaf_idx <= arr.v[i]->leaf_ref.enc_leaf_idx); - } - } -#endif - - ProfEnd(); - } else { - ProfBegin("Radsort"); - radsort(arr.v, arr.count, lnk_leaf_ref_is_before); - ProfEnd(); - } scratch_end(scratch); ProfEnd(); @@ -1873,7 +1145,7 @@ lnk_leaf_ref_array_sort(TP_Context *tp, LNK_LeafRefArray arr, U64 obj_count, U64 internal THREAD_POOL_TASK_FUNC(lnk_assign_type_indices_task) { - LNK_CvImportTypes *task = raw_task; + LNK_MergeTypes *task = raw_task; CV_TypeIndexSource ti_source = task->ti_source; LNK_LeafRefArray unique_leaf_refs = task->unique_leaf_refs_arr[ti_source]; @@ -1885,7 +1157,7 @@ THREAD_POOL_TASK_FUNC(lnk_assign_type_indices_task) LNK_LeafRef *leaf_ref = unique_leaf_refs.v[i]; CV_TypeIndex type_index = min_type_index + i; - U64 hash = u64_hash_from_str8(str8_struct(&leaf_ref)); + U64 hash = u64_hash_from_str8(str8_struct(leaf_ref)); U64 best_idx = hash % assigned_type_cap; U64 idx = best_idx; @@ -1917,7 +1189,7 @@ lnk_assigned_type_ht_search(U64 cap, CV_TypeIndex *ht, CV_TypeIndex min_type_ind U64 leaf_idx = type_index - min_type_index; LNK_LeafRef *compar = unique_leaf_refs.v[leaf_idx]; - if (compar == v) { return type_index; } + if (MemoryMatchStruct(compar,v)) { return type_index; } idx = (idx + 1) == cap ? 0 : (idx + 1); } while(idx != best_idx); @@ -1926,55 +1198,53 @@ lnk_assigned_type_ht_search(U64 cap, CV_TypeIndex *ht, CV_TypeIndex min_type_ind return 0; } +internal void +lnk_fixup_cv_type_indices(LNK_MergeTypes *ctx, U32 obj_idx, String8 data, CV_TypeIndexInfoList ti_info_list) +{ + for EachNode(n, CV_TypeIndexInfo, ti_info_list.first) { + CV_TypeIndex *ti_ptr = str8_deserial_get_raw_ptr(data, n->offset, sizeof(*ti_ptr)); + CV_TypeIndex ti = memory_read32(ti_ptr); + + // skip basic types + if (ti < ctx->input->min_type_indices[n->source]) { continue; } + + U64 ts_idx = ctx->input->obj_to_ts[obj_idx]; + CV_DebugT *debug_t = ts_idx ? &ctx->input->debug_t_arr[ctx->input->ts_obj_range.min + ts_idx] : &ctx->input->debug_t_arr[obj_idx]; + + CV_TypeIndex final_ti = 0; + if (contains_1u64(debug_t->ti_ranges[n->source], ti)) { + LNK_LeafRef leaf_ref = lnk_leaf_ref_from_ti(ctx->input, obj_idx, n->source, ti); + LNK_LeafHashTable *leaf_ht = &ctx->leaf_ht_arr[n->source]; + LNK_LeafRef *final_leaf = lnk_leaf_hash_table_search(leaf_ht, ctx->input, leaf_ref); + U64 final_hash = u64_hash_from_str8(str8_struct(final_leaf)); + final_ti = lnk_assigned_type_ht_search(ctx->assigned_type_caps [n->source], + ctx->assigned_type_hts [n->source], + ctx->min_type_indices [n->source], + ctx->unique_leaf_refs_arr[n->source], + final_leaf, + final_hash); + } else { + Assert(0 && "invalid type index"); + } + + memory_write32(ti_ptr, final_ti); + } +} + internal THREAD_POOL_TASK_FUNC(lnk_cv_patcher_symbols_task) { ProfBeginFunction(); - LNK_CvImportTypes *task = raw_task; - - for EachInRange(symbol_input_idx, task->ranges[task_id]) { - LNK_CodeViewSymbolsInput symbol_input = task->input->symbol_inputs[symbol_input_idx]; - LNK_LeafLocType loc_type = lnk_loc_type_from_obj_idx(task->input, symbol_input.obj_idx); - U64 loc_idx = lnk_loc_idx_from_obj_idx (task->input, symbol_input.obj_idx); - - CV_TypeIndex ti_lo_arr[CV_TypeIndexSource_COUNT] = {0}; - for EachElement(ti_source, ti_lo_arr) { ti_lo_arr[ti_source] = lnk_ti_lo_from_loc(task->input, loc_type, loc_idx, ti_source); } - - for EachNode(symbol_n, CV_SymbolNode, symbol_input.symbol_list->first) { + LNK_MergeTypes *task = raw_task; + for EachInRange(i, task->ranges[task_id]) { + LNK_SymbolInput symbols = task->input->symbol_inputs[i]; + for EachNode(n, CV_SymbolNode, symbols.symbol_list->first) { Temp temp = temp_begin(task->fixed_arenas[task_id]); - - // find type index offsets in symbol - CV_TypeIndexInfoList ti_info_list = cv_get_symbol_type_index_offsets(temp.arena, symbol_n->data.kind, symbol_n->data.data); - - // overwrite type indices in symbol - for EachNode(ti_info, CV_TypeIndexInfo, ti_info_list.first) { - CV_TypeIndex *ti_ptr = (CV_TypeIndex *)(symbol_n->data.data.str + ti_info->offset); - CV_TypeIndex ti = memory_read32(ti_ptr); - - // skip simple type indices - if (ti < ti_lo_arr[ti_info->source]) { continue; } - - U64 assigned_types_cap = task->assigned_type_caps [ti_info->source]; - CV_TypeIndex *assigned_types_ht = task->assigned_type_hts [ti_info->source]; - CV_TypeIndex min_type_index = task->min_type_indices [ti_info->source]; - LNK_LeafRefArray unique_leaf_refs = task->unique_leaf_refs_arr[ti_info->source]; - LNK_LeafHashTable *leaf_ht = &task->leaf_ht_arr [ti_info->source]; - - // find unique leaf refernece - LNK_LeafRef leaf_ref = lnk_leaf_ref_from_loc_idx_and_ti(task->input, loc_type, ti_info->source, loc_idx, ti); - LNK_LeafRef *leaf_ref_unique = lnk_leaf_hash_table_search(leaf_ht, task->input, task->hashes, leaf_ref); - U64 leaf_ref_unique_hash = u64_hash_from_str8(str8_struct(&leaf_ref_unique)); - - CV_TypeIndex final_ti = lnk_assigned_type_ht_search(assigned_types_cap, assigned_types_ht, min_type_index, unique_leaf_refs, leaf_ref_unique, leaf_ref_unique_hash); - - // we overwrite section memory directly - memory_write32(ti_ptr, final_ti); - } - + CV_TypeIndexInfoList ti_info_list = cv_get_symbol_type_index_offsets(temp.arena, n->data.kind, n->data.data); + lnk_fixup_cv_type_indices(task, symbols.obj_idx, n->data.data, ti_info_list); temp_end(temp); } } - ProfEnd(); } @@ -1982,49 +1252,15 @@ internal THREAD_POOL_TASK_FUNC(lnk_cv_patcher_inlines_task) { ProfBeginFunction(); - Temp scratch = scratch_begin(0, 0); - - LNK_CvImportTypes *task = raw_task; - - U64 loc_idx = lnk_loc_idx_from_obj_idx(task->input, task_id); - LNK_LeafLocType loc_type = lnk_loc_type_from_obj_idx(task->input, task_id); - String8List inline_data_list = cv_sub_section_from_debug_s(task->debug_s_arr[task_id], CV_C13SubSectionKind_InlineeLines); - - for EachNode(inline_data_node, String8Node, inline_data_list.first) { + LNK_MergeTypes *task = raw_task; + U64 obj_idx = task_id; + String8List inlinee_lines = cv_sub_section_from_debug_s(task->input->debug_s_arr[obj_idx], CV_C13SubSectionKind_InlineeLines); + for EachNode(inline_data_n, String8Node, inlinee_lines.first) { Temp temp = temp_begin(task->fixed_arenas[worker_id]); - - // get indices offsets - CV_TypeIndexInfoList ti_info_list = cv_get_inlinee_type_index_offsets(temp.arena, inline_data_node->string); - - for EachNode(ti_info, CV_TypeIndexInfo, ti_info_list.first) { - CV_TypeIndex *ti_ptr = (CV_TypeIndex *)(inline_data_node->string.str + ti_info->offset); - CV_TypeIndex ti = memory_read32(ti_ptr); - - // skip simple type indices - CV_TypeIndex ti_lo = lnk_ti_lo_from_loc(task->input, loc_type, loc_idx, ti_info->source); - if (ti < ti_lo) { continue; } - - U64 assigned_types_cap = task->assigned_type_caps [ti_info->source]; - CV_TypeIndex *assigned_types_ht = task->assigned_type_hts [ti_info->source]; - CV_TypeIndex min_type_index = task->min_type_indices [ti_info->source]; - LNK_LeafRefArray unique_leaf_refs = task->unique_leaf_refs_arr[ti_info->source]; - LNK_LeafHashTable *leaf_ht = &task->leaf_ht_arr [ti_info->source]; - - // find unique leaf refernece - LNK_LeafRef leaf_ref = lnk_leaf_ref_from_loc_idx_and_ti(task->input, loc_type, ti_info->source, loc_idx, ti); - LNK_LeafRef *leaf_ref_unique = lnk_leaf_hash_table_search(leaf_ht, task->input, task->hashes, leaf_ref); - U64 leaf_ref_unique_hash = u64_hash_from_str8(str8_struct(&leaf_ref_unique)); - - CV_TypeIndex final_ti = lnk_assigned_type_ht_search(assigned_types_cap, assigned_types_ht, min_type_index, unique_leaf_refs, leaf_ref_unique, leaf_ref_unique_hash); - - // patch index - memory_write32(ti_ptr, final_ti); - } - + CV_TypeIndexInfoList ti_info_list = cv_get_inlinee_type_index_offsets(temp.arena, inline_data_n->string); + lnk_fixup_cv_type_indices(task, obj_idx, inline_data_n->string, ti_info_list); temp_end(temp); } - - scratch_end(scratch); ProfEnd(); } @@ -2032,71 +1268,39 @@ internal THREAD_POOL_TASK_FUNC(lnk_cv_patcher_leaves_task) { ProfBeginFunction(); - LNK_CvImportTypes *task = raw_task; - + LNK_MergeTypes *task = raw_task; for EachInRange(leaf_ref_idx, task->ranges[task_id]) { Temp temp = temp_begin(task->fixed_arenas[task_id]); - - LNK_LeafRef *patch = task->unique_leaf_refs_arr[task->ti_source].v[leaf_ref_idx]; - - // patch leaf ref -> leaf record - U64 loc_idx = patch->enc_loc_idx & ~LNK_LeafRefFlag_LocIdxExternal; - LNK_LeafLocType loc_type = lnk_loc_type_from_leaf_ref(*patch); - CV_TypeIndex ti_lo = lnk_ti_lo_from_leaf_ref(task->input, *patch); - String8 raw_leaf = lnk_data_from_leaf_ref(task->input, *patch); - CV_Leaf leaf = cv_leaf_from_string(raw_leaf); - - // get type indices offsets - CV_TypeIndexInfoList ti_info_list = cv_get_leaf_type_index_offsets(temp.arena, leaf.kind, leaf.data); - - for EachNode(ti_info, CV_TypeIndexInfo, ti_info_list.first) { - CV_TypeIndex *ti_ptr = (CV_TypeIndex *)(leaf.data.str + ti_info->offset); - CV_TypeIndex ti = memory_read32(ti_ptr); - - // skip simple type indices - if (ti < ti_lo) { continue; } - - U64 assigned_types_cap = task->assigned_type_caps [ti_info->source]; - CV_TypeIndex *assigned_types_ht = task->assigned_type_hts [ti_info->source]; - CV_TypeIndex min_type_index = task->min_type_indices [ti_info->source]; - LNK_LeafRefArray unique_leaf_refs = task->unique_leaf_refs_arr[ti_info->source]; - LNK_LeafHashTable *leaf_ht = &task->leaf_ht_arr [ti_info->source]; - - // find unique leaf ref - LNK_LeafRef leaf_ref = lnk_leaf_ref_from_loc_idx_and_ti(task->input, loc_type, ti_info->source, loc_idx, ti); - LNK_LeafRef *leaf_ref_unique = lnk_leaf_hash_table_search(leaf_ht, task->input, task->hashes, leaf_ref); - U64 leaf_ref_unique_hash = u64_hash_from_str8(str8_struct(&leaf_ref_unique)); - - CV_TypeIndex final_ti = lnk_assigned_type_ht_search(assigned_types_cap, assigned_types_ht, min_type_index, unique_leaf_refs, leaf_ref_unique, leaf_ref_unique_hash); - - // patch index - memory_write32(ti_ptr, final_ti); - } - + LNK_LeafRef *patch = task->unique_leaf_refs_arr[task->ti_source].v[leaf_ref_idx]; + CV_DebugT *debug_t = &task->input->debug_t_arr[patch->obj_idx]; + CV_Leaf leaf = cv_debug_t_get_leaf(debug_t, patch->leaf_idx); + CV_TypeIndexInfoList ti_info_list = cv_get_leaf_type_index_offsets(temp.arena, leaf.kind, leaf.data); + lnk_fixup_cv_type_indices(task, patch->obj_idx, leaf.data, ti_info_list); temp_end(temp); } - ProfEnd(); } internal THREAD_POOL_TASK_FUNC(lnk_unbucket_raw_leaves_task) { - LNK_CvImportTypes *task = raw_task; + LNK_MergeTypes *task = raw_task; for EachInRange(i, task->ranges[task_id]) { - LNK_LeafRef *leaf_ref_ptr = task->unique_leaf_refs_arr[task->ti_source].v[i]; - task->merged_types.v[task->ti_source][i] = lnk_data_from_leaf_ref(task->input, *leaf_ref_ptr).str; + LNK_LeafRef leaf_ref = *task->unique_leaf_refs_arr[task->ti_source].v[i]; + CV_DebugT *debug_t = &task->input->debug_t_arr[leaf_ref.obj_idx]; + String8 raw_leaf = cv_debug_t_get_raw_leaf(debug_t, leaf_ref.leaf_idx); + task->result.v[task->ti_source][i] = raw_leaf.str; } } internal THREAD_POOL_TASK_FUNC(lnk_fixup_symbols_task) { - LNK_CvImportTypes *task = raw_task; + LNK_MergeTypes *task = raw_task; - U64 leaf_count_ipi = task->merged_types.count[CV_TypeIndexSource_IPI]; - U8 **leaf_arr_ipi = task->merged_types.v [CV_TypeIndexSource_IPI]; - CV_TypeIndex min_ti_ipi = task->min_type_indices [CV_TypeIndexSource_IPI]; + U64 leaf_count_ipi = task->result.count [CV_TypeIndexSource_IPI]; + U8 **leaf_arr_ipi = task->result.v [CV_TypeIndexSource_IPI]; + CV_TypeIndex min_ti_ipi = task->min_type_indices[CV_TypeIndexSource_IPI]; for EachNode(symnode, CV_SymbolNode, task->input->symbol_inputs[task_id].symbol_list->first) { CV_Symbol *symbol = &symnode->data; @@ -2154,150 +1358,91 @@ lnk_merge_types(TP_Context *tp, TP_Arena *tp_temp, LNK_CodeViewInput *input) ProfBeginFunction(); Temp scratch = temp_begin(lnk_get_huge_arena()); - U64 max_ti_list_size = sizeof(CV_TypeIndexInfo) * (max_U16 / sizeof(CV_TypeIndex)); - Arena **fixed_arenas = alloc_fixed_size_arena_array(scratch.arena, tp->worker_count, max_ti_list_size, max_ti_list_size); + LNK_MergeTypes task = { .input = input, }; + U64 max_ti_list_size = sizeof(CV_TypeIndexInfo) * (max_U16 / sizeof(CV_TypeIndex)); + task.fixed_arenas = alloc_fixed_size_arena_array(scratch.arena, tp->worker_count, max_ti_list_size, max_ti_list_size); - LNK_CvImportTypes task = {0}; - task.input = input; - task.hashes = push_array(scratch.arena, LNK_LeafHashes, 1); - task.fixed_arenas = fixed_arenas; - - ProfBegin("Hash Leaves"); + ProfBegin("Produce Hashes"); { - // push internal hash arrays - // - // TPI and IPI leaves in .debug$T are stored in one array (we don't move them - // to respective arrays before this point to save on memory move) - ProfBegin("Push Internal Hash Arrays"); - task.hashes->internal_hashes = push_array_no_zero(scratch.arena, U64 **, input->internal_count); - for EachIndex(obj_idx, input->internal_count) { - CV_DebugT debug_t = input->merged_debug_t_p_arr[obj_idx]; - - U64 *arr = push_array_no_zero(scratch.arena, U64, debug_t.count); - - // :debug_zero_hash_assert - #if BUILD_DEBUG - MemoryZeroTyped(arr, debug_t.count); - #endif - - task.hashes->internal_hashes[obj_idx] = push_array(scratch.arena, U64 *, CV_TypeIndexSource_COUNT); - for EachIndex(ti_source, CV_TypeIndexSource_COUNT) { - task.hashes->internal_hashes[obj_idx][ti_source] = arr; + ProfBegin("Alloc Hashes"); + U32Array indices[] = { + input->int_obj_indices, + input->debug_p_indices, + input->type_server_indices + // TODO: explain + // input->ext_obj_indices, + }; + for EachElement(i, indices) { + for EachIndex(k, indices[i].count) { + U64 obj_idx = indices[i].v[k]; + CV_DebugH *debug_h = &input->debug_h_arr[obj_idx]; + debug_h->count = input->debug_t_arr[obj_idx].count; + debug_h->v = push_array(scratch.arena, U64, debug_h->count); } } ProfEnd(); - - // push external hash arrays - ProfBegin("Push External Hash Arrays"); - task.hashes->external_hashes = push_array_no_zero(scratch.arena, U64 **, input->type_server_count); - for EachIndex(ts_idx, input->type_server_count) { - task.hashes->external_hashes[ts_idx] = push_array_no_zero(scratch.arena, U64 *, CV_TypeIndexSource_COUNT); - for EachIndex(ti_source, CV_TypeIndexSource_COUNT) { - U64 type_count = dim_1u64(input->external_ti_ranges[ts_idx][ti_source]); - task.hashes->external_hashes[ts_idx][ti_source] = push_array(scratch.arena, U64, type_count); // :zero_hash_check - } - } - ProfEnd(); - + // hash .debug$P first so we can mix in hashes for precompiled sub leaves when hashing leaves in .debug$T - task.debug_t_arr = input->internal_debug_p_arr; - ProfScope("Hash .debug$P [Count: %llu]", input->internal_count) - tp_for_parallel(tp, 0, input->internal_count, lnk_hash_debug_t_task, &task); + ProfBegin("Hash"); + task.indices = input->debug_p_indices; + ProfScope(".debug$P [Count: %llu]", input->debug_p_indices.count) + tp_for_parallel(tp, 0, task.indices.count, lnk_hash_debug_t_task, &task); - task.debug_t_arr = input->internal_debug_t_arr; - ProfScope("Hash .debug$T [Count: %.*s]", str8_varg(str8_from_count(scratch.arena, input->internal_count))) - tp_for_parallel(tp, 0, input->internal_count, lnk_hash_debug_t_task, &task); + task.indices = input->int_obj_indices; + ProfScope(".debug$T [Count: %.*s]", str8_varg(str8_from_count(scratch.arena, task.indices.count))) + tp_for_parallel(tp, 0, task.indices.count, lnk_hash_debug_t_task, &task); - task.ti_source = CV_TypeIndexSource_IPI; - ProfScope("Hash IPI Type Servers") - tp_for_parallel(tp, 0, input->type_server_count, lnk_hash_debug_t_deep_task, &task); + task.indices = input->type_server_indices; + ProfScope("Type Servers [Count: %.*s]", str8_varg(str8_from_count(scratch.arena, task.indices.count))); + tp_for_parallel(tp, 0, task.indices.count, lnk_hash_debug_t_deep_task, &task); + ProfEnd(); - task.ti_source = CV_TypeIndexSource_TPI; - ProfScope("Hash TPI Type Servers") - tp_for_parallel(tp, 0, input->type_server_count, lnk_hash_debug_t_deep_task, &task); +#if BUILD_DEBUG + for EachIndex(i, input->count) { + for EachIndex(k, input->debug_h_arr[i].count) { + Assert(input->debug_h_arr[i].v[k] != 0 && + input->debug_h_arr[i].v[k] != 1); + } + } +#endif + + // for external objs wire hash sections to type servers hashes + for EachIndex(i, input->ext_obj_indices.count) { + U64 dst_obj_idx = input->ext_obj_indices.v[i]; + U64 src_obj_idx = input->obj_to_ts[dst_obj_idx]; + CV_DebugH *dst_debug_h = &input->debug_h_arr[dst_obj_idx]; + CV_DebugH *src_debug_h = &input->debug_h_arr[src_obj_idx]; + *dst_debug_h = *src_debug_h; + } } ProfEnd(); ProfBegin("Leaf Hash Table Init"); - { - ProfBegin("Count Types Per Source"); - { - Temp temp = temp_begin(scratch.arena); - - ProfBegin("Compute Per Task Ranges"); - U64 per_task_type_count = 10000; - LNK_LeafRangeList *leaf_ranges_per_task = push_array(temp.arena, LNK_LeafRangeList, tp->worker_count); - for (U64 i = 0, task_weight = 0, task_id = 0; i < input->internal_count; i += 1) { - CV_DebugT *debug_t = &input->merged_debug_t_p_arr[i]; - for (U64 k = 0; k < debug_t->count; k += per_task_type_count) { - U64 cap = per_task_type_count - task_weight; - - LNK_LeafRange *leaf_range = push_array(temp.arena, LNK_LeafRange, 1); - leaf_range->range = rng_1u64(k, Min(k + cap, debug_t->count)); - leaf_range->debug_t = debug_t; - - LNK_LeafRangeList *list = &leaf_ranges_per_task[task_id]; - SLLQueuePush(list->first, list->last, leaf_range); - list->count += 1; - - task_weight += dim_1u64(leaf_range->range); - if (task_weight >= per_task_type_count) { - task_id = (task_id + 1) % tp->worker_count; - task_weight = 0; - } - } - } - ProfEnd(); - - task.leaf_ranges_per_task = leaf_ranges_per_task; - tp_for_parallel(tp, 0, tp->worker_count, lnk_count_per_source_leaf_task, &task); - - temp_end(temp); - } - ProfEnd(); - - // count external leaves - for EachIndex(ts_idx, input->type_server_count) { - for EachIndex(ti_source, CV_TypeIndexSource_COUNT) { - task.external_per_source_count[ti_source] += dim_1u64(input->external_ti_ranges[ts_idx][ti_source]); - } - } - - // push buckets per source - for EachIndex(ti_source, CV_TypeIndexSource_COUNT) { - task.leaf_ht_arr[ti_source].cap = 0; - task.leaf_ht_arr[ti_source].cap += task.internal_per_source_count[ti_source]; - task.leaf_ht_arr[ti_source].cap += task.external_per_source_count[ti_source]; - task.leaf_ht_arr[ti_source].cap = (task.leaf_ht_arr[ti_source].cap * 13) / 10; // * 1.3 - task.leaf_ht_arr[ti_source].bucket_arr = push_array(scratch.arena, LNK_LeafRef *, task.leaf_ht_arr[ti_source].cap); - - #if PROFILE_TELEMETRY - tmMessage(0, TMMF_ICON_NOTE, "%.*s Bucket Count: %.*s", str8_varg(cv_string_from_type_index_source(ti_source)), str8_varg(str8_from_count(scratch.arena, task.leaf_ht_arr[ti_source].cap))); - #endif - } - } - ProfEnd(); - - ProfBeginDynamic("Internal Leaf Dedup [Obj Count: %.*s, TPI: %.*s, IPI: %.*s]", - str8_varg(str8_from_count(scratch.arena, input->internal_count)), - str8_varg(str8_from_count(scratch.arena, task.internal_per_source_count[CV_TypeIndexSource_TPI])), - str8_varg(str8_from_count(scratch.arena, task.internal_per_source_count[CV_TypeIndexSource_IPI]))); - - task.debug_t_arr = input->internal_debug_p_arr; - tp_for_parallel_prof(tp, tp_temp, input->internal_count, lnk_leaf_dedup_internal_task, &task, "Dedup .debug$P"); - - task.debug_t_arr = input->internal_debug_t_arr; - tp_for_parallel_prof(tp, tp_temp, input->internal_count, lnk_leaf_dedup_internal_task, &task, "Dedup .debug$T"); - - ProfEnd(); - - ProfBeginDynamic("External Leaf Import [Type Server Count: %llu, Dependent Obj Count: %llu]", input->type_server_count, input->external_count); for EachIndex(ti_source, CV_TypeIndexSource_COUNT) { - task.ti_source = ti_source; - tp_for_parallel(tp, tp_temp, input->type_server_count, lnk_leaf_dedup_external_task, &task); + U64 total_count = 0; + for EachIndex(obj_idx, input->count) { total_count += input->debug_t_arr[obj_idx].source_counts[ti_source]; } + + task.leaf_ht_arr[ti_source].cap = total_count; + task.leaf_ht_arr[ti_source].cap = (task.leaf_ht_arr[ti_source].cap * 13) / 10; // * 1.3 + task.leaf_ht_arr[ti_source].bucket_arr = push_array(scratch.arena, LNK_LeafRef *, task.leaf_ht_arr[ti_source].cap); + +#if PROFILE_TELEMETRY + tmMessage(0, TMMF_ICON_NOTE, "%.*s Bucket Count: %.*s", str8_varg(cv_string_from_type_index_source(ti_source)), str8_varg(str8_from_count(scratch.arena, task.leaf_ht_arr[ti_source].cap))); +#endif } ProfEnd(); + ProfBegin("Leaf Dedup"); + task.indices = input->debug_p_indices; + tp_for_parallel_prof(tp, tp_temp, task.indices.count, lnk_leaf_dedup_task, &task, ".debug$P"); + + task.indices = input->int_obj_indices; + tp_for_parallel_prof(tp, tp_temp, task.indices.count, lnk_leaf_dedup_task, &task, ".debug$T"); + + task.indices = input->type_server_indices; + tp_for_parallel_prof(tp, tp_temp, task.indices.count, lnk_leaf_dedup_task, &task, "Type Servers"); + ProfEnd(); + ProfBegin("Extract present buckets from the leaf hash tables"); for EachIndex(ti_source, CV_TypeIndexSource_COUNT) { @@ -2312,7 +1457,86 @@ lnk_merge_types(TP_Context *tp, TP_Arena *tp_temp, LNK_CodeViewInput *input) tp_for_parallel_prof(tp, 0, tp->worker_count, lnk_get_present_buckets_task, &task, "Copy present buckets"); // sort output leaves based on { location index, leaf index } to guarantee determinism - lnk_leaf_ref_array_sort(tp, task.unique_leaf_refs_arr[ti_source], input->internal_count, input->type_server_count); + { + LNK_LeafRefArray arr = task.unique_leaf_refs_arr[ti_source]; + if (arr.count > 140000) { + ProfBegin("Radix"); + + U32 obj_idx_max_bits = 32 - clz32(input->count); + + task.obj_idx_bit_count_0 = Clamp(0, (S32)obj_idx_max_bits - 21, 11); + task.obj_idx_bit_count_1 = Clamp(0, (S32)obj_idx_max_bits - 10, 11); + task.obj_idx_bit_count_2 = Clamp(0, (S32)obj_idx_max_bits, 10); + task.counts_max = (1 << 11); + task.ranges = tp_divide_work(scratch.arena, arr.count, tp->worker_count); + task.dst = push_array_no_zero(scratch.arena, LNK_LeafRef *, arr.count); + task.src = arr.v; + + ProfBegin("Push Counts"); + task.counts_arr = push_array_no_zero(scratch.arena, U32 *, tp->worker_count); + for (U64 i = 0; i < tp->worker_count; ++i) { + // zero-out happens in histogram step + task.counts_arr[i] = push_array_no_zero(scratch.arena, U32, task.counts_max); + } + ProfEnd(); + + for (task.pass_idx = 0; task.pass_idx < 6; ++task.pass_idx) { + ProfBeginDynamic("Pass: %u", task.pass_idx); + + ProfBegin("Histo"); + tp_for_parallel(tp, 0, tp->worker_count, lnk_leaf_ref_histo_task, &task); + ProfEnd(); + + B32 is_range_not_empty = 0; + for (U64 task_id = 0; task_id < tp->worker_count; ++task_id) { + is_range_not_empty = task.counts_arr[task_id][0] != dim_1u64(task.ranges[task_id]); + if (is_range_not_empty) { + break; + } + } + + ProfBegin("Counts -> Offsets"); + { + U64 digit_cursor = 0; + for EachIndex(digit_idx, task.counts_max) { + for EachIndex(task_id, tp->worker_count) { + U64 count = task.counts_arr[task_id][digit_idx]; + task.counts_arr[task_id][digit_idx] = digit_cursor; + digit_cursor += count; + } + } + Assert(digit_cursor == arr.count); + } + ProfEnd(); + + ProfBegin("Sort"); + tp_for_parallel(tp, 0, tp->worker_count, lnk_loc_idx_radix_sort_task, &task); + Swap(LNK_LeafRef **, task.src, task.dst); + ProfEnd(); + + ProfEnd(); + } + + if (task.src != arr.v) { + MemoryCopyTyped(arr.v, task.dst, arr.count); + } + + ProfEnd(); + } else { + ProfBegin("Radsort"); + radsort(arr.v, arr.count, lnk_leaf_ref_is_before); + ProfEnd(); + } + +#if 0 + for (U64 i = 1; i < arr.count; ++i) { + AssertAlways(arr.v[i-1]->obj_idx <= arr.v[i]->obj_idx); + if (arr.v[i-1]->obj_idx == arr.v[i]->obj_idx) { + AssertAlways(arr.v[i-1]->obj_idx <= arr.v[i]->obj_idx); + } + } +#endif + } } #if PROFILE_TELEMETRY @@ -2335,7 +1559,7 @@ lnk_merge_types(TP_Context *tp, TP_Arena *tp_temp, LNK_CodeViewInput *input) ProfBegin("Patch Type Indices"); { - task.ranges = tp_divide_work(scratch.arena, input->total_symbol_input_count, tp->worker_count); + task.ranges = tp_divide_work(scratch.arena, input->symbol_input_count, tp->worker_count); tp_for_parallel_prof(tp, 0, tp->worker_count, lnk_cv_patcher_symbols_task, &task, "Symbols"); task.ranges = 0; @@ -2352,18 +1576,20 @@ lnk_merge_types(TP_Context *tp, TP_Arena *tp_temp, LNK_CodeViewInput *input) for EachIndex(ti_source, CV_TypeIndexSource_COUNT) { LNK_LeafRefArray unique_leaf_refs = task.unique_leaf_refs_arr[ti_source]; - task.ti_source = ti_source; - task.merged_types.count[ti_source] = unique_leaf_refs.count; - task.merged_types.v [ti_source] = push_array(tp_temp->v[0], U8 *, unique_leaf_refs.count); - task.ranges = tp_divide_work(scratch.arena, unique_leaf_refs.count, tp->worker_count); + task.ti_source = ti_source; + task.result.count[ti_source] = unique_leaf_refs.count; + task.result.v [ti_source] = push_array(tp_temp->v[0], U8 *, unique_leaf_refs.count); + task.ranges = tp_divide_work(scratch.arena, unique_leaf_refs.count, tp->worker_count); tp_for_parallel(tp, 0, tp->worker_count, lnk_unbucket_raw_leaves_task, &task); } - tp_for_parallel_prof(tp, 0, input->total_symbol_input_count, lnk_fixup_symbols_task, &task, "fixup type indices in symbols"); + tp_for_parallel_prof(tp, 0, input->symbol_input_count, lnk_fixup_symbols_task, &task, "fixup type indices in symbols"); + + MemoryCopyTyped(task.result.min_type_indices, input->min_type_indices, CV_TypeIndexSource_COUNT); temp_end(scratch); ProfEnd(); - return task.merged_types; + return task.result; } internal @@ -2976,11 +2202,10 @@ lnk_build_pdb(TP_Context *tp, U64 obj_count, LNK_Obj **obj_arr, CV_DebugS *debug_s_arr, - U64 total_symbol_input_count, - LNK_CodeViewSymbolsInput *symbol_inputs, + U64 symbol_input_count, + LNK_SymbolInput *symbol_inputs, CV_SymbolListArray *parsed_symbols, - U64 leaf_count[CV_TypeIndexSource_COUNT], - U8 **leaf_arr [CV_TypeIndexSource_COUNT]) + LNK_MergedTypes types) { ProfBegin("PDB"); Temp scratch = scratch_begin(tp_arena->v, tp_arena->count); @@ -2994,12 +2219,15 @@ lnk_build_pdb(TP_Context *tp, PDB_Context *pdb = pdb_alloc_(huge_arena_temp.arena, config->pdb_page_size, config->machine, config->time_stamp, config->age, config->guid); ProfEnd(); + // set min type indices + for EachElement(ti_source, types.min_type_indices) { pdb->type_servers[ti_source]->ti_lo = types.min_type_indices[ti_source]; } + // move patched type data // // leaf data is stored in g_file_arena which has linker's life-time // and this way we skip redundant leaf copy to the type server to make things faster - pdb_type_server_push_parallel(tp, pdb->type_servers[CV_TypeIndexSource_IPI], leaf_count[CV_TypeIndexSource_IPI], leaf_arr[CV_TypeIndexSource_IPI]); - pdb_type_server_push_parallel(tp, pdb->type_servers[CV_TypeIndexSource_TPI], leaf_count[CV_TypeIndexSource_TPI], leaf_arr[CV_TypeIndexSource_TPI]); + pdb_type_server_push_parallel(tp, pdb->type_servers[CV_TypeIndexSource_IPI], types.count[CV_TypeIndexSource_IPI], types.v[CV_TypeIndexSource_IPI]); + pdb_type_server_push_parallel(tp, pdb->type_servers[CV_TypeIndexSource_TPI], types.count[CV_TypeIndexSource_TPI], types.v[CV_TypeIndexSource_TPI]); ProfBegin("Collect Symbols for GSI"); CV_SymbolList *gsi_list_arr = push_array(scratch.arena, CV_SymbolList, obj_count); @@ -3261,39 +2489,39 @@ THREAD_POOL_TASK_FUNC(lnk_build_udt_name_hash_table_task) B32 is_inserted_or_updated = 0; do { retry:; - LNK_UDTNameBucket *curr_bucket = task->buckets[bucket_idx]; + LNK_UDTNameBucket *curr_bucket = task->buckets[bucket_idx]; - if (curr_bucket == 0) { - LNK_UDTNameBucket *compare_bucket = ins_atomic_ptr_eval_cond_assign(&task->buckets[bucket_idx], new_bucket, curr_bucket); + if (curr_bucket == 0) { + LNK_UDTNameBucket *compare_bucket = ins_atomic_ptr_eval_cond_assign(&task->buckets[bucket_idx], new_bucket, curr_bucket); - if (compare_bucket == curr_bucket) { - // success, bucket was inserted - is_inserted_or_updated = 1; - break; - } + if (compare_bucket == curr_bucket) { + // success, bucket was inserted + is_inserted_or_updated = 1; + break; + } - // another thread took the bucket... - goto retry; - } else if (str8_match(curr_bucket->name, name, 0)) { - // there is more than one UDT with identical name, pick most recent and ignore others + // another thread took the bucket... + goto retry; + } else if (str8_match(curr_bucket->name, name, 0)) { + // there is more than one UDT with identical name, pick most recent and ignore others - if (leaf_idx < curr_bucket->leaf_idx) { - LNK_UDTNameBucket *compare_bucket = ins_atomic_ptr_eval_cond_assign(&task->buckets[bucket_idx], new_bucket, curr_bucket); - if (compare_bucket == curr_bucket) { - is_inserted_or_updated = 1; - break; - } - } else { - // don't need to update, more recent leaf is in the bucket - break; - } + if (leaf_idx < curr_bucket->leaf_idx) { + LNK_UDTNameBucket *compare_bucket = ins_atomic_ptr_eval_cond_assign(&task->buckets[bucket_idx], new_bucket, curr_bucket); + if (compare_bucket == curr_bucket) { + is_inserted_or_updated = 1; + break; + } + } else { + // don't need to update, more recent leaf is in the bucket + break; + } - // another thread took the bucket... - goto retry; - } + // another thread took the bucket... + goto retry; + } - // advance - bucket_idx = (bucket_idx + 1) % task->buckets_cap; + // advance + bucket_idx = (bucket_idx + 1) % task->buckets_cap; } while (bucket_idx != best_idx); if (is_inserted_or_updated) { @@ -4502,7 +3730,7 @@ THREAD_POOL_TASK_FUNC(lnk_convert_symbols_to_rdi_task) Temp scratch = scratch_begin(&arena, 1); LNK_ConvertUnitToRDITask *task = raw_task; - LNK_CodeViewSymbolsInput symbols_input = task->symbol_inputs[task_id]; + LNK_SymbolInput symbols_input = task->symbol_inputs[task_id]; LNK_Obj *obj = task->obj_arr[symbols_input.obj_idx]; LNK_CodeViewCompilerInfo comp_info = task->comp_info_arr[symbols_input.obj_idx]; CV_InlineeLinesAccel *inlinee_lines_accel = task->inlinee_lines_accel_arr[symbols_input.obj_idx]; @@ -5217,11 +4445,10 @@ lnk_build_rad_debug_info(TP_Context *tp, U64 obj_count, LNK_Obj **obj_arr, CV_DebugS *debug_s_arr, - U64 total_symbol_input_count, - LNK_CodeViewSymbolsInput *symbol_inputs, + U64 symbol_input_count, + LNK_SymbolInput *symbol_inputs, CV_SymbolListArray *parsed_symbols, - U64 leaf_count[CV_TypeIndexSource_COUNT], - U8 **leaf_arr [CV_TypeIndexSource_COUNT]) + LNK_MergedTypes types) { ProfBegin("RDI"); Temp scratch = scratch_begin(tp_arena->v,tp_arena->count); @@ -5275,7 +4502,7 @@ lnk_build_rad_debug_info(TP_Context *tp, // assing low and high type indices per source Rng1U64 itype_ranges[CV_TypeIndexSource_COUNT]; for (U64 i = 0; i < ArrayCount(itype_ranges); ++i) { - itype_ranges[i] = rng_1u64(CV_MinComplexTypeIndex, CV_MinComplexTypeIndex + leaf_count[i]); + itype_ranges[i] = rng_1u64(CV_MinComplexTypeIndex, CV_MinComplexTypeIndex + types.count[i]); } ProfBegin("Convert Types"); @@ -5298,14 +4525,14 @@ lnk_build_rad_debug_info(TP_Context *tp, ProfBegin("Build UDT Name Hash Table"); // TODO: fix memory life-time udt_name_buckets_cap = 0; - udt_name_buckets = lnk_udt_name_hash_table_from_leaf_arr(tp, tp_arena, leaf_count[CV_TypeIndexSource_TPI], leaf_arr[CV_TypeIndexSource_TPI], &udt_name_buckets_cap); + udt_name_buckets = lnk_udt_name_hash_table_from_leaf_arr(tp, tp_arena, types.count[CV_TypeIndexSource_TPI], types.v[CV_TypeIndexSource_TPI], &udt_name_buckets_cap); ProfEnd(); ProfBegin("Convert CodeView types to RDIB Types"); LNK_ConvertTypesToRDI task = {0}; - MemoryCopyTyped(&task.leaf_count[0], &leaf_count[0], CV_TypeIndexSource_COUNT); - MemoryCopyTyped(&task.leaf_arr[0], &leaf_arr[0], CV_TypeIndexSource_COUNT); + MemoryCopyTyped(task.leaf_count, types.count, CV_TypeIndexSource_COUNT); + MemoryCopyTyped(task.leaf_arr, types.v, CV_TypeIndexSource_COUNT); task.type_cap = input.type_cap; task.udt_cap = input.udt_cap; task.variadic_type_ref = rdib_make_type_ref(scratch.arena, input.variadic_type); @@ -5322,7 +4549,7 @@ lnk_build_rad_debug_info(TP_Context *tp, task.rdib_types_params_lists = push_array(scratch.arena, RDIB_TypeChunkList, tp->worker_count); task.rdib_udt_members_lists = push_array(scratch.arena, RDIB_UDTMemberChunkList, tp->worker_count); task.rdib_enum_members_lists = push_array(scratch.arena, RDIB_UDTMemberChunkList, tp->worker_count); - task.ranges = tp_divide_work(scratch.arena, leaf_count[CV_TypeIndexSource_TPI], tp->worker_count); + task.ranges = tp_divide_work(scratch.arena, types.count[CV_TypeIndexSource_TPI], tp->worker_count); tp_for_parallel(tp, tp_arena, tp->worker_count, lnk_convert_types_to_rdi_task, &task); ProfEnd(); @@ -5405,8 +4632,8 @@ lnk_build_rad_debug_info(TP_Context *tp, task.image_sects = image_sects; task.obj_arr = obj_arr; task.debug_s_arr = debug_s_arr; - task.leaf_arr_count_ipi = leaf_count[CV_TypeIndexSource_IPI]; - task.leaf_arr_ipi = leaf_arr[CV_TypeIndexSource_IPI]; + task.leaf_arr_count_ipi = types.count[CV_TypeIndexSource_IPI]; + task.leaf_arr_ipi = types.v[CV_TypeIndexSource_IPI]; task.symbol_inputs = symbol_inputs; task.parsed_symbols = parsed_symbols; task.ipi_itype_range = itype_ranges[CV_TypeIndexSource_IPI]; @@ -5450,7 +4677,7 @@ lnk_build_rad_debug_info(TP_Context *tp, ProfEnd(); ProfBegin("Convert Symbols"); - tp_for_parallel(tp, tp_arena, total_symbol_input_count, lnk_convert_symbols_to_rdi_task, &task); + tp_for_parallel(tp, tp_arena, symbol_input_count, lnk_convert_symbols_to_rdi_task, &task); ProfEnd(); ProfBegin("Convert Inline Sites Line Tables"); diff --git a/src/linker/lnk_debug_info.h b/src/linker/lnk_debug_info.h index 76449044..eb8364d7 100644 --- a/src/linker/lnk_debug_info.h +++ b/src/linker/lnk_debug_info.h @@ -3,84 +3,89 @@ #pragma once +// --- Symbol Parsing Tasks ---------------------------------------------------- + +typedef struct +{ + LNK_Obj **obj_arr; + String8List *sect_list_arr; + CV_DebugS *debug_s_arr; +} LNK_ParseDebugSTaskData; + +typedef struct +{ + LNK_Obj **obj_arr; + String8Array *data_arr_arr; +} LNK_CheckDebugTSigTaskData; + +typedef struct +{ + LNK_Obj **obj_arr; + String8Array *data_arr_arr; + CV_DebugT *debug_t_arr; +} LNK_ParseDebugTTaskData; + // --- Code View Input --------------------------------------------------------- -typedef struct LNK_PchInfo -{ - CV_TypeIndex ti_lo; - CV_TypeIndex ti_hi; - U64 debug_p_obj_idx; -} LNK_PchInfo; - -typedef struct LNK_CodeViewSymbolsInput +typedef struct LNK_SymbolInput { U64 obj_idx; + CV_DebugS *debug_s; CV_SymbolList *symbol_list; String8 raw_symbols; -} LNK_CodeViewSymbolsInput; +} LNK_SymbolInput; -typedef struct LNK_CodeViewInput +typedef struct { - U64 count; - U64 internal_count; - U64 external_count; - U64 type_server_count; - String8 *type_server_path_arr; // [type_server_count] - String8 *type_server_data_arr; // [type_server_count] - U64List *ts_to_obj_arr; // [type_server_count] - LNK_Obj **obj_arr; // [count] - LNK_PchInfo *pch_arr; // [count] - CV_DebugS *debug_s_arr; // [count] - CV_DebugT *debug_p_arr; // [count] - CV_DebugT *debug_t_arr; // [count] - CV_DebugT *merged_debug_t_p_arr; // [count] + CV_TypeServerInfo ts_info; + U64 ts_idx; + String8 ts_path; + U64List obj_indices; +} LNK_TypeServer; +typedef struct LNK_TypeServerNode { LNK_TypeServer v; struct LNK_TypeServerNode *next; } LNK_TypeServerNode; +typedef struct LNK_TypeServerList { U64 count; LNK_TypeServerNode *first; LNK_TypeServerNode *last; } LNK_TypeServerList; +typedef struct LNK_TypeServerArray { U64 count; LNK_TypeServer *v; } LNK_TypeServerArray; - U64 total_symbol_input_count; - LNK_CodeViewSymbolsInput *symbol_inputs; // [total_symbol_input_count] - CV_SymbolListArray *parsed_symbols; // [count] - - LNK_Obj **internal_obj_arr; // [internal_count] - CV_DebugS *internal_debug_s_arr; // [internal_count] - CV_DebugT *internal_debug_t_arr; // [internal_count] - CV_DebugT *internal_debug_p_arr; // [internal_count] - U64 internal_total_symbol_input_count; - LNK_CodeViewSymbolsInput *internal_symbol_inputs; // [internal_total_symbol_input_count] - CV_SymbolListArray *internal_parsed_symbols; // [internal_count] - - LNK_Obj **external_obj_arr; // [external_count] - CV_DebugS *external_debug_s_arr; // [external_count] - CV_DebugT *external_debug_t_arr; // [external_count] - CV_DebugT *external_debug_p_arr; // [external_count] - U64 external_total_symbol_input_count; - LNK_CodeViewSymbolsInput *external_symbol_inputs; // [exteranl_total_symbol_input_count] - CV_SymbolListArray *external_parsed_symbols; // [external_count] - Rng1U64 **external_ti_ranges; // [type_server_count] - CV_DebugT **external_leaves; // [type_server_count] - U64 *external_obj_to_ts_idx_arr; // [external_count] - Rng1U64 external_obj_range; -} LNK_CodeViewInput; - -typedef struct LNK_MergedTypes +typedef struct { + CV_TypeIndex min_type_indices[CV_TypeIndexSource_COUNT]; U64 count[CV_TypeIndexSource_COUNT]; U8 **v [CV_TypeIndexSource_COUNT]; } LNK_MergedTypes; -// --- Leaf Ref ---------------------------------------------------------------- - -typedef enum -{ - LNK_LeafLocType_Internal, - LNK_LeafLocType_External, - LNK_LeafLocType_Count -} LNK_LeafLocType; - -#define LNK_LeafRefFlag_LocIdxExternal (1u << 31) -#define LNK_LeafRefFlag_LeafIdxIPI (1u << 31) typedef struct { - U32 enc_loc_idx; - U32 enc_leaf_idx; + LNK_IO_Flags io_flags; + U64 count; + LNK_Obj **obj_arr; + CV_DebugS *debug_s_arr; + CV_DebugT *debug_p_arr; + CV_DebugT *debug_t_arr; + CV_DebugH *debug_h_arr; + U64 *obj_to_ts; + CV_SymbolListArray *parsed_symbols; // [count] + + U32Array int_obj_indices; + U32Array ext_obj_indices; + U32Array debug_p_indices; + U32Array type_server_indices; + + Rng1U64 ts_obj_range; + LNK_TypeServerArray ts_arr; + B32 *is_type_server_discarded; // [ts_arr.count] + + CV_TypeIndex min_type_indices[CV_TypeIndexSource_COUNT]; + + U64 symbol_input_count; + LNK_SymbolInput *symbol_inputs; // [symbol_input_count] +} LNK_CodeViewInput; + +// --- Leaf Deduping Tasks ----------------------------------------------------- + +typedef struct +{ + U32 obj_idx; + U32 leaf_idx; } LNK_LeafRef; typedef struct LNK_LeafRange @@ -109,107 +114,43 @@ typedef struct LNK_LeafRef **bucket_arr; } LNK_LeafHashTable; -typedef union -{ - struct { - U64 ***internal_hashes; - U64 ***external_hashes; - }; - U64 ***v[CV_TypeIndexSource_COUNT]; -} LNK_LeafHashes; - -// --- Symbol Parsing Tasks ---------------------------------------------------- - -typedef struct -{ - LNK_Obj **obj_arr; - String8List *sect_list_arr; - CV_DebugS *debug_s_arr; -} LNK_ParseDebugSTaskData; - -typedef struct -{ - LNK_Obj **obj_arr; - String8Array *data_arr_arr; -} LNK_CheckDebugTSigTaskData; - -typedef struct -{ - LNK_Obj **obj_arr; - String8Array *data_arr_arr; - CV_DebugT *debug_t_arr; -} LNK_ParseDebugTTaskData; - -typedef struct -{ - String8Array data_arr; - MSF_Parsed **msf_parse_arr; -} LNK_MsfParsedFromDataTask; - -typedef struct -{ - CV_TypeServerInfo *ts_info_arr; - MSF_Parsed **msf_parse_arr; - Rng1U64 **external_ti_ranges; - CV_DebugT **external_leaves; - B8 *is_corrupted; -} LNK_GetExternalLeavesTask; - -// --- Leaf Deduping Tasks ----------------------------------------------------- - -typedef struct -{ - U64 loc_idx_bit_count_0; - U64 loc_idx_bit_count_1; - U64 loc_idx_bit_count_2; - U64 counts_max; - U32 **counts_arr; - Rng1U64 *ranges; - LNK_LeafRef **dst; - LNK_LeafRef **src; - U64 loc_idx_max; - U64 pass_idx; -} LNK_LeafRadixSortTask; - -typedef struct -{ - U32 *counts; - U32 *offsets; - LNK_LeafRef **dst; - LNK_LeafRef **src; - Rng1U64 *ranges; -} LNK_LeafLocRadixSortTask; - typedef struct { LNK_CodeViewInput *input; - LNK_LeafHashes *hashes; - Arena **fixed_arenas; - Rng1U64 *ranges; + CV_DebugS *debug_s_arr; + CV_SymbolList *symbol_list_arr; LNK_LeafHashTable leaf_ht_arr[CV_TypeIndexSource_COUNT]; - CV_DebugT *debug_t_arr; + Arena **fixed_arenas; CV_TypeIndexSource ti_source; + U32Array indices; + Rng1U64 *ranges; // count types per source LNK_LeafRangeList *leaf_ranges_per_task; - U64 internal_per_source_count[CV_TypeIndexSource_COUNT]; - U64 external_per_source_count[CV_TypeIndexSource_COUNT]; + U64 per_source_count[CV_TypeIndexSource_COUNT]; // extract present buckets U64 *counts [CV_TypeIndexSource_COUNT]; U64 *offsets[CV_TypeIndexSource_COUNT]; + // leaf ref radix sort + U64 obj_idx_bit_count_0; + U64 obj_idx_bit_count_1; + U64 obj_idx_bit_count_2; + U64 counts_max; + U32 **counts_arr; + LNK_LeafRef **dst; + LNK_LeafRef **src; + U64 pass_idx; + // assign type indices U64 assigned_type_caps [CV_TypeIndexSource_COUNT]; CV_TypeIndex *assigned_type_hts [CV_TypeIndexSource_COUNT]; CV_TypeIndex min_type_indices [CV_TypeIndexSource_COUNT]; LNK_LeafRefArray unique_leaf_refs_arr[CV_TypeIndexSource_COUNT]; - CV_SymbolList *symbol_list_arr; - CV_DebugS *debug_s_arr; - - LNK_MergedTypes merged_types; -} LNK_CvImportTypes; + LNK_MergedTypes result; +} LNK_MergeTypes; // --- Code View Processing Trasks --------------------------------------------- @@ -226,16 +167,16 @@ typedef struct typedef struct { - LNK_CodeViewSymbolsInput *inputs; + LNK_SymbolInput *inputs; } LNK_ParseCVSymbolsTaskData; typedef struct { - U64 total_symbol_input_count; + U64 symbol_input_count; CV_SymbolListArray *parsed_symbols; U64 *serialized_symbol_data_sizes; - LNK_CodeViewSymbolsInput *symbol_inputs; + LNK_SymbolInput *symbol_inputs; PDB_DbiModule **mod_arr; String8List *symbol_data_arr; CV_SymbolList *gsi_list_arr; @@ -298,7 +239,7 @@ typedef struct { CV_TypeIndex ipi_min_type_index; CV_DebugT ipi_types; - LNK_CodeViewSymbolsInput *symbol_inputs; + LNK_SymbolInput *symbol_inputs; CV_SymbolListArray *parsed_symbols; } LNK_PostProcessCvSymbolsTask; @@ -410,7 +351,7 @@ typedef struct CV_DebugS *debug_s_arr; U64 leaf_arr_count_ipi; U8 **leaf_arr_ipi; - LNK_CodeViewSymbolsInput *symbol_inputs; + LNK_SymbolInput *symbol_inputs; CV_SymbolListArray *parsed_symbols; Rng1U64 ipi_itype_range; Rng1U64 tpi_itype_range; @@ -449,35 +390,18 @@ typedef struct internal CV_DebugS * lnk_parse_debug_s_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8List *sect_list_arr); internal CV_DebugT * lnk_parse_debug_t_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8List *debug_t_list_arr); -internal LNK_PchInfo * lnk_setup_pch (Arena *arena, U64 obj_count, LNK_Obj **obj_arr, CV_DebugT *debug_t_arr, CV_DebugT *debug_p_arr, CV_SymbolListArray *parsed_symbols, String8List alt_pch_dirs); - internal LNK_CodeViewInput lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_flags, String8List lib_dir_list, String8List alt_pch_dirs, U64 objs_count, LNK_Obj **objs); -internal LNK_LeafRef lnk_leaf_ref (U32 idx, U32 leaf_idx); -internal LNK_LeafRef lnk_obj_leaf_ref (U32 obj_idx, U32 leaf_idx); -internal LNK_LeafRef lnk_ts_leaf_ref (CV_TypeIndexSource ti_source, U32 ts_idx, U32 leaf_idx); -internal LNK_LeafLocType lnk_loc_type_from_leaf_ref(LNK_LeafRef leaf_ref); -internal int lnk_leaf_ref_compare (LNK_LeafRef a, LNK_LeafRef b); - -internal LNK_LeafLocType lnk_loc_type_from_obj_idx (LNK_CodeViewInput *input, U64 obj_idx); -internal U64 lnk_loc_idx_from_obj_idx (LNK_CodeViewInput *input, U64 obj_idx); -internal CV_TypeIndex lnk_ti_lo_from_loc (LNK_CodeViewInput *input, LNK_LeafLocType loc_type, U64 loc_idx, CV_TypeIndexSource ti_source); -internal CV_TypeIndex lnk_ti_lo_from_leaf_ref (LNK_CodeViewInput *input, LNK_LeafRef leaf_ref); -internal String8 lnk_data_from_leaf_ref (LNK_CodeViewInput *input, LNK_LeafRef leaf_ref); -internal LNK_LeafRef lnk_leaf_ref_from_loc_idx_and_ti(LNK_CodeViewInput *input, LNK_LeafLocType loc_type, CV_TypeIndexSource ti_source, U64 loc_idx, CV_TypeIndex obj_ti); -internal B32 lnk_match_leaf_ref (LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafRef a, LNK_LeafRef b); - -// leaf hashing -internal U64 lnk_hash_cv_leaf (LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafLocType loc_type, U32 loc_idx, Rng1U64 *ti_ranges, CV_TypeIndex curr_ti, CV_Leaf leaf, CV_TypeIndexInfoList ti_info_list); -internal void lnk_hash_cv_leaf_deep(Arena *arena, LNK_CodeViewInput *input, Rng1U64 *ti_ranges, CV_DebugT *leaves, LNK_LeafHashes *hashes, CV_TypeIndexSource ti_source, CV_TypeIndex ti, LNK_LeafLocType loc_type, U32 loc_idx, CV_TypeIndexInfoList ti_info_list, CV_Leaf leaf); -internal U64 lnk_hash_from_leaf_ref(LNK_LeafHashes *hashes, LNK_LeafRef leaf_ref); - -// leaf hash table -internal LNK_LeafRef * lnk_leaf_hash_table_insert_or_update(LNK_LeafHashTable *leaf_ht, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, U64 hash, LNK_LeafRef *new_bucket); -internal LNK_LeafRef * lnk_leaf_hash_table_search(LNK_LeafHashTable *ht, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafRef leaf_ref); - -internal LNK_MergedTypes lnk_merge_types(TP_Context *tp, TP_Arena *tp_temp, LNK_CodeViewInput *input); -internal void lnk_replace_type_names_with_hashes(TP_Context *tp, TP_Arena *arena, U64 leaf_count, U8 **leaf_arr, LNK_TypeNameHashMode mode, U64 hash_length, String8 map_name); +// type merging +internal int lnk_leaf_ref_compare (LNK_LeafRef a, LNK_LeafRef b); +internal int lnk_leaf_ref_is_before (void *raw_a, void *raw_b); +internal B32 lnk_match_leaf_ref (LNK_CodeViewInput *input, LNK_LeafRef a, LNK_LeafRef b); +internal U64 lnk_hash_cv_leaf (LNK_CodeViewInput *input, LNK_LeafRef leaf_ref, CV_TypeIndexInfoList ti_info_list, B32 discard_cycles); +internal void lnk_hash_cv_leaf_deep (Arena *arena, LNK_CodeViewInput *input, LNK_LeafRef leaf_ref, CV_TypeIndexInfoList ti_info_list); +internal LNK_LeafRef * lnk_leaf_hash_table_insert_or_update(LNK_LeafHashTable *leaf_ht, LNK_CodeViewInput *input, CV_DebugH *hashes, U64 hash, LNK_LeafRef *new_bucket); +internal LNK_LeafRef * lnk_leaf_hash_table_search (LNK_LeafHashTable *ht, LNK_CodeViewInput *input, LNK_LeafRef leaf_ref); +internal LNK_MergedTypes lnk_merge_types (TP_Context *tp, TP_Arena *tp_temp, LNK_CodeViewInput *input); +internal void lnk_replace_type_names_with_hashes (TP_Context *tp, TP_Arena *arena, U64 leaf_count, U8 **leaf_arr, LNK_TypeNameHashMode mode, U64 hash_length, String8 map_name); // --- RAD Debug info ---------------------------------------------------------- @@ -501,11 +425,10 @@ lnk_build_rad_debug_info(TP_Context *tp, U64 obj_count, LNK_Obj **obj_arr, CV_DebugS *debug_s_arr, - U64 total_symbol_input_count, - LNK_CodeViewSymbolsInput *symbol_inputs, + U64 symbol_input_count, + LNK_SymbolInput *symbol_inputs, CV_SymbolListArray *parsed_symbols, - U64 leaf_count[CV_TypeIndexSource_COUNT], - U8 **leaf_arr [CV_TypeIndexSource_COUNT]); + LNK_MergedTypes types); // --- PDB --------------------------------------------------------------------- @@ -522,11 +445,10 @@ internal String8List lnk_build_pdb(TP_Context *tp, U64 obj_count, LNK_Obj **obj_arr, CV_DebugS *debug_s_arr, - U64 total_symbol_input_count, - LNK_CodeViewSymbolsInput *symbol_inputs, + U64 symbol_input_count, + LNK_SymbolInput *symbol_inputs, CV_SymbolListArray *parsed_symbols, - U64 leaf_count[CV_TypeIndexSource_COUNT], - U8 **leaf_arr [CV_TypeIndexSource_COUNT]); + LNK_MergedTypes types); // --- RAD Debug Info ---------------------------------------------------------- diff --git a/src/linker/pdb_ext/pdb_builder.c b/src/linker/pdb_ext/pdb_builder.c index 65835a4a..9f4e21e5 100644 --- a/src/linker/pdb_ext/pdb_builder.c +++ b/src/linker/pdb_ext/pdb_builder.c @@ -946,6 +946,39 @@ pdb_type_server_parse_from_data_v80(String8 data, PDB_TypeServerParse *parse) return error; } +internal B32 +pdb_extract_type_server_info(String8 raw_msf, MSF_RawStreamTable *st, MSF_StreamNumber sn, Rng1U64 *ti_range_out, Rng1U64 *leaf_range_out) +{ + Temp scratch = scratch_begin(0,0); + B32 is_ok = 0; + String8 version_data = msf_data_from_stream_number_ex(scratch.arena, raw_msf, st, sn, r1u64(0, sizeof(PDB_TpiVersion)), 8); + PDB_TpiVersion *version = str8_deserial_get_raw_ptr(version_data, 0, sizeof(*version)); + if (version) { + switch (*version) { + case PDB_TpiVersion_IMPV80: { + String8 header_size_data = msf_data_from_stream_number_ex(scratch.arena, raw_msf, st, sn, r1u64(sizeof(*version), sizeof(*version) + sizeof(U32)), 8); + U32 *header_size = str8_deserial_get_raw_ptr(header_size_data, 0, sizeof(*header_size)); + if (header_size && *header_size >= sizeof(PDB_TpiVersion) + sizeof(U32)) { + String8 header_data = msf_data_from_stream_number_ex(scratch.arena, raw_msf, st, sn, r1u64(0, *header_size), 8); + PDB_TpiHeader *header = str8_deserial_get_raw_ptr(header_data, 0, sizeof(*header)); + if (*header_size + header->leaf_data_size <= st->streams[sn].size) { + *ti_range_out = r1u64(header->ti_lo, header->ti_hi); + *leaf_range_out = r1u64(*header_size, *header_size + header->leaf_data_size); + is_ok = 1; + } else { + NotImplemented; // TODO: handle error + } + } else { + NotImplemented; // TODO: handle error + } + } break; + default: break; + } + } + scratch_end(scratch); + return is_ok; +} + internal PDB_OpenTypeServerError pdb_type_server_parse_from_data(String8 data, PDB_TypeServerParse *parse_out) { @@ -1027,42 +1060,42 @@ pdb_type_server_open_v80(MSF_Context *msf, MSF_StreamNumber sn, PDB_StringTable if (header.hash_vals.off + header.hash_vals.size > hash_stream_size) { goto exit; } - + ts = pdb_type_server_alloc(header.hash_bucket_count); - + // read & parse code view types String8 types_data = msf_stream_read_block(ts->arena, msf, sn, header.leaf_data_size); CV_DebugT debug_t = cv_debug_t_from_data(scratch.arena, types_data, PDB_LEAF_ALIGN); - + // read hash data U8 *hash_buffer = push_array(scratch.arena, U8, header.hash_vals.size); msf_stream_seek(msf, header.hash_sn, header.hash_vals.off); MSF_UInt hash_buffer_size = msf_stream_read(msf, header.hash_sn, hash_buffer, header.hash_vals.size); Assert(hash_buffer_size == header.hash_vals.size); - + // rebuild type buckets for (U64 cursor = 0, leaf_idx = 0; - cursor + header.hash_key_size <= hash_buffer_size; - cursor += header.hash_key_size, leaf_idx += 1) { - String8 raw_leaf = cv_debug_t_get_raw_leaf(debug_t, leaf_idx); + cursor + header.hash_key_size <= hash_buffer_size; + cursor += header.hash_key_size, leaf_idx += 1) { + String8 raw_leaf = cv_debug_t_get_raw_leaf(&debug_t, leaf_idx); str8_list_push(ts->arena, &ts->leaf_list, raw_leaf); - + // read out bucket hash U64 hash = 0; MemoryCopy(&hash, hash_buffer + cursor, header.hash_key_size); - + // push bucket PDB_TypeBucket *bucket = push_array(ts->arena, PDB_TypeBucket, 1); bucket->raw_leaf = raw_leaf; bucket->type_index = header.ti_lo + leaf_idx; SLLStackPush(ts->buckets[hash], bucket); } - + // adjust type buckets msf_stream_seek(msf, header.hash_sn, header.hash_adj.off); String8 adjust_data = msf_stream_read_block(scratch.arena, msf, header.hash_sn, header.hash_adj.size); - + // open adjust hash table PDB_HashTableParseError hash_adj_parse_error = pdb_hash_adj_hash_table_from_data(&ts->hash_adj, adjust_data, strtab, 0); if (hash_adj_parse_error == PDB_HashTableParseError_OUT_OF_BYTES) { @@ -1075,7 +1108,7 @@ pdb_type_server_open_v80(MSF_Context *msf, MSF_StreamNumber sn, PDB_StringTable String8Array key_arr = {0}; String8Array value_arr = {0}; pdb_hash_table_get_present_keys_and_values(scratch.arena, &ts->hash_adj, &key_arr, &value_arr); - + // adjust type buckets for (U64 i = 0; i < ts->hash_adj.count; i += 1) { String8 type_name = key_arr.v[i]; @@ -1084,7 +1117,7 @@ pdb_type_server_open_v80(MSF_Context *msf, MSF_StreamNumber sn, PDB_StringTable // name -> hash U64 hash = pdb_hash_v1(type_name); hash %= ts->bucket_cap; - + // search for type bucket PDB_TypeBucket *curr, *prev; for (curr = ts->buckets[hash], prev = 0; curr != 0; prev = curr, curr = curr->next) { @@ -1092,18 +1125,18 @@ pdb_type_server_open_v80(MSF_Context *msf, MSF_StreamNumber sn, PDB_StringTable break; } } - + // move type to the head if (prev && curr) { prev->next = curr->next; curr->next = ts->buckets[hash]; ts->buckets[hash] = curr; } - + Assert(curr); } - - exit:; + +exit: scratch_end(scratch); ProfEnd(); return ts; diff --git a/src/msf/msf_parse.c b/src/msf/msf_parse.c index 23f0df63..40c84813 100644 --- a/src/msf/msf_parse.c +++ b/src/msf/msf_parse.c @@ -227,51 +227,63 @@ msf_raw_stream_table_from_data(Arena *arena, String8 msf_data) } internal String8 -msf_data_from_stream_number(Arena *arena, String8 msf_data, MSF_RawStreamTable *st, MSF_StreamNumber sn) +msf_data_from_stream_number_ex(Arena *arena, String8 msf_data, MSF_RawStreamTable *st, MSF_StreamNumber sn, Rng1U64 range, U64 align) { ProfBeginFunction(); String8 result = {0}; if(sn < st->stream_count) { - MSF_RawStream stream = st->streams[sn]; - U8 *stream_buf = push_array_no_zero(arena, U8, stream.size); - U8 *stream_out_ptr = stream_buf; - for (U32 i = 0; i < stream.page_count; ++i) { - U64 page_idx; - if (st->index_size == 4) { - page_idx = stream.u.page_indices_u32[i]; - } else { - page_idx = stream.u.page_indices_u16[i]; + MSF_RawStream stream = st->streams[sn]; + Rng1U64 range_clamped = { .min = Min(range.min, stream.size), .max = Min(range.max, stream.size) }; + U64 page_count = CeilIntegerDiv(dim_1u64(range_clamped), st->page_size); + U64 pn_base = range_clamped.min / st->page_size; + + result = (String8){ .str = push_array_aligned(arena, U8, dim_1u64(range_clamped), align) }; + + for EachIndex(page_idx, page_count) { + U64 pn_begin; + if (st->index_size == 4) { pn_begin = stream.u.page_indices_u32[pn_base + page_idx]; } + else { pn_begin = stream.u.page_indices_u16[pn_base + page_idx]; } + + U64 pn_end = pn_begin; + for (; page_idx+1 < page_count; page_idx += 1) { + U64 next_pn = 0; + if (st->index_size == 4) { next_pn = stream.u.page_indices_u32[pn_base + page_idx + 1]; } + else { next_pn = stream.u.page_indices_u16[pn_base + page_idx + 1]; } + if ((pn_end + 1) != next_pn) { break; } + pn_end = next_pn; } - - U64 stream_page_off = (U64)page_idx * st->page_size; - if (stream_page_off + st->page_size > msf_data.size) { - break; - } - - U8 *stream_page_base = msf_data.str + stream_page_off; - - // clamp copy size by end of stream - U32 stream_pos = (U32) (stream_out_ptr - stream_buf); - U32 remaining_size = stream.size - stream_pos; - U32 copy_size = ClampTop(st->page_size, remaining_size); - + pn_end += 1; + + U64 read_off = result.size + range_clamped.min; + U64 to_read = dim_1u64(range_clamped) - result.size; + U64 read_size = Min(to_read, (pn_end - pn_begin) * st->page_size - (read_off % st->page_size)); + + U64 page_off = (pn_begin * st->page_size) + (read_off % st->page_size); + String8 page = str8_substr(msf_data, r1u64(page_off, page_off + read_size)); + if (page.size != read_size) { break; } + // copy page data - MemoryCopy(stream_out_ptr, stream_page_base, copy_size); - stream_out_ptr += copy_size; + Assert(result.size + read_size <= dim_1u64(range_clamped)); + U8 *ptr = result.str + result.size; + MemoryCopy(ptr, page.str, read_size); + result.size += read_size; } - - U64 copy_size = (U64)(stream_out_ptr - stream_buf); - - U64 unused_buf_size = stream.size - copy_size; + + // release unused bytes + U64 unused_buf_size = dim_1u64(range_clamped) - result.size; arena_pop(arena, unused_buf_size); - - result = str8(stream_buf, copy_size); } ProfEnd(); return result; } +internal String8 +msf_data_from_stream_number(Arena *arena, String8 msf_data, MSF_RawStreamTable *st, MSF_StreamNumber sn) +{ + return msf_data_from_stream_number_ex(arena, msf_data, st, sn, r1u64(0, max_U64), 8); +} + internal MSF_Parsed * msf_parsed_from_data(Arena *arena, String8 msf_data) { diff --git a/src/msf/msf_parse.h b/src/msf/msf_parse.h index df4afb85..afd1c3c2 100644 --- a/src/msf/msf_parse.h +++ b/src/msf/msf_parse.h @@ -41,6 +41,8 @@ struct MSF_Parsed //~ rjf: MSF Parser Functions internal MSF_RawStreamTable* msf_raw_stream_table_from_data(Arena *arena, String8 msf_data); +internal U64 msf_size_from_stream_number(MSF_RawStreamTable *st, MSF_StreamNumber sn); +internal String8 msf_data_from_stream_number_ex(Arena *arena, String8 msf_data, MSF_RawStreamTable *st, MSF_StreamNumber sn, Rng1U64 range, U64 align); internal String8 msf_data_from_stream_number(Arena *arena, String8 msf_data, MSF_RawStreamTable *st, MSF_StreamNumber sn); internal MSF_Parsed* msf_parsed_from_data(Arena *arena, String8 msf_data); internal String8 msf_data_from_stream(MSF_Parsed *msf, MSF_StreamNumber sn); diff --git a/src/torture/torture_radlink.c b/src/torture/torture_radlink.c index 55144234..022d0a2e 100644 --- a/src/torture/torture_radlink.c +++ b/src/torture/torture_radlink.c @@ -3943,6 +3943,67 @@ T_EndTest; T_BeginTest(merge_duplicate_types) { { + U32 pch_sig = 0xCAFEBABE; + + String8 debug_s; + { + String8List srl; + str8_serial_begin(scratch.arena, &srl); + + CV_Signature sig = CV_Signature_C13; + str8_serial_push_struct(scratch.arena, &srl, &sig); + + CV_C13SubSectionHeader *ss_header = str8_serial_push_size(scratch.arena, &srl, sizeof(*ss_header)); + U64 ss_start_off = srl.total_size; + + CV_SymObjName obj_name = {0}; + obj_name.sig = pch_sig; + String8 obj_name_string = str8_lit("a.obj"); + str8_serial_push_u16(scratch.arena, &srl, sizeof(CV_SymKind) + sizeof(obj_name) + obj_name_string.size + 1); + str8_serial_push_u16(scratch.arena, &srl, CV_SymKind_OBJNAME); + str8_serial_push_struct(scratch.arena, &srl, &obj_name); + str8_serial_push_cstr(scratch.arena, &srl, obj_name_string); + str8_serial_push_align(scratch.arena, &srl, CV_SymbolAlign); + + ss_header->kind = CV_C13SubSectionKind_Symbols; + ss_header->size = srl.total_size - ss_start_off; + str8_serial_push_align(scratch.arena, &srl, CV_C13SubSectionAlign); + + debug_s = str8_serial_end(scratch.arena, &srl); + } + String8 debug_p; + { + String8List srl; + str8_serial_begin(scratch.arena, &srl); + + // signature + CV_Signature sig = CV_Signature_C13; + str8_serial_push_struct(scratch.arena, &srl, &sig); + + // duplicate in a.obj + CV_LeafPointer ptr = { .itype = CV_BasicType_VOID }; + str8_serial_push_u16(scratch.arena, &srl, sizeof(CV_LeafKind) + sizeof(ptr)); + str8_serial_push_u16(scratch.arena, &srl, CV_LeafKind_POINTER); + str8_serial_push_struct(scratch.arena, &srl, &ptr); + str8_serial_push_align(scratch.arena, &srl, CV_LeafAlign); + + // unique procedure type + CV_LeafProcedure proc = { .ret_itype = 0x1000, .call_kind = CV_CallKind_NearPascal }; + str8_serial_push_u16(scratch.arena, &srl, sizeof(CV_LeafKind) + sizeof(proc)); + str8_serial_push_u16(scratch.arena, &srl, CV_LeafKind_PROCEDURE); + str8_serial_push_struct(scratch.arena, &srl, &proc); + str8_serial_push_align(scratch.arena, &srl, CV_LeafAlign); + + // PCH ender + CV_LeafEndPreComp endprecomp = { .sig = pch_sig }; + str8_serial_push_u16(scratch.arena, &srl, sizeof(CV_LeafKind) + sizeof(endprecomp)); + str8_serial_push_u16(scratch.arena, &srl, CV_LeafKind_ENDPRECOMP); + str8_serial_push_struct(scratch.arena, &srl, &endprecomp); + str8_serial_push_align(scratch.arena, &srl, CV_LeafAlign); + + debug_p = str8_serial_end(scratch.arena, &srl); + } + String8 debug_t; { String8List srl; @@ -3951,17 +4012,25 @@ T_BeginTest(merge_duplicate_types) CV_Signature sig = CV_Signature_C13; str8_serial_push_struct(scratch.arena, &srl, &sig); + CV_LeafPreComp precomp = { .start_index = CV_MinComplexTypeIndex, .count = 3, sig = pch_sig }; + String8 pch_obj_name = str8_lit("pch.obj"); + str8_serial_push_u16(scratch.arena, &srl, sizeof(CV_LeafKind) + sizeof(CV_LeafPreComp) + pch_obj_name.size + 1); + str8_serial_push_u16(scratch.arena, &srl, CV_LeafKind_PRECOMP); + str8_serial_push_struct(scratch.arena, &srl, &precomp); + str8_serial_push_cstr(scratch.arena, &srl, pch_obj_name); + str8_serial_push_align(scratch.arena, &srl, CV_LeafAlign); + CV_LeafPointer ptr = { .itype = CV_BasicType_VOID }; str8_serial_push_u16(scratch.arena, &srl, sizeof(CV_LeafKind) + sizeof(CV_LeafPointer)); str8_serial_push_u16(scratch.arena, &srl, CV_LeafKind_POINTER); str8_serial_push_struct(scratch.arena, &srl, &ptr); - str8_serial_push_align(scratch.arena, &srl, 4); + str8_serial_push_align(scratch.arena, &srl, CV_LeafAlign); CV_LeafProcedure proc = { .ret_itype = 0x1000, .call_kind = CV_CallKind_NearC }; str8_serial_push_u16(scratch.arena, &srl, sizeof(CV_LeafKind) + sizeof(CV_LeafProcedure)); str8_serial_push_u16(scratch.arena, &srl, CV_LeafKind_PROCEDURE); str8_serial_push_struct(scratch.arena, &srl, &proc); - str8_serial_push_align(scratch.arena, &srl, 4); + str8_serial_push_align(scratch.arena, &srl, CV_LeafAlign); debug_t = str8_serial_end(scratch.arena, &srl); } @@ -3978,17 +4047,25 @@ T_BeginTest(merge_duplicate_types) str8_serial_push_u16(scratch.arena, &srl, sizeof(CV_LeafKind) + sizeof(CV_LeafPointer)); str8_serial_push_u16(scratch.arena, &srl, CV_LeafKind_POINTER); str8_serial_push_struct(scratch.arena, &srl, &ptr); - str8_serial_push_align(scratch.arena, &srl, 4); + str8_serial_push_align(scratch.arena, &srl, CV_LeafAlign); CV_LeafProcedure proc = { .ret_itype = 0x1000, .call_kind = CV_CallKind_NearC }; str8_serial_push_u16(scratch.arena, &srl, sizeof(CV_LeafKind) + sizeof(CV_LeafProcedure)); str8_serial_push_u16(scratch.arena, &srl, CV_LeafKind_PROCEDURE); str8_serial_push_struct(scratch.arena, &srl, &proc); - str8_serial_push_align(scratch.arena, &srl, 4); + str8_serial_push_align(scratch.arena, &srl, CV_LeafAlign); same_but_different = str8_serial_end(scratch.arena, &srl); } + String8 pch_obj; + { + COFF_ObjWriter *cow = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_section(cow, str8_lit(".debug$P"), PE_DEBUG_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, debug_p); + coff_obj_writer_push_section(cow, str8_lit(".debug$S"), PE_DEBUG_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, debug_s); + pch_obj = coff_obj_writer_serialize(scratch.arena, cow); + } + String8 a_obj; { COFF_ObjWriter *cow = coff_obj_writer_alloc(0, COFF_MachineType_X64); @@ -4013,11 +4090,12 @@ T_BeginTest(merge_duplicate_types) String8 entry_obj = t_make_entry_obj(scratch.arena); T_Ok(t_write_file(str8_lit("entry.obj"), entry_obj)); + T_Ok(t_write_file(str8_lit("pch.obj"), pch_obj)); T_Ok(t_write_file(str8_lit("a.obj"), a_obj)); T_Ok(t_write_file(str8_lit("b.obj"), b_obj)); T_Ok(t_write_file(str8_lit("c.obj"), c_obj)); - t_invoke_linkerf("/subsystem:console /entry:entry /debug:full /out:a.exe a.obj b.obj c.obj entry.obj"); + t_invoke_linkerf("/subsystem:console /entry:entry /debug:full /out:a.exe a.obj b.obj c.obj pch.obj entry.obj"); T_Ok(g_last_exit_code == 0); }