diff --git a/project.4coder b/project.4coder index 865b05ec..26b9969f 100644 --- a/project.4coder +++ b/project.4coder @@ -49,7 +49,7 @@ commands = // .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, - .f1 = { .win = "raddbg_stable --ipc kill_all && build ryan_scratch no_meta", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, + .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin no_meta", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, //- rjf: [raddbg wsl] // .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, diff --git a/src/radbin/radbin_main.c b/src/radbin/radbin_main.c index 5a7e7bcf..3c1fb526 100644 --- a/src/radbin/radbin_main.c +++ b/src/radbin/radbin_main.c @@ -40,6 +40,7 @@ #include "arch/arch_inc.h" #include "rdi_from_coff/rdi_from_coff.h" #include "rdi_from_elf/rdi_from_elf.h" +#include "rdi_from_codeview/rdi_from_codeview.h" #include "rdi_from_pdb/rdi_from_pdb.h" #include "rdi_from_dwarf/rdi_from_dwarf.h" #include "radbin/radbin.h" @@ -75,6 +76,7 @@ #include "rdi_from_coff/rdi_from_coff.c" #include "rdi_from_elf/rdi_from_elf.c" #include "rdi_from_pdb/rdi_from_pdb.c" +#include "rdi_from_codeview/rdi_from_codeview.c" #include "rdi_from_dwarf/rdi_from_dwarf.c" #include "radbin/radbin.c" diff --git a/src/rdi_from_codeview/rdi_from_codeview.c b/src/rdi_from_codeview/rdi_from_codeview.c new file mode 100644 index 00000000..73641b60 --- /dev/null +++ b/src/rdi_from_codeview/rdi_from_codeview.c @@ -0,0 +1,5075 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Basic Helpers + +internal U64 +cv2r_end_of_cplusplus_container_name(String8 str) +{ + // NOTE: This finds the index one past the last "::" contained in str. + // if no "::" is contained in str, then the returned index is 0. + // The intent is that [0,clamp_bot(0,result - 2)) gives the + // "container name" and [result,str.size) gives the leaf name. + U64 result = 0; + if(str.size >= 2) + { + S64 template_nest_depth = 0; + for(U64 i = str.size; i >= 2; i -= 1) + { + if(template_nest_depth == 0 && str.str[i - 2] == ':' && str.str[i - 1] == ':') + { + result = i; + break; + } + if(str.str[i-1] == '>') + { + template_nest_depth += 1; + } + else if(str.str[i-1] == '<') + { + template_nest_depth -= 1; + template_nest_depth = ClampBot(template_nest_depth, 0); + } + } + } + return result; +} + +internal U64 +cv2r_hash_from_voff(U64 voff) +{ + U64 hash = (voff >> 3) ^ ((7 & voff) << 6); + return hash; +} + +internal int +cv2r_namespace_node_is_before(void *raw_a, void *raw_b) +{ + CV2R_NamespaceNode **a = (CV2R_NamespaceNode **)raw_a, **b = (CV2R_NamespaceNode **)raw_b; + return (str8_compar(a[0]->string, b[0]->string, 0) < 0); +} + +internal U64 +cv2r_voff_from_soff(CV2R_Section *sections, U64 sections_count, U64 sec_num, U64 soff) +{ + U64 result = 0; + if(0 < sec_num && sec_num <= sections_count) + { + result = sections[sec_num-1].voff + soff; + } + return result; +} + +//////////////////////////////// +//~ rjf: String Table Functions + +internal String8 +cv2r_string_from_off(CV2R_StringTable *strtbl, U64 off) +{ + U32 strblock_max = strtbl->strblock_max; + U32 full_off_raw = strtbl->strblock_min + off; + U32 full_off = ClampTop(full_off_raw, strblock_max); + String8 result = str8_cstring_capped((char*)(strtbl->data.str + full_off), + (char*)(strtbl->data.str + strblock_max)); + return result; +} + +//////////////////////////////// +//~ rjf: Compilation Unit Contribution Functions + +internal CV2R_CompUnitContribution * +cv2r_comp_unit_contribution_from_voff__binary_search(CV2R_CompUnitContribution *contributions, U64 contributions_count, U64 voff) +{ + CV2R_CompUnitContribution *result = 0; + if(contributions_count != 0) + { + U64 first_idx = 0; + U64 last_idx = contributions_count-1; + for(;first_idx < last_idx;) + { + U64 mid_idx = (last_idx + first_idx) / 2; + U64 mid_voff_first = contributions[mid_idx].voff_first; + U64 mid_voff_opl = contributions[mid_idx].voff_opl; + if(voff < mid_voff_first) + { + last_idx = mid_idx; + } + else if(mid_voff_opl <= voff) + { + first_idx = mid_idx+1; + } + else + { + result = &contributions[mid_idx]; + break; + } + } + } + return result; +} + +//////////////////////////////// +//~ rjf: TPI Hash Table Functions + +internal U32 +cv2r_tpi_hash_from_data(String8 string) +{ + U32 result = 0; + U8 *ptr = string.str; + for(U8 *opl = ptr + (string.size & (~3)); ptr < opl; ptr += 4) + { + result ^= memory_read32(ptr); + } + if((string.size & 2) != 0) + { + result ^= memory_read16(ptr); ptr += 2; + } + if((string.size & 1) != 0) + { + result ^= memory_read8(ptr); + } + result |= 0x20202020; + result ^= (result >> 11); + result ^= (result >> 16); + return result; +} + +internal CV_TypeIdArray +cv2r_itypes_from_name(Arena *arena, CV2R_TPIHash *tpi_hash, CV_LeafParsed *leaf, String8 name, B32 compare_unique_name, U32 output_cap) +{ + CV_TypeIdArray result = {0}; + if(tpi_hash->bucket_count != 0) + { + Temp scratch = scratch_begin(&arena, 1); + U32 hash = cv2r_tpi_hash_from_data(name); + U32 bucket_idx = ((tpi_hash->bucket_mask != 0) ? + hash&tpi_hash->bucket_mask : + hash%tpi_hash->bucket_count); + CV_TypeId itype_first = leaf->itype_first; + CV_TypeId itype_opl = leaf->itype_opl; + String8 data = leaf->data; + struct Chain + { + struct Chain *next; + CV_TypeId itype; + }; + struct Chain *first = 0; + struct Chain *last = 0; + U32 count = 0; + + for(CV2R_TPIHashBlock *block = tpi_hash->buckets[bucket_idx]; + block != 0; + block = block->next) + { + U32 local_count = block->local_count; + CV_TypeId *itype_ptr = block->itypes; + for (U32 i = 0; i < local_count; i += 1, itype_ptr += 1){ + + String8 extracted_name = {0}; + + CV_TypeId itype = *itype_ptr; + if (itype_first <= itype && itype < itype_opl){ + CV_RecRange *range = &leaf->leaf_ranges.ranges[itype - leaf->itype_first]; + if (range->off + range->hdr.size <= data.size){ + U8 *first = data.str + range->off + 2; + U64 cap = range->hdr.size - 2; + + switch (range->hdr.kind){ + default:break; + + case CV_LeafKind_CLASS: + case CV_LeafKind_STRUCTURE: + { + if (sizeof(CV_LeafStruct) <= cap){ + CV_LeafStruct *lf_struct = (CV_LeafStruct*)first; + + if (!(lf_struct->props & CV_TypeProp_FwdRef)){ + // size + U8 *numeric_ptr = (U8*)(lf_struct + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap); + + // name + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap)); + + // unique name + if (compare_unique_name){ + if (lf_struct->props & CV_TypeProp_HasUniqueName) { + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap)); + extracted_name = unique_name; + } + } + else{ + extracted_name = name; + } + } + } + }break; + + case CV_LeafKind_CLASS2: + case CV_LeafKind_STRUCT2: + { + if (sizeof(CV_LeafStruct2) <= cap){ + CV_LeafStruct2 *lf_struct = (CV_LeafStruct2*)first; + + if (!(lf_struct->props & CV_TypeProp_FwdRef)){ + // size + U8 *numeric_ptr = (U8*)(lf_struct + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap); + + // name + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap)); + + // unique name + if (compare_unique_name){ + if (lf_struct->props & CV_TypeProp_HasUniqueName) { + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap)); + extracted_name = unique_name; + } + } + else{ + extracted_name = name; + } + } + } + }break; + + case CV_LeafKind_UNION: + { + if (sizeof(CV_LeafUnion) <= cap){ + CV_LeafUnion *lf_union = (CV_LeafUnion*)first; + + if (!(lf_union->props & CV_TypeProp_FwdRef)){ + // size + U8 *numeric_ptr = (U8*)(lf_union + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap); + + // name + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap)); + + // unique name + if (compare_unique_name){ + if (lf_union->props & CV_TypeProp_HasUniqueName) { + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap)); + extracted_name = unique_name; + } + } + else{ + extracted_name = name; + } + } + } + }break; + + case CV_LeafKind_ENUM: + { + if (sizeof(CV_LeafEnum) <= cap){ + CV_LeafEnum *lf_enum = (CV_LeafEnum*)first; + + if (!(lf_enum->props & CV_TypeProp_FwdRef)){ + // name + U8 *name_ptr = (U8*)(lf_enum + 1); + String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap)); + + // unique name + if (compare_unique_name){ + if (lf_enum->props & CV_TypeProp_HasUniqueName) { + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap)); + extracted_name = unique_name; + } + } + else{ + extracted_name = name; + } + } + } + }break; + } + } + } + + if (str8_match(extracted_name, name, 0)){ + struct Chain *chain = push_array(scratch.arena, struct Chain, 1); + SLLQueuePush(first, last, chain); + count += 1; + chain->itype = itype; + if (count == output_cap){ + goto dblbreak; + } + } + } + } + + dblbreak:; + + + // assemble result + CV_TypeId *itypes = push_array_aligned(arena, CV_TypeId, count, 8); + { + CV_TypeId *itype_ptr = itypes; + for (struct Chain *node = first; + node != 0; + node = node->next, itype_ptr += 1){ + *itype_ptr = node->itype; + } + } + result.itypes = itypes; + result.count = count; + + scratch_end(scratch); + } + return result; +} + +internal CV_TypeId +cv2r_first_itype_from_name(CV2R_TPIHash *tpi_hash, CV_LeafParsed *tpi_leaf, String8 name, B32 compare_unique_name) +{ + +} + +//////////////////////////////// +//~ rjf: CodeView => RDI Canonical Conversions + +internal RDI_Arch +cv2r_rdi_arch_from_cv_arch(CV_Arch arch) +{ + RDI_Arch result = 0; + switch(arch) + { + case CV_Arch_8086:{}break; + case CV_Arch_X64:{result = RDI_Arch_X64;}break; + //case CV_Arch_8080: break; + //case CV_Arch_80286: break; + //case CV_Arch_80386: break; + //case CV_Arch_80486: break; + //case CV_Arch_PENTIUM: break; + //case CV_Arch_PENTIUMII: break; + //case CV_Arch_PENTIUMIII: break; + //case CV_Arch_MIPS: break; + //case CV_Arch_MIPS16: break; + //case CV_Arch_MIPS32: break; + //case CV_Arch_MIPS64: break; + //case CV_Arch_MIPSI: break; + //case CV_Arch_MIPSII: break; + //case CV_Arch_MIPSIII: break; + //case CV_Arch_MIPSIV: break; + //case CV_Arch_MIPSV: break; + //case CV_Arch_M68000: break; + //case CV_Arch_M68010: break; + //case CV_Arch_M68020: break; + //case CV_Arch_M68030: break; + //case CV_Arch_M68040: break; + //case CV_Arch_ALPHA: break; + //case CV_Arch_ALPHA_21164: break; + //case CV_Arch_ALPHA_21164A: break; + //case CV_Arch_ALPHA_21264: break; + //case CV_Arch_ALPHA_21364: break; + //case CV_Arch_PPC601: break; + //case CV_Arch_PPC603: break; + //case CV_Arch_PPC604: break; + //case CV_Arch_PPC620: break; + //case CV_Arch_PPCFP: break; + //case CV_Arch_PPCBE: break; + //case CV_Arch_SH3: break; + //case CV_Arch_SH3E: break; + //case CV_Arch_SH3DSP: break; + //case CV_Arch_SH4: break; + //case CV_Arch_SHMEDIA: break; + //case CV_Arch_ARM3: break; + //case CV_Arch_ARM4: break; + //case CV_Arch_ARM4T: break; + //case CV_Arch_ARM5: break; + //case CV_Arch_ARM5T: break; + //case CV_Arch_ARM6: break; + //case CV_Arch_ARM_XMAC: break; + //case CV_Arch_ARM_WMMX: break; + //case CV_Arch_ARM7: break; + //case CV_Arch_OMNI: break; + //case CV_Arch_IA64_1: break; + //case CV_Arch_IA64_2: break; + //case CV_Arch_CEE: break; + //case CV_Arch_AM33: break; + //case CV_Arch_M32R: break; + //case CV_Arch_TRICORE: break; + //case CV_Arch_EBC: break; + //case CV_Arch_THUMB: break; + //case CV_Arch_ARMNT: break; + //case CV_Arch_ARM64: break; + //case CV_Arch_D3D11_SHADER: break; + } + return result; +} + +internal RDI_RegCode +cv2r_rdi_reg_code_from_cv_reg_code(RDI_Arch arch, CV_Reg reg_code) +{ + RDI_RegCode result = 0; + switch(arch) + { + case RDI_Arch_X64: + { + switch(reg_code) + { +#define X(CVN,C,RDN,BP,BZ) case C: result = RDI_RegCodeX64_##RDN; break; + CV_Reg_X64_XList(X) +#undef X + } + }break; + } + return(result); +} + +internal RDI_Language +cv2r_rdi_language_from_cv_language(CV_Language language) +{ + RDI_Language result = 0; + switch(language) + { + case CV_Language_C: result = RDI_Language_C; break; + case CV_Language_CXX: result = RDI_Language_CPlusPlus; break; + //case CV_Language_FORTRAN: result = ; break; + //case CV_Language_MASM: result = ; break; + //case CV_Language_PASCAL: result = ; break; + //case CV_Language_BASIC: result = ; break; + //case CV_Language_COBOL: result = ; break; + //case CV_Language_LINK: result = ; break; + //case CV_Language_CVTRES: result = ; break; + //case CV_Language_CVTPGD: result = ; break; + //case CV_Language_CSHARP: result = ; break; + //case CV_Language_VB: result = ; break; + //case CV_Language_ILASM: result = ; break; + //case CV_Language_JAVA: result = ; break; + //case CV_Language_JSCRIPT: result = ; break; + //case CV_Language_MSIL: result = ; break; + //case CV_Language_HLSL: result = ; break; + } + return(result); +} + +internal RDI_TypeKind +cv2r_rdi_type_kind_from_cv_basic_type(CV_BasicType basic_type) +{ + RDI_TypeKind result = RDI_TypeKind_Null; + switch(basic_type) + { + case CV_BasicType_VOID: {result = RDI_TypeKind_Void;}break; + case CV_BasicType_HRESULT: {result = RDI_TypeKind_HResult;}break; + + case CV_BasicType_RCHAR: + case CV_BasicType_CHAR: + case CV_BasicType_CHAR8: + {result = RDI_TypeKind_Char8;}break; + + case CV_BasicType_UCHAR: {result = RDI_TypeKind_UChar8;}break; + case CV_BasicType_WCHAR: {result = RDI_TypeKind_UChar16;}break; + case CV_BasicType_CHAR16: {result = RDI_TypeKind_Char16;}break; + case CV_BasicType_CHAR32: {result = RDI_TypeKind_Char32;}break; + + case CV_BasicType_BOOL8: + case CV_BasicType_INT8: + {result = RDI_TypeKind_S8;}break; + + case CV_BasicType_BOOL16: + case CV_BasicType_INT16: + case CV_BasicType_SHORT: + {result = RDI_TypeKind_S16;}break; + + case CV_BasicType_BOOL32: + case CV_BasicType_INT32: + case CV_BasicType_LONG: + {result = RDI_TypeKind_S32;}break; + + case CV_BasicType_BOOL64: + case CV_BasicType_INT64: + case CV_BasicType_QUAD: + {result = RDI_TypeKind_S64;}break; + + case CV_BasicType_INT128: + case CV_BasicType_OCT: + {result = RDI_TypeKind_S128;}break; + + case CV_BasicType_UINT8: {result = RDI_TypeKind_U8;}break; + + case CV_BasicType_UINT16: + case CV_BasicType_USHORT: + {result = RDI_TypeKind_U16;}break; + + case CV_BasicType_UINT32: + case CV_BasicType_ULONG: + {result = RDI_TypeKind_U32;}break; + + case CV_BasicType_UINT64: + case CV_BasicType_UQUAD: + {result = RDI_TypeKind_U64;}break; + + case CV_BasicType_UINT128: + case CV_BasicType_UOCT: + {result = RDI_TypeKind_U128;}break; + + case CV_BasicType_FLOAT16:{result = RDI_TypeKind_F16;}break; + case CV_BasicType_FLOAT32:{result = RDI_TypeKind_F32;}break; + case CV_BasicType_FLOAT32PP:{result = RDI_TypeKind_F32PP;}break; + case CV_BasicType_FLOAT48:{result = RDI_TypeKind_F48;}break; + case CV_BasicType_FLOAT64:{result = RDI_TypeKind_F64;}break; + case CV_BasicType_FLOAT80:{result = RDI_TypeKind_F80;}break; + case CV_BasicType_FLOAT128:{result = RDI_TypeKind_F128;}break; + case CV_BasicType_COMPLEX32:{result = RDI_TypeKind_ComplexF32;}break; + case CV_BasicType_COMPLEX64:{result = RDI_TypeKind_ComplexF64;}break; + case CV_BasicType_COMPLEX80:{result = RDI_TypeKind_ComplexF80;}break; + case CV_BasicType_COMPLEX128:{result = RDI_TypeKind_ComplexF128;}break; + case CV_BasicType_PTR:{result = RDI_TypeKind_Handle;}break; + } + return result; +} + +internal RDI_ChecksumKind +cv2r_rdi_from_cv_c13_checksum_kind(CV_C13ChecksumKind k) +{ + RDI_ChecksumKind result = RDI_ChecksumKind_NULL; + switch((CV_C13ChecksumKindEnum)k) + { + case CV_C13ChecksumKind_Null: {result = RDI_ChecksumKind_NULL;}break; + case CV_C13ChecksumKind_MD5: {result = RDI_ChecksumKind_MD5;}break; + case CV_C13ChecksumKind_SHA1: {result = RDI_ChecksumKind_SHA1;}break; + case CV_C13ChecksumKind_SHA256:{result = RDI_ChecksumKind_SHA256;}break; + } + return result; +} + +//////////////////////////////// +//~ rjf: Location Info Building Helpers + +internal RDI_RegCode +cv2r_reg_code_from_arch_encoded_fp_reg(RDI_Arch arch, CV_EncodedFramePtrReg encoded_reg) +{ + RDI_RegCode result = 0; + switch(arch) + { + case RDI_Arch_X64: + { + switch(encoded_reg) + { + case CV_EncodedFramePtrReg_StackPtr: + { + result = RDI_RegCodeX64_rsp; + }break; + case CV_EncodedFramePtrReg_FramePtr: + { + result = RDI_RegCodeX64_rbp; + }break; + case CV_EncodedFramePtrReg_BasePtr: + { + result = RDI_RegCodeX64_r13; + }break; + } + }break; + } + return result; +} + +internal RDIM_Location +cv2r_location_from_addr_reg_off(Arena *arena, RDI_Arch arch, RDI_RegCode reg_code, U32 reg_byte_size, U32 reg_byte_pos, S64 offset, B32 extra_indirection) +{ + RDIM_Location result = {0}; + if(0 <= offset && offset <= (S64)max_U16) + { + if(extra_indirection) + { + result.kind = RDI_LocationKind_AddrAddrRegPlusOff; + result.reg_code = reg_code; + result.offset = offset; + } + else + { + result.kind = RDI_LocationKind_AddrRegPlusOff; + result.reg_code = reg_code; + result.offset = offset; + } + } + else + { + RDIM_EvalBytecode bytecode = {0}; + U32 regread_param = RDI_EncodeRegReadParam(reg_code, reg_byte_size, reg_byte_pos); + rdim_bytecode_push_op(arena, &bytecode, RDI_EvalOp_RegRead, regread_param); + rdim_bytecode_push_sconst(arena, &bytecode, offset); + rdim_bytecode_push_op(arena, &bytecode, RDI_EvalOp_Add, 0); + if(extra_indirection) + { + U64 addr_size = rdi_addr_size_from_arch(arch); + rdim_bytecode_push_op(arena, &bytecode, RDI_EvalOp_MemRead, addr_size); + } + result.kind = RDI_LocationKind_AddrBytecodeStream; + result.bytecode = bytecode; + } + return result; +} + +internal void +cv2r_location_case_list_push_over_lvar_addr_range(Arena *arena, RDIM_LocationCaseList *loc_cases, RDIM_Location loc, Rng1U64 voff_range, CV_LvarAddrGap *gaps, U64 gap_count) +{ + //- rjf: emit location for ranges not coverd by gaps + CV_LvarAddrGap *gap_ptr = gaps; + U64 voff_cursor = voff_range.min; + for(U64 i = 0; i < gap_count; i += 1, gap_ptr += 1) + { + U64 voff_gap_first = voff_range.min + gap_ptr->off; + U64 voff_gap_opl = voff_gap_first + gap_ptr->len; + if(voff_cursor < voff_gap_first) + { + RDIM_Rng1U64 truncated_voff_range = {voff_cursor, voff_gap_first}; + rdim_location_case_list_push(arena, loc_cases, loc, truncated_voff_range); + } + voff_cursor = voff_gap_opl; + } + + //- rjf: emit remaining range + if(voff_cursor < voff_range.max) + { + RDIM_Rng1U64 remaining_voff_range = {voff_cursor, voff_range.max}; + rdim_location_case_list_push(arena, loc_cases, loc, remaining_voff_range); + } +} + +//////////////////////////////// +//~ rjf: Top-Level Conversion Entry Point + +internal RDIM_BakeParams +cv2r_convert(Arena *arena, CV2R_ConvertParams *params) +{ + Temp scratch = scratch_begin(&arena, 1); + + ////////////////////////////////////////////////////////////// + //- rjf: unpack params + // + U64 all_syms_count = params->all_syms_count; + CV_SymParsed **all_syms = params->all_syms; + CV_C13Parsed **all_c13s = params->all_c13s; + U64 comp_units_count = params->comp_units_count; + CV2R_CompUnit *comp_units = params->comp_units; + U64 sections_count = params->sections_count; + CV2R_Section *sections = params->sections; + CV2R_StringTable *strtbl = params->strtbl; + CV_LeafParsed *tpi_leaf = params->tpi_leaf; + CV_LeafParsed *ipi_leaf = params->ipi_leaf; + CV2R_TPIHash *tpi_hash = params->tpi_hash; + CV2R_TPIHash *ipi_hash = params->ipi_hash; + + ////////////////////////////////////////////////////////////// + //- rjf: hash EXE + // + U64 exe_hash = 0; + if(lane_idx() == lane_from_task_idx(0)) ProfScope("hash EXE") + { + exe_hash = rdi_hash(params->exe_data.str, params->exe_data.size); + } + lane_sync_u64(&exe_hash, 0); + + ////////////////////////////////////////////////////////////// + //- rjf: determine architecture + // + RDI_Arch arch = RDI_Arch_NULL; + U64 arch_addr_size = 0; + { + for EachIndex(idx, all_syms_count) + { + arch = cv2r_rdi_arch_from_cv_arch(all_syms[idx]->info.arch); + if(arch != RDI_Arch_NULL) + { + break; + } + } + arch_addr_size = rdi_addr_size_from_arch(arch); + } + + ////////////////////////////////////////////////////////////// + //- rjf: predict total symbol count + // + U64 symbol_count_prediction = 0; + { + U64 *symbol_count_prediction_ptr = &symbol_count_prediction; + lane_sync_u64(&symbol_count_prediction_ptr, 0); + U64 lane_sym_count = 0; + Rng1U64 range = lane_range(all_syms_count); + for EachInRange(idx, range) + { + lane_sym_count += all_syms[idx]->sym_ranges.count; + } + ins_atomic_u64_add_eval(symbol_count_prediction_ptr, lane_sym_count); + lane_sync(); + symbol_count_prediction = *symbol_count_prediction_ptr; + } + + ////////////////////////////////////////////////////////////// + //- rjf: build link name map + // + CV2R_LinkNameMap *link_name_map = 0; + ProfScope("build link name map") if(all_syms_count != 0 && lane_idx() == 0) + { + // rjf: set up + { + link_name_map = push_array(scratch.arena, CV2R_LinkNameMap, 1); + link_name_map->buckets_count = Max(1, symbol_count_prediction); + link_name_map->buckets = push_array(scratch.arena, CV2R_LinkNameNode *, link_name_map->buckets_count); + } + + // rjf: fill + if(params->subset_flags & RDIM_SubsetFlag_Procedures) + { + CV_SymParsed *sym = all_syms[0]; + for(CV_RecIter iter = {0}; cv_rec_next(sym->data, &sym->sym_ranges, 0, &iter);) + { + switch(iter.kind) + { + default:{}break; + case CV_SymKind_PUB32: + { + // rjf: unpack sym + CV_SymPub32 *pub32 = (CV_SymPub32 *)iter.struct_base; + String8 name = str8_cstring_capped(pub32+1, iter.opl); + U64 voff = cv2r_voff_from_soff(sections, sections_count, pub32->sec, pub32->off); + + // rjf: commit to link name map + U64 hash = cv2r_hash_from_voff(voff); + U64 bucket_idx = hash%link_name_map->buckets_count; + CV2R_LinkNameNode *node = push_array(scratch.arena, CV2R_LinkNameNode, 1); + SLLStackPush(link_name_map->buckets[bucket_idx], node); + node->voff = voff; + node->name = name; + }break; + } + } + } + } + lane_sync_u64(&link_name_map, 0); + + ////////////////////////////////////////////////////////////// + //- rjf: gather all file paths + // + CV2R_SrcFileStubArray *unit_file_stubs = 0; + U64Array *unit_file_paths_hashes = 0; + ProfScope("gather all file paths") + { + //- rjf: prep outputs + ProfScope("prep outputs") if(lane_idx() == 0) + { + unit_file_stubs = push_array(scratch.arena, CV2R_SrcFileStubArray, comp_units_count + 1); + unit_file_paths_hashes = push_array(scratch.arena, U64Array, comp_units_count + 1); + } + lane_sync_u64(&unit_file_stubs, 0); + lane_sync_u64(&unit_file_paths_hashes, 0); + + //- rjf: do wide gather + ProfScope("do wide gather") + { + Temp scratch2 = scratch_begin(&scratch.arena, 1); + + //- rjf: build local hash table to dedup files within this lane + U64 hit_path_slots_count = 4096; + String8Node **hit_path_slots = push_array(scratch2.arena, String8Node *, hit_path_slots_count); + + //- rjf: take units across lanes, find all file paths + U64 *sym_take_counter = lane_idx() == 0 ? push_array(scratch.arena, U64, 1) : 0; + lane_sync_u64(&sym_take_counter, 0); + ProfScope("take units across lanes, find all file paths") + for(;;) + { + //- rjf: take next unit + U64 unit_idx = ins_atomic_u64_inc_eval(sym_take_counter) - 1; + if(unit_idx >= comp_units_count + 1) + { + break; + } + + //- rjf: unpack unit + CV_SymParsed *sym = all_syms[unit_idx]; + CV_C13Parsed *c13 = all_c13s[unit_idx]; + + //- rjf: produce obj name/path + String8 obj_name = str8_lit("*global*"); + if(unit_idx > 0) + { + obj_name = comp_units[unit_idx-1].obj_name; + if(str8_match(obj_name, str8_lit("* Linker *"), 0) || + str8_match(obj_name, str8_lit("Import:"), StringMatchFlag_RightSideSloppy)) + { + MemoryZeroStruct(&obj_name); + } + } + String8 obj_folder_path = backslashed_from_str8(scratch2.arena, str8_chop_last_slash(obj_name)); + + //- rjf: find all inline site symbols & gather file stubs + CV2R_SrcFileStubNode *first_src_file_stub = 0; + CV2R_SrcFileStubNode *last_src_file_stub = 0; + U64 src_file_stub_count = 0; + U64 base_voff = 0; + for(CV_RecIter iter = {0}; cv_rec_next(sym->data, &sym->sym_ranges, 0, &iter);) + { + switch(iter.kind) + { + default:{}break; + + //- rjf: LPROC32/GPROC32 (gather base address) + case CV_SymKind_LPROC32: + case CV_SymKind_GPROC32: + { + CV_SymProc32 *proc32 = (CV_SymProc32 *)iter.struct_base; + base_voff = cv2r_voff_from_soff(sections, sections_count, proc32->sec, proc32->off); + }break; + + //- rjf: INLINESITE + case CV_SymKind_INLINESITE: + { + // rjf: unpack sym + CV_SymInlineSite *sym = (CV_SymInlineSite *)iter.struct_base; + String8 binary_annots = str8((U8 *)(sym+1), (U64)((U8 *)iter.opl - (U8 *)(sym+1))); + + // rjf: map inlinee -> parsed cv c13 inlinee line info + CV_C13InlineeLinesParsed *inlinee_lines_parsed = 0; + { + U64 hash = cv_hash_from_item_id(sym->inlinee); + U64 slot_idx = hash%c13->inlinee_lines_parsed_slots_count; + for(CV_C13InlineeLinesParsedNode *n = c13->inlinee_lines_parsed_slots[slot_idx]; n != 0; n = n->hash_next) + { + if(n->v.inlinee == sym->inlinee) + { + inlinee_lines_parsed = &n->v; + break; + } + } + } + + // rjf: build line table, fill with parsed binary annotations + if(inlinee_lines_parsed != 0) + { + // rjf: grab checksums sub-section + CV_C13SubSectionNode *file_chksms = c13->file_chksms_sub_section; + + // rjf: gathered lines + U32 last_file_off = max_U32; + U32 curr_file_off = max_U32; + U64 line_count = 0; + CV_C13InlineSiteDecoder decoder = cv_c13_inline_site_decoder_init(inlinee_lines_parsed->file_off, inlinee_lines_parsed->first_source_ln, base_voff); + for(;;) + { + // rjf: step & update + CV_C13InlineSiteDecoderStep step = cv_c13_inline_site_decoder_step(&decoder, binary_annots); + if(step.flags & CV_C13InlineSiteDecoderStepFlag_EmitFile) + { + last_file_off = curr_file_off; + curr_file_off = step.file_off; + } + if(step.flags == 0 && line_count > 0) + { + last_file_off = curr_file_off; + curr_file_off = max_U32; + } + + // rjf: file updated -> gather new file name + if(last_file_off != max_U32 && last_file_off != curr_file_off) + { + String8 seq_file_name = {0}; + CV_C13ChecksumKind checksum_kind = CV_C13ChecksumKind_Null; + String8 checksum_value = {0}; + if(last_file_off + sizeof(CV_C13Checksum) <= file_chksms->size) + { + CV_C13Checksum *checksum = (CV_C13Checksum *)(c13->data.str + file_chksms->off + last_file_off); + U32 name_off = checksum->name_off; + seq_file_name = cv2r_string_from_off(strtbl, name_off); + checksum_kind = checksum->kind; + checksum_value = str8_skip(c13->data, file_chksms->off + last_file_off + sizeof(*checksum)); + checksum_value.size = Min(checksum->len, checksum_value.size); + } + + // rjf: file name -> sanitized file path + String8 file_path = seq_file_name; + String8 file_path_sanitized = str8_copy(scratch2.arena, str8_skip_chop_whitespace(file_path)); + { + PathStyle file_path_sanitized_style = path_style_from_str8(file_path_sanitized); + String8List file_path_sanitized_parts = str8_split_path(scratch2.arena, file_path_sanitized); + if(file_path_sanitized_style == PathStyle_Relative) + { + String8List obj_folder_path_parts = str8_split_path(scratch2.arena, obj_folder_path); + str8_list_concat_in_place(&obj_folder_path_parts, &file_path_sanitized_parts); + file_path_sanitized_parts = obj_folder_path_parts; + file_path_sanitized_style = path_style_from_str8(obj_folder_path); + } + str8_path_list_resolve_dots_in_place(&file_path_sanitized_parts, file_path_sanitized_style); + file_path_sanitized = str8_path_list_join_by_style(scratch2.arena, &file_path_sanitized_parts, file_path_sanitized_style); + } + + // rjf: sanitized file path -> source file node + U64 file_path_sanitized_hash = rdi_hash(file_path_sanitized.str, file_path_sanitized.size); + U64 hit_path_slot = file_path_sanitized_hash%hit_path_slots_count; + String8Node *hit_path_node = 0; + for(String8Node *n = hit_path_slots[hit_path_slot]; n != 0; n = n->next) + { + if(str8_match(n->string, file_path_sanitized, 0)) + { + hit_path_node = n; + break; + } + } + if(hit_path_node == 0) + { + hit_path_node = push_array(scratch2.arena, String8Node, 1); + SLLStackPush(hit_path_slots[hit_path_slot], hit_path_node); + hit_path_node->string = file_path_sanitized; + CV2R_SrcFileStubNode *stub_n = push_array(scratch2.arena, CV2R_SrcFileStubNode, 1); + SLLQueuePush(first_src_file_stub, last_src_file_stub, stub_n); + src_file_stub_count += 1; + stub_n->v.file_path = str8_copy(scratch.arena, file_path_sanitized); + stub_n->v.checksum_kind = checksum_kind; + stub_n->v.checksum = str8_copy(scratch.arena, checksum_value); + } + line_count = 0; + } + + // rjf: count lines + if(step.flags & CV_C13InlineSiteDecoderStepFlag_EmitLine) + { + line_count += 1; + } + + // rjf: no more flags -> done + if(step.flags == 0) + { + break; + } + } + } + }break; + } + } + + // rjf: find all files in this unit's (non-inline) line info + if(c13 != 0) + { + ProfScope("find all files in this unit's (non-inline) line info") + for(CV_C13SubSectionNode *node = c13->first_sub_section; + node != 0; + node = node->next) + { + if(node->kind == CV_C13SubSectionKind_Lines) + { + for(CV_C13LinesParsedNode *lines_n = node->lines_first; + lines_n != 0; + lines_n = lines_n->next) + { + // rjf: file name -> sanitized file path + String8 file_path = lines_n->v.file_name; + String8 file_path_sanitized = str8_copy(scratch2.arena, str8_skip_chop_whitespace(file_path)); + { + PathStyle file_path_sanitized_style = path_style_from_str8(file_path_sanitized); + String8List file_path_sanitized_parts = str8_split_path(scratch2.arena, file_path_sanitized); + if(file_path_sanitized_style == PathStyle_Relative) + { + String8List obj_folder_path_parts = str8_split_path(scratch2.arena, obj_folder_path); + str8_list_concat_in_place(&obj_folder_path_parts, &file_path_sanitized_parts); + file_path_sanitized_parts = obj_folder_path_parts; + file_path_sanitized_style = path_style_from_str8(obj_folder_path); + } + str8_path_list_resolve_dots_in_place(&file_path_sanitized_parts, file_path_sanitized_style); + file_path_sanitized = str8_path_list_join_by_style(scratch2.arena, &file_path_sanitized_parts, file_path_sanitized_style); + } + + // rjf: sanitized file path -> source file node + U64 file_path_sanitized_hash = rdi_hash(file_path_sanitized.str, file_path_sanitized.size); + U64 hit_path_slot = file_path_sanitized_hash%hit_path_slots_count; + String8Node *hit_path_node = 0; + for(String8Node *n = hit_path_slots[hit_path_slot]; n != 0; n = n->next) + { + if(str8_match(n->string, file_path_sanitized, 0)) + { + hit_path_node = n; + break; + } + } + if(hit_path_node == 0) + { + hit_path_node = push_array(scratch2.arena, String8Node, 1); + SLLStackPush(hit_path_slots[hit_path_slot], hit_path_node); + hit_path_node->string = file_path_sanitized; + CV2R_SrcFileStubNode *stub_n = push_array(scratch2.arena, CV2R_SrcFileStubNode, 1); + SLLQueuePush(first_src_file_stub, last_src_file_stub, stub_n); + src_file_stub_count += 1; + stub_n->v.file_path = str8_copy(scratch.arena, file_path_sanitized); + stub_n->v.checksum_kind = lines_n->v.checksum_kind; + stub_n->v.checksum = str8_copy(scratch.arena, lines_n->v.checksum); + } + } + } + } + } + + //- rjf: merge into array for this unit + unit_file_stubs[unit_idx].count = src_file_stub_count; + unit_file_stubs[unit_idx].v = push_array_no_zero(scratch.arena, CV2R_SrcFileStub, unit_file_stubs[unit_idx].count); + { + U64 idx = 0; + for EachNode(n, CV2R_SrcFileStubNode, first_src_file_stub) + { + unit_file_stubs[unit_idx].v[idx] = n->v; + idx += 1; + } + } + + //- rjf: hash this unit's file paths + U64Array hashes = {0}; + hashes.count = unit_file_stubs[unit_idx].count; + hashes.v = push_array(scratch.arena, U64, hashes.count); + for EachIndex(idx, unit_file_stubs[unit_idx].count) + { + hashes.v[idx] = rdi_hash(unit_file_stubs[unit_idx].v[idx].file_path.str, unit_file_stubs[unit_idx].v[idx].file_path.size); + } + unit_file_paths_hashes[unit_idx] = hashes; + } + lane_sync(); + + scratch_end(scratch2); + } + } + + ////////////////////////////////////////////////////////////// + //- rjf: build unified collection & map for source files + // + RDIM_SrcFileChunkList *all_src_files__sequenceless = 0; + CV2R_SrcFileMap *src_file_map = 0; + if(lane_idx() == 0 && params->subset_flags & (RDIM_SubsetFlag_NormalSourcePathNameMap| + RDIM_SubsetFlag_LineInfo| + RDIM_SubsetFlag_InlineLineInfo)) + { + //- rjf: set up table + U64 total_path_count = 0; + ProfScope("set up table") + { + all_src_files__sequenceless = push_array(scratch.arena, RDIM_SrcFileChunkList, 1); + src_file_map = push_array(scratch.arena, CV2R_SrcFileMap, 1); + for EachIndex(idx, comp_units_count) + { + total_path_count += unit_file_stubs[idx].count; + } + src_file_map->slots_count = total_path_count + total_path_count/2 + 1; + src_file_map->slots = push_array(scratch.arena, CV2R_SrcFileNode *, src_file_map->slots_count); + } + + //- rjf: fill table + ProfScope("fill table") + { + for EachIndex(idx, comp_units_count) + { + CV2R_SrcFileStubArray stubs = unit_file_stubs[idx]; + U64Array hashes = unit_file_paths_hashes[idx]; + for EachIndex(stub_idx, stubs.count) + { + String8 file_path_sanitized = stubs.v[stub_idx].file_path; + CV_C13ChecksumKind c13_checksum_kind = stubs.v[stub_idx].checksum_kind; + String8 checksum = stubs.v[stub_idx].checksum; + U64 file_path_sanitized_hash = hashes.v[stub_idx]; + U64 src_file_slot = file_path_sanitized_hash%src_file_map->slots_count; + CV2R_SrcFileNode *src_file_node = 0; + for(CV2R_SrcFileNode *n = src_file_map->slots[src_file_slot]; n != 0; n = n->next) + { + if(str8_match(n->src_file->path, file_path_sanitized, 0)) + { + src_file_node = n; + break; + } + } + if(src_file_node == 0) + { + src_file_node = push_array(arena, CV2R_SrcFileNode, 1); + SLLStackPush(src_file_map->slots[src_file_slot], src_file_node); + src_file_node->src_file = rdim_src_file_chunk_list_push(arena, all_src_files__sequenceless, total_path_count); + src_file_node->src_file->path = str8_copy(arena, file_path_sanitized); + src_file_node->src_file->checksum_kind = p2r_rdi_from_cv_c13_checksum_kind(c13_checksum_kind); + src_file_node->src_file->checksum = str8_copy(arena, checksum); + } + } + } + } + } + lane_sync_u64(&all_src_files__sequenceless, 0); + lane_sync_u64(&src_file_map, 0); + + ////////////////////////////////////////////////////////////// + //- rjf: convert unit header info + // + RDIM_UnitChunkList *all_units_ptr = 0; + RDIM_LineTableChunkList *units_line_tables = 0; + RDIM_LineTable **units_first_inline_site_line_tables = 0; + ProfScope("convert unit header info") + { + //- rjf: set up outputs + ProfScope("set up outputs") if(lane_idx() == 0) + { + all_units_ptr = push_array(scratch.arena, RDIM_UnitChunkList, 1); + if(params->subset_flags & RDIM_SubsetFlag_Units) + { + for EachIndex(idx, comp_units_count + 1) + { + rdim_unit_chunk_list_push(arena, all_units_ptr, comp_units_count + 1); + } + } + units_line_tables = push_array(scratch.arena, RDIM_LineTableChunkList, comp_units_count + 1); + units_first_inline_site_line_tables = push_array(scratch.arena, RDIM_LineTable *, comp_units_count + 1); + } + lane_sync_u64(&all_units_ptr, 0); + lane_sync_u64(&units_line_tables, 0); + lane_sync_u64(&units_first_inline_site_line_tables, 0); + RDIM_Unit *units = all_units_ptr->first ? all_units_ptr->first->v : 0; + U64 units_count = all_units_ptr->first ? all_units_ptr->first->count : 0; + + //- rjf: do per-lane work + if(params->subset_flags & (RDIM_SubsetFlag_Units| + RDIM_SubsetFlag_NormalSourcePathNameMap| + RDIM_SubsetFlag_LineInfo| + RDIM_SubsetFlag_InlineLineInfo)) + { + U64 *sym_take_counter = lane_idx() == 0 ? push_array(scratch.arena, U64, 1) : 0; + lane_sync_u64(&sym_take_counter, 0); + ProfScope("wide fill") for(;;) + { + //- rjf: take next unit + U64 unit_idx = ins_atomic_u64_inc_eval(sym_take_counter) - 1; + if(unit_idx >= units_count) + { + break; + } + Temp scratch = scratch_begin(&arena, 1); + RDIM_LineTableChunkList *dst_line_tables = &units_line_tables[unit_idx]; + CV2R_CompUnit *src_unit = (unit_idx > 0 ? &comp_units[unit_idx-1] : 0); + CV_SymParsed *src_unit_sym = all_syms[unit_idx]; + CV_C13Parsed *src_unit_c13 = all_c13s[unit_idx]; + RDIM_Unit *dst_unit = 0; + if(params->subset_flags & RDIM_SubsetFlag_Units) { dst_unit = &units[unit_idx]; } + + // rjf: extract unit ranges + RDIM_Rng1U64ChunkList unit_ranges = {0}; + if(src_unit != 0) + { + unit_ranges = src_unit->ranges; + } + + // rjf: extract unit name + String8 unit_name = {0}; + if(src_unit != 0) + { + unit_name = src_unit->obj_name; + if(unit_name.size != 0) + { + String8 unit_name_past_last_slash = str8_skip_last_slash(unit_name); + if(unit_name_past_last_slash.size != 0) + { + unit_name = unit_name_past_last_slash; + } + } + } + else + { + unit_name = str8_lit("*global*"); + } + + // rjf: produce obj name/path + String8 obj_name = {0}; + if(src_unit != 0) + { + obj_name = src_unit->obj_name; + if(str8_match(obj_name, str8_lit("* Linker *"), 0) || + str8_match(obj_name, str8_lit("Import:"), StringMatchFlag_RightSideSloppy)) + { + MemoryZeroStruct(&obj_name); + } + } + String8 obj_folder_path = backslashed_from_str8(scratch.arena, str8_chop_last_slash(obj_name)); + + // rjf: extract unit group name + String8 group_name = {0}; + if(src_unit != 0) + { + group_name = src_unit->group_name; + } + + //- rjf: main unit line table conversion + RDIM_LineTable *line_table = 0; + if(params->subset_flags & RDIM_SubsetFlag_LineInfo && src_unit_c13 != 0) ProfScope("main unit line table conversion") + { + for(CV_C13SubSectionNode *node = src_unit_c13->first_sub_section; + node != 0; + node = node->next) + { + if(node->kind == CV_C13SubSectionKind_Lines) + { + for(CV_C13LinesParsedNode *lines_n = node->lines_first; + lines_n != 0; + lines_n = lines_n->next) + { + CV_C13LinesParsed *lines = &lines_n->v; + + // rjf: file name -> sanitized file path + String8 file_path = lines->file_name; + String8 file_path_sanitized = str8_copy(scratch.arena, str8_skip_chop_whitespace(file_path)); + { + PathStyle file_path_sanitized_style = path_style_from_str8(file_path_sanitized); + String8List file_path_sanitized_parts = str8_split_path(scratch.arena, file_path_sanitized); + if(file_path_sanitized_style == PathStyle_Relative) + { + String8List obj_folder_path_parts = str8_split_path(scratch.arena, obj_folder_path); + str8_list_concat_in_place(&obj_folder_path_parts, &file_path_sanitized_parts); + file_path_sanitized_parts = obj_folder_path_parts; + file_path_sanitized_style = path_style_from_str8(obj_folder_path); + } + str8_path_list_resolve_dots_in_place(&file_path_sanitized_parts, file_path_sanitized_style); + file_path_sanitized = str8_path_list_join_by_style(scratch.arena, &file_path_sanitized_parts, file_path_sanitized_style); + } + + // rjf: sanitized file path -> source file node + U64 file_path_sanitized_hash = rdi_hash(file_path_sanitized.str, file_path_sanitized.size); + U64 src_file_slot = file_path_sanitized_hash%src_file_map->slots_count; + CV2R_SrcFileNode *src_file_node = 0; + if(lines->line_count != 0) + { + for(CV2R_SrcFileNode *n = src_file_map->slots[src_file_slot]; n != 0; n = n->next) + { + if(str8_match(n->src_file->path, file_path_sanitized, 0)) + { + src_file_node = n; + break; + } + } + } + + // rjf: push sequence into both line table & source file's line map + if(src_file_node != 0) + { + if(line_table == 0) + { + line_table = rdim_line_table_chunk_list_push(arena, dst_line_tables, 256); + } + RDIM_LineSequence *seq = rdim_line_table_push_sequence(arena, dst_line_tables, line_table, src_file_node->src_file, lines->voffs, lines->line_nums, lines->col_nums, lines->line_count); + } + } + } + } + } + + //- rjf: fill unit + if(dst_unit != 0) + { + dst_unit->unit_name = unit_name; + dst_unit->compiler_name = src_unit_sym->info.compiler_name; + dst_unit->object_file = obj_name; + dst_unit->archive_file = group_name; + dst_unit->language = cv2r_rdi_language_from_cv_language(src_unit_sym->info.language); + dst_unit->line_table = line_table; + dst_unit->voff_ranges = unit_ranges; + } + + //- rjf: build per-inline-site line tables + if(params->subset_flags & RDIM_SubsetFlag_InlineLineInfo) ProfScope("build per-inline-site line tables") + { + U64 base_voff = 0; + for(CV_RecIter iter = {0}; cv_rec_next(src_unit_sym->data, &src_unit_sym->sym_ranges, 0, &iter);) + { + switch(iter.kind) + { + default:{}break; + + //- rjf: LPROC32/GPROC32 (gather base address) + case CV_SymKind_LPROC32: + case CV_SymKind_GPROC32: + { + CV_SymProc32 *proc32 = (CV_SymProc32 *)iter.struct_base; + base_voff = cv2r_voff_from_soff(sections, sections_count, proc32->sec, proc32->off); + }break; + + //- rjf: INLINESITE + case CV_SymKind_INLINESITE: + { + // rjf: unpack sym + CV_SymInlineSite *sym = (CV_SymInlineSite *)iter.struct_base; + String8 binary_annots = str8((U8 *)(sym+1), (U64)((U8 *)iter.opl - (U8 *)(sym+1))); + + // rjf: map inlinee -> parsed cv c13 inlinee line info + CV_C13InlineeLinesParsed *inlinee_lines_parsed = 0; + { + U64 hash = cv_hash_from_item_id(sym->inlinee); + U64 slot_idx = hash%src_unit_c13->inlinee_lines_parsed_slots_count; + for(CV_C13InlineeLinesParsedNode *n = src_unit_c13->inlinee_lines_parsed_slots[slot_idx]; n != 0; n = n->hash_next) + { + if(n->v.inlinee == sym->inlinee) + { + inlinee_lines_parsed = &n->v; + break; + } + } + } + + // rjf: build line table, fill with parsed binary annotations + if(inlinee_lines_parsed != 0) + { + // rjf: grab checksums sub-section + CV_C13SubSectionNode *file_chksms = src_unit_c13->file_chksms_sub_section; + + // rjf: gathered lines + typedef struct LineChunk LineChunk; + struct LineChunk + { + LineChunk *next; + U64 cap; + U64 count; + U64 *voffs; // [line_count + 1] (sorted) + U32 *line_nums; // [line_count] + U16 *col_nums; // [2*line_count] + }; + LineChunk *first_line_chunk = 0; + LineChunk *last_line_chunk = 0; + U64 total_line_chunk_line_count = 0; + U32 last_file_off = max_U32; + U32 curr_file_off = max_U32; + RDIM_LineTable* line_table = 0; + + CV_C13InlineSiteDecoder decoder = cv_c13_inline_site_decoder_init(inlinee_lines_parsed->file_off, inlinee_lines_parsed->first_source_ln, base_voff); + for(;;) + { + // rjf: step & update + CV_C13InlineSiteDecoderStep step = cv_c13_inline_site_decoder_step(&decoder, binary_annots); + if(step.flags & CV_C13InlineSiteDecoderStepFlag_EmitFile) + { + last_file_off = curr_file_off; + curr_file_off = step.file_off; + } + if(step.flags == 0 && total_line_chunk_line_count > 0) + { + last_file_off = curr_file_off; + curr_file_off = max_U32; + } + + // rjf: file updated -> push line chunks gathered for this file + if(last_file_off != max_U32 && last_file_off != curr_file_off) + { + String8 seq_file_name = {0}; + if(last_file_off + sizeof(CV_C13Checksum) <= file_chksms->size) + { + CV_C13Checksum *checksum = (CV_C13Checksum*)(src_unit_c13->data.str + file_chksms->off + last_file_off); + U32 name_off = checksum->name_off; + seq_file_name = cv2r_string_from_off(strtbl, name_off); + } + + // rjf: file name -> sanitized file path + String8 file_path = seq_file_name; + String8 file_path_sanitized = str8_copy(scratch.arena, str8_skip_chop_whitespace(file_path)); + { + PathStyle file_path_sanitized_style = path_style_from_str8(file_path_sanitized); + String8List file_path_sanitized_parts = str8_split_path(scratch.arena, file_path_sanitized); + if(file_path_sanitized_style == PathStyle_Relative) + { + String8List obj_folder_path_parts = str8_split_path(scratch.arena, obj_folder_path); + str8_list_concat_in_place(&obj_folder_path_parts, &file_path_sanitized_parts); + file_path_sanitized_parts = obj_folder_path_parts; + file_path_sanitized_style = path_style_from_str8(obj_folder_path); + } + str8_path_list_resolve_dots_in_place(&file_path_sanitized_parts, file_path_sanitized_style); + file_path_sanitized = str8_path_list_join_by_style(scratch.arena, &file_path_sanitized_parts, file_path_sanitized_style); + } + + // rjf: sanitized file path -> source file node + U64 file_path_sanitized_hash = rdi_hash(file_path_sanitized.str, file_path_sanitized.size); + U64 src_file_slot = file_path_sanitized_hash%src_file_map->slots_count; + CV2R_SrcFileNode *src_file_node = 0; + for(CV2R_SrcFileNode *n = src_file_map->slots[src_file_slot]; n != 0; n = n->next) + { + if(str8_match(n->src_file->path, file_path_sanitized, 0)) + { + src_file_node = n; + break; + } + } + + // rjf: gather all lines + RDI_U64 *voffs = 0; + RDI_U32 *line_nums = 0; + RDI_U64 line_count = 0; + if(src_file_node != 0) + { + voffs = push_array_no_zero(arena, RDI_U64, total_line_chunk_line_count+1); + line_nums = push_array_no_zero(arena, RDI_U32, total_line_chunk_line_count); + line_count = total_line_chunk_line_count; + U64 dst_idx = 0; + for(LineChunk *chunk = first_line_chunk; chunk != 0; chunk = chunk->next) + { + MemoryCopy(voffs+dst_idx, chunk->voffs, sizeof(U64)*(chunk->count+1)); + MemoryCopy(line_nums+dst_idx, chunk->line_nums, sizeof(U32)*chunk->count); + dst_idx += chunk->count; + } + } + + // rjf: push + if(line_count != 0) + { + if(line_table == 0) + { + line_table = rdim_line_table_chunk_list_push(arena, dst_line_tables, 256); + if(units_first_inline_site_line_tables[unit_idx] == 0) + { + units_first_inline_site_line_tables[unit_idx] = line_table; + } + } + rdim_line_table_push_sequence(arena, dst_line_tables, line_table, src_file_node->src_file, voffs, line_nums, 0, line_count); + } + + // rjf: clear line chunks for subsequent sequences + first_line_chunk = last_line_chunk = 0; + total_line_chunk_line_count = 0; + } + + // rjf: new line -> emit to chunk + if(step.flags & CV_C13InlineSiteDecoderStepFlag_EmitLine) + { + LineChunk *chunk = last_line_chunk; + if(chunk == 0 || chunk->count+1 >= chunk->cap) + { + chunk = push_array(scratch.arena, LineChunk, 1); + SLLQueuePush(first_line_chunk, last_line_chunk, chunk); + chunk->cap = 8; + chunk->voffs = push_array_no_zero(scratch.arena, U64, chunk->cap); + chunk->line_nums = push_array_no_zero(scratch.arena, U32, chunk->cap); + } + chunk->voffs[chunk->count] = step.line_voff; + chunk->voffs[chunk->count+1] = step.line_voff_end; + chunk->line_nums[chunk->count] = step.ln; + chunk->count += 1; + total_line_chunk_line_count += 1; + } + + // rjf: no more flags -> done + if(step.flags == 0) + { + break; + } + } + } + }break; + } + } + } + + scratch_end(scratch); + } + } + } + lane_sync(); + RDIM_UnitChunkList all_units = *all_units_ptr; + + ////////////////////////////////////////////////////////////// + //- rjf: join all line tables + // + RDIM_LineTableChunkList all_line_tables = {0}; + RDIM_LineTableChunkList *all_line_tables_ptr = &all_line_tables; + ProfScope("join all line tables") if(lane_idx() == 0) + { + for EachIndex(idx, comp_units_count + 1) + { + rdim_line_table_chunk_list_concat_in_place(&all_line_tables, &units_line_tables[idx]); + } + } + lane_sync_u64(&all_line_tables_ptr, 0); + all_line_tables = *all_line_tables_ptr; + + ////////////////////////////////////////////////////////////// + //- rjf: equip source files with line sequences + // + ProfScope("equip source files with line sequences") if(lane_idx() == 0) + { + for(RDIM_LineTableChunkNode *line_table_chunk_n = all_line_tables.first; + line_table_chunk_n != 0; + line_table_chunk_n = line_table_chunk_n->next) + { + for EachIndex(chunk_line_table_idx, line_table_chunk_n->count) + { + RDIM_LineTable *line_table = &line_table_chunk_n->v[chunk_line_table_idx]; + for(RDIM_LineSequenceNode *s = line_table->first_seq; s != 0; s = s->next) + { + rdim_src_file_push_line_sequence(arena, all_src_files__sequenceless, s->v.src_file, &s->v); + } + } + } + } + lane_sync(); + RDIM_SrcFileChunkList all_src_files = *all_src_files__sequenceless; + + ////////////////////////////////////////////////////////////// + //- rjf: types pass 1: produce type forward resolution map + // + // this map is used to resolve usage of "incomplete structs" in codeview's + // type info. this often happens when e.g. "struct Foo" is used to refer to + // a later-defined "Foo", which actually contains members and so on. we want + // to hook types up to their actual destination complete types wherever + // possible, and so this map can be used to do that in subsequent stages. + // + CV_TypeId *itype_fwd_map = 0; + CV_TypeId itype_first = 0; + CV_TypeId itype_opl = 0; + ProfScope("types pass 1: produce type forward resolution map") + { + //- rjf: allocate forward resolution map + if(lane_idx() == 0) + { + itype_first = tpi_leaf->itype_first; + itype_opl = tpi_leaf->itype_opl; + itype_fwd_map = push_array(scratch.arena, CV_TypeId, (U64)itype_opl); + } + lane_sync_u64(&itype_first, 0); + lane_sync_u64(&itype_opl, 0); + lane_sync_u64(&itype_fwd_map, 0); + + //- rjf: do wide fill + if(params->subset_flags & RDIM_SubsetFlag_Types) + { + Rng1U64 range = lane_range(itype_opl); + for EachInRange(idx, range) + { + CV_TypeId itype = (CV_TypeId)idx; + if(itype < itype_first) { continue; } + + //- rjf: determine if this itype resolves to another + CV_TypeId itype_fwd = 0; + CV_RecRange *range = &tpi_leaf->leaf_ranges.ranges[itype-tpi_leaf->itype_first]; + CV_LeafKind kind = range->hdr.kind; + U64 header_struct_size = cv_header_struct_size_from_leaf_kind(kind); + if(range->off+range->hdr.size <= tpi_leaf->data.size && + range->off+2+header_struct_size <= tpi_leaf->data.size && + range->hdr.size >= 2) + { + U8 *itype_leaf_first = tpi_leaf->data.str + range->off+2; + U8 *itype_leaf_opl = itype_leaf_first + range->hdr.size-2; + switch(kind) + { + default:{}break; + + //- rjf: CLASS/STRUCTURE + case CV_LeafKind_CLASS: + case CV_LeafKind_STRUCTURE: + { + // rjf: unpack leaf header + CV_LeafStruct *lf_struct = (CV_LeafStruct *)itype_leaf_first; + + // rjf: has fwd ref flag -> lookup itype that this itype resolves to + if(lf_struct->props & CV_TypeProp_FwdRef) + { + // rjf: unpack rest of leaf + U8 *numeric_ptr = (U8 *)(lf_struct + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped(unique_name_ptr, itype_leaf_opl); + + // rjf: lookup + B32 do_unique_name_lookup = (((lf_struct->props & CV_TypeProp_Scoped) != 0) && + ((lf_struct->props & CV_TypeProp_HasUniqueName) != 0)); + itype_fwd = cv2r_first_itype_from_name(tpi_hash, tpi_leaf, do_unique_name_lookup?unique_name:name, do_unique_name_lookup); + } + }break; + + //- rjf: CLASS2/STRUCT2 + case CV_LeafKind_CLASS2: + case CV_LeafKind_STRUCT2: + { + // rjf: unpack leaf header + CV_LeafStruct2 *lf_struct = (CV_LeafStruct2 *)itype_leaf_first; + + // rjf: has fwd ref flag -> lookup itype that this itype resolves to + if(lf_struct->props & CV_TypeProp_FwdRef) + { + // rjf: unpack rest of leaf + U8 *numeric_ptr = (U8 *)(lf_struct + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U8 *name_ptr = (U8 *)numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped(unique_name_ptr, itype_leaf_opl); + + // rjf: lookup + B32 do_unique_name_lookup = (((lf_struct->props & CV_TypeProp_Scoped) != 0) && + ((lf_struct->props & CV_TypeProp_HasUniqueName) != 0)); + itype_fwd = cv2r_first_itype_from_name(tpi_hash, tpi_leaf, do_unique_name_lookup?unique_name:name, do_unique_name_lookup); + } + }break; + + //- rjf: UNION + case CV_LeafKind_UNION: + { + // rjf: unpack leaf + CV_LeafUnion *lf_union = (CV_LeafUnion *)itype_leaf_first; + U8 *numeric_ptr = (U8 *)(lf_union + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped(unique_name_ptr, itype_leaf_opl); + + // rjf: has fwd ref flag -> lookup itype that this itype resolves tos + if(lf_union->props & CV_TypeProp_FwdRef) + { + B32 do_unique_name_lookup = (((lf_union->props & CV_TypeProp_Scoped) != 0) && + ((lf_union->props & CV_TypeProp_HasUniqueName) != 0)); + itype_fwd = cv2r_first_itype_from_name(tpi_hash, tpi_leaf, do_unique_name_lookup?unique_name:name, do_unique_name_lookup); + } + }break; + + //- rjf: ENUM + case CV_LeafKind_ENUM: + { + // rjf: unpack leaf + CV_LeafEnum *lf_enum = (CV_LeafEnum*)itype_leaf_first; + U8 *name_ptr = (U8 *)(lf_enum + 1); + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped(unique_name_ptr, itype_leaf_opl); + + // rjf: has fwd ref flag -> lookup itype that this itype resolves to + if(lf_enum->props & CV_TypeProp_FwdRef) + { + B32 do_unique_name_lookup = (((lf_enum->props & CV_TypeProp_Scoped) != 0) && + ((lf_enum->props & CV_TypeProp_HasUniqueName) != 0)); + itype_fwd = cv2r_first_itype_from_name(tpi_hash, tpi_leaf, do_unique_name_lookup?unique_name:name, do_unique_name_lookup); + } + }break; + } + } + + //- rjf: if the forwarded itype is nonzero & in TPI range -> save to map + if(itype_fwd != 0 && itype_fwd < tpi_leaf->itype_opl) + { + itype_fwd_map[itype] = itype_fwd; + } + } + } + } + lane_sync(); + + + ////////////////////////////////////////////////////////////// + //- rjf: types pass 2: produce per-itype itype chain + // + // this pass is to ensure that subsequent passes always produce types for + // dependent itypes first - guaranteeing rdi's "only reference backward" + // rule (which eliminates cycles). each itype slot gets a list of itypes, + // starting with the deepest dependency - when types are produced per-itype, + // this chain is walked, so that deeper dependencies are built first, and + // as such, always show up *earlier* in the actually built types. + // + CV2R_TypeIdChain **itype_chains = 0; + ProfScope("types pass 2: produce per-itype itype chain (for producing dependent types first)") + { + //- rjf: allocate itype chain table + if(lane_idx() == 0) + { + itype_chains = push_array(scratch.arena, CV2R_TypeIdChain *, (U64)itype_opl); + } + lane_sync_u64(&itype_chains, 0); + + //- rjf: do wide fill + if(params->subset_flags & RDIM_SubsetFlag_Types) + { + Rng1U64 range = lane_range(itype_opl); + for EachInRange(idx, range) + { + CV_TypeId itype = (CV_TypeId)idx; + + //- rjf: push initial itype - should be final-visited-itype for this itype + { + CV2R_TypeIdChain *c = push_array(scratch.arena, CV2R_TypeIdChain, 1); + c->itype = itype; + SLLStackPush(itype_chains[itype], c); + } + + //- rjf: skip basic types for dependency walk + if(itype < tpi_leaf->itype_first) + { + continue; + } + + //- rjf: walk dependent types, push to chain + Temp scratch2 = scratch_begin(&scratch.arena, 1); + CV2R_TypeIdChain start_walk_task = {0, itype}; + CV2R_TypeIdChain *first_walk_task = &start_walk_task; + CV2R_TypeIdChain *last_walk_task = &start_walk_task; + for(CV2R_TypeIdChain *walk_task = first_walk_task; + walk_task != 0; + walk_task = walk_task->next) + { + CV_TypeId walk_itype = itype_fwd_map[walk_task->itype] ? itype_fwd_map[walk_task->itype] : walk_task->itype; + if(walk_itype < tpi_leaf->itype_first || tpi_leaf->itype_opl <= walk_itype) + { + continue; + } + CV_RecRange *range = &tpi_leaf->leaf_ranges.ranges[walk_itype-tpi_leaf->itype_first]; + CV_LeafKind kind = range->hdr.kind; + U64 header_struct_size = cv_header_struct_size_from_leaf_kind(kind); + if(range->off+range->hdr.size <= tpi_leaf->data.size && + range->off+2+header_struct_size <= tpi_leaf->data.size && + range->hdr.size >= 2) + { + U8 *itype_leaf_first = tpi_leaf->data.str + range->off+2; + U8 *itype_leaf_opl = itype_leaf_first + range->hdr.size-2; + switch(kind) + { + default:{}break; + + //- rjf: MODIFIER + case CV_LeafKind_MODIFIER: + { + CV_LeafModifier *lf = (CV_LeafModifier *)itype_leaf_first; + + // rjf: push dependent itype to chain + { + CV2R_TypeIdChain *c = push_array(scratch.arena, CV2R_TypeIdChain, 1); + c->itype = lf->itype; + SLLStackPush(itype_chains[itype], c); + } + + // rjf: push task to walk dependency itype + { + CV2R_TypeIdChain *c = push_array(scratch2.arena, CV2R_TypeIdChain, 1); + c->itype = lf->itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + }break; + + //- rjf: POINTER + case CV_LeafKind_POINTER: + { + CV_LeafModifier *lf = (CV_LeafModifier *)itype_leaf_first; + + // rjf: push dependent itype to chain + { + CV2R_TypeIdChain *c = push_array(scratch.arena, CV2R_TypeIdChain, 1); + c->itype = lf->itype; + SLLStackPush(itype_chains[itype], c); + } + + // rjf: push task to walk dependency itype + { + CV2R_TypeIdChain *c = push_array(scratch2.arena, CV2R_TypeIdChain, 1); + c->itype = lf->itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + }break; + + //- rjf: PROCEDURE + case CV_LeafKind_PROCEDURE: + { + CV_LeafProcedure *lf = (CV_LeafProcedure *)itype_leaf_first; + + // rjf: push return itypes to chain + { + CV2R_TypeIdChain *c = push_array(scratch.arena, CV2R_TypeIdChain, 1); + c->itype = lf->ret_itype; + SLLStackPush(itype_chains[itype], c); + } + + // rjf: push task to walk return itype + { + CV2R_TypeIdChain *c = push_array(scratch2.arena, CV2R_TypeIdChain, 1); + c->itype = lf->ret_itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + + // rjf: unpack arglist range + CV_RecRange *arglist_range = &tpi_leaf->leaf_ranges.ranges[lf->arg_itype-tpi_leaf->itype_first]; + if(arglist_range->hdr.kind != CV_LeafKind_ARGLIST || + arglist_range->hdr.size<2 || + arglist_range->off + arglist_range->hdr.size > tpi_leaf->data.size) + { + break; + } + U8 *arglist_first = tpi_leaf->data.str + arglist_range->off + 2; + U8 *arglist_opl = arglist_first+arglist_range->hdr.size-2; + if(arglist_first + sizeof(CV_LeafArgList) > arglist_opl) + { + break; + } + + // rjf: unpack arglist info + CV_LeafArgList *arglist = (CV_LeafArgList*)arglist_first; + CV_TypeId *arglist_itypes_base = (CV_TypeId *)(arglist+1); + U32 arglist_itypes_count = arglist->count; + + // rjf: push arg types to chain + for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) + { + CV2R_TypeIdChain *c = push_array(scratch.arena, CV2R_TypeIdChain, 1); + c->itype = arglist_itypes_base[idx]; + SLLStackPush(itype_chains[itype], c); + } + + // rjf: push task to walk arg types + for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) + { + CV2R_TypeIdChain *c = push_array(scratch2.arena, CV2R_TypeIdChain, 1); + c->itype = arglist_itypes_base[idx]; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + }break; + + //- rjf: MFUNCTION + case CV_LeafKind_MFUNCTION: + { + CV_LeafMFunction *lf = (CV_LeafMFunction *)itype_leaf_first; + + // rjf: push dependent itypes to chain + { + CV2R_TypeIdChain *c = push_array(scratch.arena, CV2R_TypeIdChain, 1); + c->itype = lf->ret_itype; + SLLStackPush(itype_chains[itype], c); + } + { + CV2R_TypeIdChain *c = push_array(scratch.arena, CV2R_TypeIdChain, 1); + c->itype = lf->arg_itype; + SLLStackPush(itype_chains[itype], c); + } + { + CV2R_TypeIdChain *c = push_array(scratch.arena, CV2R_TypeIdChain, 1); + c->itype = lf->this_itype; + SLLStackPush(itype_chains[itype], c); + } + + // rjf: push task to walk dependency itypes + { + CV2R_TypeIdChain *c = push_array(scratch2.arena, CV2R_TypeIdChain, 1); + c->itype = lf->ret_itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + { + CV2R_TypeIdChain *c = push_array(scratch2.arena, CV2R_TypeIdChain, 1); + c->itype = lf->arg_itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + { + CV2R_TypeIdChain *c = push_array(scratch2.arena, CV2R_TypeIdChain, 1); + c->itype = lf->this_itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + + // rjf: unpack arglist range + CV_RecRange *arglist_range = &tpi_leaf->leaf_ranges.ranges[lf->arg_itype-tpi_leaf->itype_first]; + if(arglist_range->hdr.kind != CV_LeafKind_ARGLIST || + arglist_range->hdr.size<2 || + arglist_range->off + arglist_range->hdr.size > tpi_leaf->data.size) + { + break; + } + U8 *arglist_first = tpi_leaf->data.str + arglist_range->off + 2; + U8 *arglist_opl = arglist_first+arglist_range->hdr.size-2; + if(arglist_first + sizeof(CV_LeafArgList) > arglist_opl) + { + break; + } + + // rjf: unpack arglist info + CV_LeafArgList *arglist = (CV_LeafArgList*)arglist_first; + CV_TypeId *arglist_itypes_base = (CV_TypeId *)(arglist+1); + U32 arglist_itypes_count = arglist->count; + + // rjf: push arg types to chain + for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) + { + CV2R_TypeIdChain *c = push_array(scratch.arena, CV2R_TypeIdChain, 1); + c->itype = arglist_itypes_base[idx]; + SLLStackPush(itype_chains[itype], c); + } + + // rjf: push task to walk arg types + for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) + { + CV2R_TypeIdChain *c = push_array(scratch2.arena, CV2R_TypeIdChain, 1); + c->itype = arglist_itypes_base[idx]; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + }break; + + //- rjf: BITFIELD + case CV_LeafKind_BITFIELD: + { + CV_LeafBitField *lf = (CV_LeafBitField *)itype_leaf_first; + + // rjf: push dependent itype to chain + { + CV2R_TypeIdChain *c = push_array(scratch.arena, CV2R_TypeIdChain, 1); + c->itype = lf->itype; + SLLStackPush(itype_chains[itype], c); + } + + // rjf: push task to walk dependency itype + { + CV2R_TypeIdChain *c = push_array(scratch2.arena, CV2R_TypeIdChain, 1); + c->itype = lf->itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + }break; + + //- rjf: ARRAY + case CV_LeafKind_ARRAY: + { + CV_LeafArray *lf = (CV_LeafArray *)itype_leaf_first; + + // rjf: push dependent itypes to chain + { + CV2R_TypeIdChain *c = push_array(scratch.arena, CV2R_TypeIdChain, 1); + c->itype = lf->entry_itype; + SLLStackPush(itype_chains[itype], c); + } + { + CV2R_TypeIdChain *c = push_array(scratch.arena, CV2R_TypeIdChain, 1); + c->itype = lf->index_itype; + SLLStackPush(itype_chains[itype], c); + } + + // rjf: push task to walk dependency itypes + { + CV2R_TypeIdChain *c = push_array(scratch2.arena, CV2R_TypeIdChain, 1); + c->itype = lf->entry_itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + { + CV2R_TypeIdChain *c = push_array(scratch2.arena, CV2R_TypeIdChain, 1); + c->itype = lf->index_itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + }break; + + //- rjf: ENUM + case CV_LeafKind_ENUM: + { + CV_LeafEnum *lf = (CV_LeafEnum *)itype_leaf_first; + + // rjf: push dependent itypes to chain + { + CV2R_TypeIdChain *c = push_array(scratch.arena, CV2R_TypeIdChain, 1); + c->itype = lf->base_itype; + SLLStackPush(itype_chains[itype], c); + } + + // rjf: push task to walk dependency itypes + { + CV2R_TypeIdChain *c = push_array(scratch2.arena, CV2R_TypeIdChain, 1); + c->itype = lf->base_itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + }break; + } + } + } + scratch_end(scratch2); + } + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: types pass 3: gather all unique namespaces from types + // + CV2R_NamespaceNode **all_namespace_slots = 0; + U64 all_namespace_slots_count = 0; + ProfScope("gather all unique namespaces from types") + { + Temp scratch2 = scratch_begin(&scratch.arena, 1); + + //- rjf: find all unique namespaces on this lane + U64 namespace_slots_count = (U64)itype_opl / lane_count(); + namespace_slots_count = Max(1, namespace_slots_count); + String8Node **namespace_slots = push_array(scratch2.arena, String8Node *, namespace_slots_count); + U64 namespace_count = 0; + Rng1U64 range = lane_range(itype_opl); + for EachInRange(idx, range) + { + CV_TypeId itype = (CV_TypeId)idx; + if(itype < itype_first) { continue; } + + // rjf: unpack itype info + CV_RecRange *range = &tpi_leaf->leaf_ranges.ranges[itype-tpi_leaf->itype_first]; + CV_LeafKind kind = range->hdr.kind; + U64 header_struct_size = cv_header_struct_size_from_leaf_kind(kind); + + // rjf: gather names from types + String8 name = {0}; + if(range->off+range->hdr.size <= tpi_leaf->data.size && + range->off+2+header_struct_size <= tpi_leaf->data.size && + range->hdr.size >= 2) + { + U8 *itype_leaf_first = tpi_leaf->data.str + range->off+2; + U8 *itype_leaf_opl = itype_leaf_first + range->hdr.size-2; + switch(kind) + { + default:{}break; + + //- rjf: CLASS/STRUCT + case CV_LeafKind_CLASS: + case CV_LeafKind_STRUCTURE: + { + CV_LeafStruct *lf_struct = (CV_LeafStruct *)itype_leaf_first; + U8 *numeric_ptr = (U8 *)(lf_struct + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U8 *name_ptr = (U8 *)numeric_ptr + size.encoded_size; + name = str8_cstring_capped(name_ptr, itype_leaf_opl); + }break; + + //- rjf: CLASS2/STRUCT2 + case CV_LeafKind_CLASS2: + case CV_LeafKind_STRUCT2: + { + CV_LeafStruct2 *lf_struct = (CV_LeafStruct2 *)itype_leaf_first; + U8 *numeric_ptr = (U8 *)(lf_struct + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U8 *name_ptr = (U8 *)numeric_ptr + size.encoded_size; + name = str8_cstring_capped(name_ptr, itype_leaf_opl); + }break; + + //- rjf: UNION + case CV_LeafKind_UNION: + { + CV_LeafUnion *lf_struct = (CV_LeafUnion *)itype_leaf_first; + U8 *numeric_ptr = (U8 *)(lf_struct + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U8 *name_ptr = (U8 *)numeric_ptr + size.encoded_size; + name = str8_cstring_capped(name_ptr, itype_leaf_opl); + }break; + + //- rjf: ENUM + case CV_LeafKind_ENUM: + { + CV_LeafEnum *lf_enum = (CV_LeafEnum *)itype_leaf_first; + U8 *name_ptr = (U8 *)(lf_enum+1); + name = str8_cstring_capped(name_ptr, itype_leaf_opl); + }break; + + //- rjf: ALIAS + case CV_LeafKind_ALIAS: + { + CV_LeafAlias *lf = (CV_LeafAlias *)itype_leaf_first; + U8 *name_ptr = (U8 *)(lf+1); + name = str8_cstring_capped(name_ptr, itype_leaf_opl); + }break; + } + } + + // rjf: add namespaces from strings + String8 name_before_templates = str8_prefix(name, str8_find_needle(name, 0, str8_lit("<"), 0)); + for(U64 scope_resolution_operator_pos = 0; + scope_resolution_operator_pos < name_before_templates.size; + scope_resolution_operator_pos = str8_find_needle(name_before_templates, scope_resolution_operator_pos+1, str8_lit("::"), 0)) + { + String8 namespace_fully_qualified_name = str8_substr(name_before_templates, r1u64(0, scope_resolution_operator_pos)); + if(namespace_fully_qualified_name.size != 0) + { + namespace_count += 1; + U64 hash = u64_hash_from_str8(namespace_fully_qualified_name); + U64 slot_idx = hash%namespace_slots_count; + B32 already_exists = 0; + for(String8Node *n = namespace_slots[slot_idx]; n != 0; n = n->next) + { + if(str8_match(n->string, namespace_fully_qualified_name, 0)) + { + already_exists = 1; + break; + } + } + if(!already_exists) + { + String8Node *node = push_array(scratch2.arena, String8Node, 1); + SLLStackPush(namespace_slots[slot_idx], node); + node->string = namespace_fully_qualified_name; + } + } + } + } + + //- rjf: on lane 0 -> combine all unique namespaces to singular map + { + // rjf: gather lane maps + typedef struct LaneNamespaceTable LaneNamespaceTable; + struct LaneNamespaceTable + { + String8Node **slots; + U64 slots_count; + }; + U64 total_namespace_count = 0; + U64 *total_namespace_count_ptr = &total_namespace_count; + LaneNamespaceTable *lane_namespace_tables = 0; + if(lane_idx() == 0) + { + lane_namespace_tables = push_array(scratch2.arena, LaneNamespaceTable, lane_count()); + } + lane_sync_u64(&total_namespace_count_ptr, 0); + lane_sync_u64(&lane_namespace_tables, 0); + lane_namespace_tables[lane_idx()].slots = namespace_slots; + lane_namespace_tables[lane_idx()].slots_count = namespace_slots_count; + ins_atomic_u64_add_eval(total_namespace_count_ptr, namespace_count); + lane_sync(); + + // rjf: combine + if(lane_idx() == 0) + { + all_namespace_slots_count = *total_namespace_count_ptr / 3; + all_namespace_slots_count = Max(all_namespace_slots_count, 1); + all_namespace_slots = push_array(scratch.arena, CV2R_NamespaceNode *, all_namespace_slots_count); + for EachIndex(l_idx, lane_count()) + { + LaneNamespaceTable *tbl = &lane_namespace_tables[l_idx]; + for EachIndex(slot_idx, tbl->slots_count) + { + for(String8Node *n = tbl->slots[slot_idx]; n != 0; n = n->next) + { + U64 hash = u64_hash_from_str8(n->string); + U64 dst_slot_idx = hash%all_namespace_slots_count; + B32 already_exists = 0; + for(CV2R_NamespaceNode *dst_n = all_namespace_slots[dst_slot_idx]; dst_n != 0; dst_n = dst_n->next) + { + if(str8_match(dst_n->string, n->string, 0)) + { + already_exists = 1; + break; + } + } + if(!already_exists) + { + CV2R_NamespaceNode *dst_n = push_array(scratch.arena, CV2R_NamespaceNode, 1); + dst_n->string = n->string; + SLLStackPush(all_namespace_slots[dst_slot_idx], dst_n); + } + } + } + } + } + lane_sync_u64(&all_namespace_slots_count, 0); + lane_sync_u64(&all_namespace_slots, 0); + } + scratch_end(scratch2); + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: types pass 4: construct all types from TPI + // + // this doesn't gather struct/class/union/enum members, which is done by + // subsequent passes, to build RDI "UDT" information, which is distinct + // from regular type info. + // + RDIM_TypeChunkList all_types__pre_typedefs = {0}; + RDIM_TypeChunkList *all_types__pre_typedefs_ptr = &all_types__pre_typedefs; + RDIM_Type **itype_type_ptrs = 0; + RDIM_Type **basic_type_ptrs = 0; + if(lane_idx() == 0) ProfScope("types pass 4: construct all root/stub types from TPI") + { +#define p2r_builtin_type_ptr_from_kind(kind) ((basic_type_ptrs && RDI_TypeKind_FirstBuiltIn <= (kind) && (kind) <= RDI_TypeKind_LastBuiltIn) ? (basic_type_ptrs[(kind) - RDI_TypeKind_FirstBuiltIn]) : 0) +#define p2r_type_ptr_from_itype(itype) ((itype_type_ptrs && (itype) < itype_opl) ? (itype_type_ptrs[(itype_fwd_map[(itype)] ? itype_fwd_map[(itype)] : (itype))]) : 0) + itype_type_ptrs = push_array(scratch.arena, RDIM_Type *, (U64)(itype_opl)); + basic_type_ptrs = push_array(scratch.arena, RDIM_Type *, (RDI_TypeKind_LastBuiltIn - RDI_TypeKind_FirstBuiltIn + 1)); + + //////////////////////////// + //- rjf: build basic types + // + if(params->subset_flags & RDIM_SubsetFlag_Types) + { + for(RDI_TypeKind type_kind = RDI_TypeKind_FirstBuiltIn; + type_kind <= RDI_TypeKind_LastBuiltIn; + type_kind += 1) + { + RDIM_Type *type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, 512); + type->name.str = rdi_string_from_type_kind(type_kind, &type->name.size); + type->kind = type_kind; + type->byte_size = rdi_size_from_basic_type_kind(type_kind); + basic_type_ptrs[type_kind - RDI_TypeKind_FirstBuiltIn] = type; + } + } + + //////////////////////////// + //- rjf: build basic type aliases + // + if(params->subset_flags & RDIM_SubsetFlag_Types) + { + RDIM_DataModel data_model = rdim_data_model_from_os_arch(OperatingSystem_Windows, arch); + RDI_TypeKind short_type = rdim_short_type_kind_from_data_model(data_model); + RDI_TypeKind ushort_type = rdim_unsigned_short_type_kind_from_data_model(data_model); + RDI_TypeKind long_type = rdim_long_type_kind_from_data_model(data_model); + RDI_TypeKind ulong_type = rdim_unsigned_long_type_kind_from_data_model(data_model); + RDI_TypeKind long_long_type = rdim_long_long_type_kind_from_data_model(data_model); + RDI_TypeKind ulong_long_type = rdim_unsigned_long_long_type_kind_from_data_model(data_model); + RDI_TypeKind ptr_type = rdim_pointer_size_t_type_kind_from_data_model(data_model); + struct + { + char * name; + RDI_TypeKind kind_rdi; + CV_LeafKind kind_cv; + } + table[] = + { + { "signed char" , RDI_TypeKind_Char8 , CV_BasicType_CHAR }, + { "short" , short_type , CV_BasicType_SHORT }, + { "long" , long_type , CV_BasicType_LONG }, + { "long long" , long_long_type , CV_BasicType_QUAD }, + { "__int128" , RDI_TypeKind_S128 , CV_BasicType_OCT }, // Clang type + { "unsigned char" , RDI_TypeKind_UChar8 , CV_BasicType_UCHAR }, + { "unsigned short" , ushort_type , CV_BasicType_USHORT }, + { "unsigned long" , ulong_type , CV_BasicType_ULONG }, + { "unsigned long long" , ulong_long_type , CV_BasicType_UQUAD }, + { "__uint128" , RDI_TypeKind_U128 , CV_BasicType_UOCT }, // Clang type + { "bool" , RDI_TypeKind_S8 , CV_BasicType_BOOL8 }, + { "__bool16" , RDI_TypeKind_S16 , CV_BasicType_BOOL16 }, // not real C type + { "__bool32" , RDI_TypeKind_S32 , CV_BasicType_BOOL32 }, // not real C type + { "float" , RDI_TypeKind_F32 , CV_BasicType_FLOAT32 }, + { "double" , RDI_TypeKind_F64 , CV_BasicType_FLOAT64 }, + { "long double" , RDI_TypeKind_F80 , CV_BasicType_FLOAT80 }, + { "__float128" , RDI_TypeKind_F128 , CV_BasicType_FLOAT128 }, // Clang type + { "__float48" , RDI_TypeKind_F48 , CV_BasicType_FLOAT48 }, // not real C type + { "__float32pp" , RDI_TypeKind_F32PP , CV_BasicType_FLOAT32PP }, // not real C type + { "__float16" , RDI_TypeKind_F16 , CV_BasicType_FLOAT16 }, + { "_Complex float" , RDI_TypeKind_ComplexF32 , CV_BasicType_COMPLEX32 }, + { "_Complex double" , RDI_TypeKind_ComplexF64 , CV_BasicType_COMPLEX64 }, + { "_Complex long double" , RDI_TypeKind_ComplexF80 , CV_BasicType_COMPLEX80 }, + { "_Complex __float128" , RDI_TypeKind_ComplexF128, CV_BasicType_COMPLEX128 }, + { "__int8" , RDI_TypeKind_S8 , CV_BasicType_INT8 }, + { "__uint8" , RDI_TypeKind_U8 , CV_BasicType_UINT8 }, + { "__int16" , RDI_TypeKind_S16 , CV_BasicType_INT16 }, + { "__uint16" , RDI_TypeKind_U16 , CV_BasicType_UINT16 }, + { "int" , RDI_TypeKind_S32 , CV_BasicType_INT32 }, + { "int32" , RDI_TypeKind_S32 , CV_BasicType_INT32 }, + { "uint32" , RDI_TypeKind_U32 , CV_BasicType_UINT32 }, + { "__int64" , RDI_TypeKind_S64 , CV_BasicType_INT64 }, + { "__uint64" , RDI_TypeKind_U64 , CV_BasicType_UINT64 }, + { "__int128" , RDI_TypeKind_S128 , CV_BasicType_INT128 }, + { "__uint128" , RDI_TypeKind_U128 , CV_BasicType_UINT128 }, + { "char" , RDI_TypeKind_Char8 , CV_BasicType_RCHAR }, // always ASCII + { "wchar_t" , RDI_TypeKind_UChar16 , CV_BasicType_WCHAR }, // on windows always UTF-16 + { "char8_t" , RDI_TypeKind_Char8 , CV_BasicType_CHAR8 }, // always UTF-8 + { "char16_t" , RDI_TypeKind_Char16 , CV_BasicType_CHAR16 }, // always UTF-16 + { "char32_t" , RDI_TypeKind_Char32 , CV_BasicType_CHAR32 }, // always UTF-32 + { "__pointer" , ptr_type , CV_BasicType_PTR } + }; + for EachElement(idx, table) + { + RDIM_Type *builtin_alias = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, tpi_leaf->itype_opl); + builtin_alias->kind = RDI_TypeKind_Alias; + builtin_alias->name = str8_cstring(table[idx].name); + builtin_alias->direct_type = p2r_builtin_type_ptr_from_kind(table[idx].kind_rdi); + builtin_alias->byte_size = rdi_size_from_basic_type_kind(table[idx].kind_rdi); + itype_type_ptrs[table[idx].kind_cv] = builtin_alias; + } + itype_type_ptrs[CV_BasicType_HRESULT] = basic_type_ptrs[RDI_TypeKind_HResult - RDI_TypeKind_FirstBuiltIn]; + itype_type_ptrs[CV_BasicType_VOID] = basic_type_ptrs[RDI_TypeKind_Void - RDI_TypeKind_FirstBuiltIn]; + } + + //////////////////////////// + //- rjf: build types from TPI + // + if(params->subset_flags & RDIM_SubsetFlag_Types) + { + for(CV_TypeId root_itype = 0; root_itype < itype_opl; root_itype += 1) + { + for(CV2R_TypeIdChain *itype_chain = itype_chains[root_itype]; + itype_chain != 0; + itype_chain = itype_chain->next) + { + CV_TypeId itype = (root_itype != itype_chain->itype && itype_chain->itype < itype_opl && itype_fwd_map[itype_chain->itype]) ? itype_fwd_map[itype_chain->itype] : itype_chain->itype; + B32 itype_is_basic = (itype < tpi_leaf->itype_first); + + ////////////////////////// + //- rjf: skip forward-reference itypes - all future resolutions will + // reference whatever this itype resolves to, and so there is no point + // in filling out this slot + // + if(itype_fwd_map[root_itype] != 0) + { + continue; + } + + ////////////////////////// + //- rjf: skip already produced dependencies + // + if(itype_type_ptrs[itype] != 0) + { + continue; + } + + ////////////////////////// + //- rjf: build basic type + // + if(itype_is_basic) + { + RDIM_Type *dst_type = 0; + + // rjf: unpack itype + CV_BasicPointerKind cv_basic_ptr_kind = CV_BasicPointerKindFromTypeId(itype); + CV_BasicType cv_basic_type_code = CV_BasicTypeFromTypeId(itype); + + // rjf: get basic type slot, fill if unfilled + RDIM_Type *basic_type = itype_type_ptrs[cv_basic_type_code]; + if(basic_type == 0) + { + RDI_TypeKind type_kind = p2r_rdi_type_kind_from_cv_basic_type(cv_basic_type_code); + U32 byte_size = rdi_size_from_basic_type_kind(type_kind); + basic_type = dst_type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, (U64)itype_opl); + if(byte_size == 0xffffffff) + { + byte_size = arch_addr_size; + } + basic_type->kind = type_kind; + basic_type->name = cv_type_name_from_basic_type(cv_basic_type_code); + basic_type->byte_size = byte_size; + } + + // rjf: nonzero ptr kind -> form ptr type to basic tpye + if(cv_basic_ptr_kind != 0) + { + dst_type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, (U64)itype_opl); + dst_type->kind = RDI_TypeKind_Ptr; + dst_type->byte_size = arch_addr_size; + dst_type->direct_type = basic_type; + } + + // rjf: fill this itype's slot with the finished type + itype_type_ptrs[itype] = dst_type; + } + + ////////////////////////// + //- rjf: build non-basic type + // + if(!itype_is_basic && itype >= itype_first) + { + RDIM_Type *dst_type = 0; + CV_RecRange *range = &tpi_leaf->leaf_ranges.ranges[itype-itype_first]; + CV_LeafKind kind = range->hdr.kind; + U64 header_struct_size = cv_header_struct_size_from_leaf_kind(kind); + if(range->off+range->hdr.size <= tpi_leaf->data.size && + range->off+2+header_struct_size <= tpi_leaf->data.size && + range->hdr.size >= 2) + { + U8 *itype_leaf_first = tpi_leaf->data.str + range->off+2; + U8 *itype_leaf_opl = itype_leaf_first + range->hdr.size-2; + switch(kind) + { + //- rjf: MODIFIER + case CV_LeafKind_MODIFIER: + { + // rjf: unpack leaf + CV_LeafModifier *lf = (CV_LeafModifier *)itype_leaf_first; + + // rjf: cv -> rdi flags + RDI_TypeModifierFlags flags = 0; + if(lf->flags & CV_ModifierFlag_Const) {flags |= RDI_TypeModifierFlag_Const;} + if(lf->flags & CV_ModifierFlag_Volatile) {flags |= RDI_TypeModifierFlag_Volatile;} + + // rjf: fill type + if(flags == 0) + { + dst_type = p2r_type_ptr_from_itype(lf->itype); + } + else + { + dst_type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, (U64)itype_opl); + dst_type->kind = RDI_TypeKind_Modifier; + dst_type->flags = flags; + dst_type->direct_type = p2r_type_ptr_from_itype(lf->itype); + dst_type->byte_size = dst_type->direct_type ? dst_type->direct_type->byte_size : 0; + } + }break; + + //- rjf: POINTER + case CV_LeafKind_POINTER: + { + // TODO(rjf): if ptr_mode in {PtrMem, PtrMethod} then output a member pointer instead + + // rjf: unpack leaf + CV_LeafPointer *lf = (CV_LeafPointer *)itype_leaf_first; + RDIM_Type *direct_type = p2r_type_ptr_from_itype(lf->itype); + CV_PointerKind ptr_kind = CV_PointerAttribs_Extract_Kind(lf->attribs); + CV_PointerMode ptr_mode = CV_PointerAttribs_Extract_Mode(lf->attribs); + U32 ptr_size = CV_PointerAttribs_Extract_Size(lf->attribs); + + // rjf: cv -> rdi modifier flags + RDI_TypeModifierFlags modifier_flags = 0; + if(lf->attribs & CV_PointerAttrib_Const) {modifier_flags |= RDI_TypeModifierFlag_Const;} + if(lf->attribs & CV_PointerAttrib_Volatile) {modifier_flags |= RDI_TypeModifierFlag_Volatile;} + if(lf->attribs & CV_PointerAttrib_Restricted) {modifier_flags |= RDI_TypeModifierFlag_Restrict;} + + // rjf: cv info -> rdi pointer type kind + RDI_TypeKind type_kind = RDI_TypeKind_Ptr; + { + if(lf->attribs & CV_PointerAttrib_LRef) + { + type_kind = RDI_TypeKind_LRef; + } + else if(lf->attribs & CV_PointerAttrib_RRef) + { + type_kind = RDI_TypeKind_RRef; + } + if(ptr_mode == CV_PointerMode_LRef) + { + type_kind = RDI_TypeKind_LRef; + } + else if(ptr_mode == CV_PointerMode_RRef) + { + type_kind = RDI_TypeKind_RRef; + } + } + + // rjf: fill type + if(modifier_flags != 0) + { + RDIM_Type *pointer_type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, (U64)itype_opl); + dst_type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, (U64)itype_opl); + dst_type->kind = RDI_TypeKind_Modifier; + dst_type->flags = modifier_flags; + dst_type->direct_type = pointer_type; + dst_type->byte_size = arch_addr_size; + pointer_type->kind = type_kind; + pointer_type->byte_size = arch_addr_size; + pointer_type->direct_type = direct_type; + } + else + { + dst_type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, (U64)itype_opl); + dst_type->kind = type_kind; + dst_type->byte_size = arch_addr_size; + dst_type->direct_type = direct_type; + } + }break; + + //- rjf: PROCEDURE + case CV_LeafKind_PROCEDURE: + { + // TODO(rjf): handle call_kind & attribs + + // rjf: unpack leaf + CV_LeafProcedure *lf = (CV_LeafProcedure *)itype_leaf_first; + RDIM_Type *ret_type = p2r_type_ptr_from_itype(lf->ret_itype); + + // rjf: fill type's basics + dst_type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, (U64)itype_opl); + dst_type->kind = RDI_TypeKind_Function; + dst_type->byte_size = arch_addr_size; + dst_type->direct_type = ret_type; + + // rjf: unpack arglist range + CV_RecRange *arglist_range = &tpi_leaf->leaf_ranges.ranges[lf->arg_itype-itype_first]; + if(arglist_range->hdr.kind != CV_LeafKind_ARGLIST || + arglist_range->hdr.size<2 || + arglist_range->off + arglist_range->hdr.size > tpi_leaf->data.size) + { + break; + } + U8 *arglist_first = tpi_leaf->data.str + arglist_range->off + 2; + U8 *arglist_opl = arglist_first+arglist_range->hdr.size-2; + if(arglist_first + sizeof(CV_LeafArgList) > arglist_opl) + { + break; + } + + // rjf: unpack arglist info + CV_LeafArgList *arglist = (CV_LeafArgList*)arglist_first; + CV_TypeId *arglist_itypes_base = (CV_TypeId *)(arglist+1); + U32 arglist_itypes_count = arglist->count; + + // rjf: count non-zero arguments + U32 arglist_itypes_nonzero_count = 0; + for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) + { + if(arglist_itypes_base[idx] != 0) + { + arglist_itypes_nonzero_count += 1; + } + } + + // rjf: build param type array + RDIM_Type **params = push_array(arena, RDIM_Type *, arglist_itypes_nonzero_count); + { + U64 dst_idx = 0; + for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) + { + if(arglist_itypes_base[idx] != 0) + { + params[dst_idx] = p2r_type_ptr_from_itype(arglist_itypes_base[idx]); + dst_idx += 1; + } + } + } + + // rjf: fill dst type + dst_type->count = arglist_itypes_nonzero_count; + dst_type->param_types = params; + }break; + + //- rjf: MFUNCTION + case CV_LeafKind_MFUNCTION: + { + // TODO(rjf): handle call_kind & attribs + // TODO(rjf): preserve "this_adjust" + + // rjf: unpack leaf + CV_LeafMFunction *lf = (CV_LeafMFunction *)itype_leaf_first; + RDIM_Type *ret_type = p2r_type_ptr_from_itype(lf->ret_itype); + + // rjf: fill type + dst_type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, (U64)itype_opl); + dst_type->kind = (lf->this_itype != 0) ? RDI_TypeKind_Method : RDI_TypeKind_Function; + dst_type->byte_size = arch_addr_size; + dst_type->direct_type = ret_type; + + // rjf: unpack arglist range + CV_RecRange *arglist_range = &tpi_leaf->leaf_ranges.ranges[lf->arg_itype-itype_first]; + if(arglist_range->hdr.kind != CV_LeafKind_ARGLIST || + arglist_range->hdr.size<2 || + arglist_range->off + arglist_range->hdr.size > tpi_leaf->data.size) + { + break; + } + U8 *arglist_first = tpi_leaf->data.str + arglist_range->off + 2; + U8 *arglist_opl = arglist_first+arglist_range->hdr.size-2; + if(arglist_first + sizeof(CV_LeafArgList) > arglist_opl) + { + break; + } + + // rjf: unpack arglist info + CV_LeafArgList *arglist = (CV_LeafArgList*)arglist_first; + CV_TypeId *arglist_itypes_base = (CV_TypeId *)(arglist+1); + U32 arglist_itypes_count = arglist->count; + + // rjf: build param type array + U64 num_this_extras = 1; + if(lf->this_itype == 0) + { + num_this_extras = 0; + } + RDIM_Type **params = push_array(arena, RDIM_Type *, arglist_itypes_count+num_this_extras); + for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) + { + params[idx+num_this_extras] = p2r_type_ptr_from_itype(arglist_itypes_base[idx]); + } + if(lf->this_itype != 0) + { + params[0] = p2r_type_ptr_from_itype(lf->this_itype); + } + + // rjf: fill dst type + dst_type->count = arglist_itypes_count+num_this_extras; + dst_type->param_types = params; + }break; + + //- rjf: BITFIELD + case CV_LeafKind_BITFIELD: + { + // rjf: unpack leaf + CV_LeafBitField *lf = (CV_LeafBitField *)itype_leaf_first; + RDIM_Type *direct_type = p2r_type_ptr_from_itype(lf->itype); + + // rjf: fill type + dst_type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, (U64)itype_opl); + dst_type->kind = RDI_TypeKind_Bitfield; + dst_type->off = lf->pos; + dst_type->count = lf->len; + dst_type->byte_size = direct_type?direct_type->byte_size:0; + dst_type->direct_type = direct_type; + }break; + + //- rjf: ARRAY + case CV_LeafKind_ARRAY: + { + // rjf: unpack leaf + CV_LeafArray *lf = (CV_LeafArray *)itype_leaf_first; + RDIM_Type *direct_type = p2r_type_ptr_from_itype(lf->entry_itype); + U8 *numeric_ptr = (U8*)(lf + 1); + CV_NumericParsed array_count = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U64 full_size = cv_u64_from_numeric(&array_count); + + // rjf: fill type + dst_type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, (U64)itype_opl); + dst_type->kind = RDI_TypeKind_Array; + dst_type->direct_type = direct_type; + dst_type->byte_size = full_size; + dst_type->count = (direct_type && direct_type->byte_size) ? (dst_type->byte_size/direct_type->byte_size) : 0; + }break; + + //- rjf: CLASS/STRUCTURE + case CV_LeafKind_CLASS: + case CV_LeafKind_STRUCTURE: + { + // TODO(rjf): handle props + + // rjf: unpack leaf + CV_LeafStruct *lf = (CV_LeafStruct *)itype_leaf_first; + U8 *numeric_ptr = (U8*)(lf + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U64 size_u64 = cv_u64_from_numeric(&size); + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + + // rjf: fill type + dst_type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, (U64)itype_opl); + if(lf->props & CV_TypeProp_FwdRef) + { + dst_type->kind = (kind == CV_LeafKind_CLASS ? RDI_TypeKind_IncompleteClass : RDI_TypeKind_IncompleteStruct); + dst_type->name = name; + } + else + { + dst_type->kind = (kind == CV_LeafKind_CLASS ? RDI_TypeKind_Class : RDI_TypeKind_Struct); + dst_type->byte_size = (U32)size_u64; + dst_type->name = name; + } + }break; + + //- rjf: CLASS2/STRUCT2 + case CV_LeafKind_CLASS2: + case CV_LeafKind_STRUCT2: + { + // TODO(rjf): handle props + + // rjf: unpack leaf + CV_LeafStruct2 *lf = (CV_LeafStruct2 *)itype_leaf_first; + U8 *numeric_ptr = (U8*)(lf + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U64 size_u64 = cv_u64_from_numeric(&size); + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + + // rjf: fill type + dst_type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, (U64)itype_opl); + if(lf->props & CV_TypeProp_FwdRef) + { + dst_type->kind = (kind == CV_LeafKind_CLASS2 ? RDI_TypeKind_IncompleteClass : RDI_TypeKind_IncompleteStruct); + dst_type->name = name; + } + else + { + dst_type->kind = (kind == CV_LeafKind_CLASS2 ? RDI_TypeKind_Class : RDI_TypeKind_Struct); + dst_type->byte_size = (U32)size_u64; + dst_type->name = name; + } + }break; + + //- rjf: alias + case CV_LeafKind_ALIAS: + { + // rjf: unpack leaf + CV_LeafAlias *lf = (CV_LeafAlias *)itype_leaf_first; + U8 *name_ptr = (U8 *)(lf+1); + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + + // rjf: fill type + dst_type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, (U64)itype_opl); + dst_type->kind = RDI_TypeKind_Alias; + dst_type->name = name; + dst_type->direct_type = p2r_type_ptr_from_itype(lf->itype); + if(dst_type->direct_type != 0) + { + dst_type->byte_size = dst_type->direct_type->byte_size; + } + }break; + + //- rjf: UNION + case CV_LeafKind_UNION: + { + // TODO(rjf): handle props + + // rjf: unpack leaf + CV_LeafUnion *lf = (CV_LeafUnion *)itype_leaf_first; + U8 *numeric_ptr = (U8*)(lf + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U64 size_u64 = cv_u64_from_numeric(&size); + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + + // rjf: fill type + dst_type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, (U64)itype_opl); + if(lf->props & CV_TypeProp_FwdRef) + { + dst_type->kind = RDI_TypeKind_IncompleteUnion; + dst_type->name = name; + } + else + { + dst_type->kind = RDI_TypeKind_Union; + dst_type->byte_size = (U32)size_u64; + dst_type->name = name; + } + }break; + + //- rjf: ENUM + case CV_LeafKind_ENUM: + { + // TODO(rjf): handle props + + // rjf: unpack leaf + CV_LeafEnum *lf = (CV_LeafEnum *)itype_leaf_first; + RDIM_Type *direct_type = p2r_type_ptr_from_itype(lf->base_itype); + U8 *name_ptr = (U8 *)(lf + 1); + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + + // rjf: fill type + dst_type = rdim_type_chunk_list_push(arena, all_types__pre_typedefs_ptr, (U64)itype_opl); + if(lf->props & CV_TypeProp_FwdRef) + { + dst_type->kind = RDI_TypeKind_IncompleteEnum; + dst_type->name = name; + } + else + { + dst_type->kind = RDI_TypeKind_Enum; + dst_type->direct_type = direct_type; + dst_type->byte_size = direct_type ? direct_type->byte_size : 0; + dst_type->name = name; + } + }break; + } + } + + //- rjf: store finalized type to this itype's slot + itype_type_ptrs[itype] = dst_type; + } + } + } + } +#undef p2r_type_ptr_from_itype +#undef p2r_builtin_type_ptr_from_kind + } + lane_sync_u64(&itype_type_ptrs, 0); + lane_sync_u64(&basic_type_ptrs, 0); + lane_sync_u64(&all_types__pre_typedefs_ptr, 0); + all_types__pre_typedefs = *all_types__pre_typedefs_ptr; + + ////////////////////////////////////////////////////////////// + //- rjf: set bit in all namespace nodes that correspond to scopes + // + if(params->subset_flags & (RDIM_SubsetFlag_Procedures| + RDIM_SubsetFlag_GlobalVariables| + RDIM_SubsetFlag_ThreadVariables| + RDIM_SubsetFlag_Scopes| + RDIM_SubsetFlag_Locals| + RDIM_SubsetFlag_GlobalVariableNameMap| + RDIM_SubsetFlag_ThreadVariableNameMap| + RDIM_SubsetFlag_ProcedureNameMap| + RDIM_SubsetFlag_ConstantNameMap| + RDIM_SubsetFlag_LinkNameProcedureNameMap| + RDIM_SubsetFlag_Types)) + ProfScope("determine which namespace nodes correspond to scopes") + { + U64 *sym_take_counter = lane_idx() == 0 ? push_array(scratch.arena, U64, 1) : 0; + lane_sync_u64(&sym_take_counter, 0); + for(;;) + { + U64 sym_idx = ins_atomic_u64_inc_eval(sym_take_counter) - 1; + if(sym_idx >= all_syms_count) + { + break; + } + CV_SymParsed *sym = all_syms[sym_idx]; + for(CV_RecIter iter = {0}; cv_rec_next(sym->data, &sym->sym_ranges, 0, &iter);) + { + switch(iter.kind) + { + default:{}break; + case CV_SymKind_LPROC32: + case CV_SymKind_GPROC32: + { + CV_SymProc32 *proc32 = (CV_SymProc32 *)iter.struct_base; + String8 name = str8_cstring_capped(proc32+1, iter.opl); + U64 hash = u64_hash_from_str8(name); + U64 slot_idx = hash%all_namespace_slots_count; + for(CV2R_NamespaceNode *n = all_namespace_slots[slot_idx]; n != 0; n = n->next) + { + if(str8_match(n->string, name, 0)) + { + ins_atomic_u32_eval_assign(&n->corresponds_to_scope, 1); + break; + } + } + }break; + } + } + } + } + + ////////////////////////////////////////////////////////////// + //- rjf: gather all namespaces which are only encoded in symbols (not found in types) + // + if(params->subset_flags & (RDIM_SubsetFlag_Procedures| + RDIM_SubsetFlag_GlobalVariables| + RDIM_SubsetFlag_ThreadVariables| + RDIM_SubsetFlag_Scopes| + RDIM_SubsetFlag_Locals| + RDIM_SubsetFlag_GlobalVariableNameMap| + RDIM_SubsetFlag_ThreadVariableNameMap| + RDIM_SubsetFlag_ProcedureNameMap| + RDIM_SubsetFlag_ConstantNameMap| + RDIM_SubsetFlag_LinkNameProcedureNameMap| + RDIM_SubsetFlag_Types)) + ProfScope("gather all namespaces which are only encoded in symbols (not found in types)") + { + Temp scratch2 = scratch_begin(&scratch.arena, 1); + U64 lane_namespace_slots_count = 4096; + String8Node **lane_namespace_slots = push_array(scratch2.arena, String8Node *, lane_namespace_slots_count); + + //- rjf: gather from syms + U64 *sym_take_counter = lane_idx() == 0 ? push_array(scratch2.arena, U64, 1) : 0; + lane_sync_u64(&sym_take_counter, 0); + for(;;) + { + U64 sym_idx = ins_atomic_u64_inc_eval(sym_take_counter) - 1; + if(sym_idx >= all_syms_count) + { + break; + } + CV_SymParsed *sym = all_syms[sym_idx]; + for(CV_RecIter iter = {0}; cv_rec_next(sym->data, &sym->sym_ranges, 0, &iter);) + { + // rjf: get global symbol name + String8 symbol_name = {0}; + switch(iter.kind) + { + default:{}break; + case CV_SymKind_GDATA32: + { + CV_SymData32 *data32 = (CV_SymData32 *)iter.struct_base; + symbol_name = str8_cstring_capped(data32+1, iter.opl); + }break; + case CV_SymKind_GPROC32: + { + CV_SymProc32 *proc32 = (CV_SymProc32 *)iter.struct_base; + symbol_name = str8_cstring_capped(proc32+1, iter.opl); + }break; + case CV_SymKind_GTHREAD32: + { + CV_SymThread32 *thread32 = (CV_SymThread32 *)iter.struct_base; + symbol_name = str8_cstring_capped(thread32+1, iter.opl); + }break; + } + + // rjf: symbol name -> container name + String8 container_name = str8_chop(str8_prefix(symbol_name, p2r_end_of_cplusplus_container_name(symbol_name)), 2); + + // rjf: non-empty container name -> gather + if(container_name.size != 0) + { + U64 container_name_hash = u64_hash_from_str8(container_name); + + // rjf: first, check if this container already showed up from type info + B32 already_exists = 0; + if(!already_exists) + { + U64 slot_idx = container_name_hash%all_namespace_slots_count; + for(CV2R_NamespaceNode *n = all_namespace_slots[slot_idx]; n != 0; n = n->next) + { + if(str8_match(n->string, container_name, 0)) + { + already_exists = 1; + break; + } + } + } + + // rjf: next, check if we've already gathered this namespace for this lane + if(!already_exists) + { + U64 slot_idx = container_name_hash%lane_namespace_slots_count; + for(String8Node *n = lane_namespace_slots[slot_idx]; n != 0; n = n->next) + { + if(str8_match(n->string, container_name, 0)) + { + already_exists = 1; + break; + } + } + } + + // rjf: if we didn't find this namespace in either those from types, or already found in this lane, then gather + if(!already_exists) + { + U64 slot_idx = container_name_hash%lane_namespace_slots_count; + String8Node *n = push_array(scratch2.arena, String8Node, 1); + SLLStackPush(lane_namespace_slots[slot_idx], n); + n->string = container_name; + } + } + } + } + lane_sync(); + + //- rjf: combine + fold into the all_namespaces table + { + // rjf: gather lane maps + typedef struct LaneNamespaceTable LaneNamespaceTable; + struct LaneNamespaceTable + { + String8Node **slots; + U64 slots_count; + }; + LaneNamespaceTable *lane_namespace_tables = 0; + if(lane_idx() == 0) + { + lane_namespace_tables = push_array(scratch2.arena, LaneNamespaceTable, lane_count()); + } + lane_sync_u64(&lane_namespace_tables, 0); + lane_namespace_tables[lane_idx()].slots = lane_namespace_slots; + lane_namespace_tables[lane_idx()].slots_count = lane_namespace_slots_count; + lane_sync(); + + // rjf: combine + if(lane_idx() == 0) + { + for EachIndex(l_idx, lane_count()) + { + LaneNamespaceTable *tbl = &lane_namespace_tables[l_idx]; + for EachIndex(slot_idx, tbl->slots_count) + { + for(String8Node *n = tbl->slots[slot_idx]; n != 0; n = n->next) + { + U64 hash = u64_hash_from_str8(n->string); + U64 dst_slot_idx = hash%all_namespace_slots_count; + B32 already_exists = 0; + for(CV2R_NamespaceNode *dst_n = all_namespace_slots[dst_slot_idx]; dst_n != 0; dst_n = dst_n->next) + { + if(str8_match(dst_n->string, n->string, 0)) + { + already_exists = 1; + break; + } + } + if(!already_exists) + { + CV2R_NamespaceNode *dst_n = push_array(scratch.arena, CV2R_NamespaceNode, 1); + dst_n->string = n->string; + SLLStackPush(all_namespace_slots[dst_slot_idx], dst_n); + } + } + } + } + } + } + lane_sync(); + scratch_end(scratch2); + } + + ////////////////////////////////////////////////////////////// + //- rjf: upgrade namespace nodes with type info, if they match a type + // + ProfScope("upgrade namespace nodes with type info, if they match a type") + { +#define p2r_type_ptr_from_itype(itype) ((itype_type_ptrs && (itype) < tpi_leaf->itype_opl) ? (itype_type_ptrs[(itype_fwd_map[(itype)] ? itype_fwd_map[(itype)] : (itype))]) : 0) + Rng1U64 range = lane_range(all_namespace_slots_count); + for EachInRange(slot_idx, range) + { + for(CV2R_NamespaceNode *n = all_namespace_slots[slot_idx]; n != 0; n = n->next) + { + String8 container_string = n->string; + CV_TypeId container_type_id = cv2r_first_itype_from_name(tpi_hash, tpi_leaf, container_string, 0); + if(container_type_id != 0) + { + n->type = p2r_type_ptr_from_itype(container_type_id); + } + } + } +#undef p2r_type_ptr_from_itype + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: build namespaces, that are not scopes, nor types + // + RDIM_NamespaceChunkList all_namespaces = {0}; + { + // rjf: gather all per-lane namespaces + RDIM_NamespaceChunkList lane_namespaces = {0}; + Rng1U64 range = lane_range(all_namespace_slots_count); + for EachInRange(slot_idx, range) + { + Temp scratch = scratch_begin(&arena, 1); + + // rjf: gather nodes, sort + U64 node_count = 0; + for(CV2R_NamespaceNode *n = all_namespace_slots[slot_idx]; n != 0; n = n->next) { node_count += 1; } + CV2R_NamespaceNode **nodes = push_array(scratch.arena, CV2R_NamespaceNode *, node_count); + { + U64 idx = 0; + for(CV2R_NamespaceNode *n = all_namespace_slots[slot_idx]; n != 0; n = n->next, idx += 1) + { + nodes[idx] = n; + } + } + radsort(nodes, node_count, p2r_namespace_node_is_before); + + // rjf: build namespaces for this slot + for EachIndex(node_in_slot_idx, node_count) + { + CV2R_NamespaceNode *n = nodes[node_in_slot_idx]; + if(n->corresponds_to_scope || n->scope != 0 || n->type != 0) { continue; } + String8 string = n->string; + RDIM_Namespace *ns = rdim_namespace_chunk_list_push(arena, &lane_namespaces, 32); + ns->name = string; + n->ns = ns; + } + + scratch_end(scratch); + } + + // rjf: combine all per-lane namespaces + RDIM_NamespaceChunkList *lanes_namespaces = 0; + if(lane_idx() == 0) + { + lanes_namespaces = push_array(scratch.arena, RDIM_NamespaceChunkList, lane_count()); + } + lane_sync_u64(&lanes_namespaces, 0); + lanes_namespaces[lane_idx()] = lane_namespaces; + lane_sync(); + + // rjf: join all per-lane namespaces + RDIM_NamespaceChunkList *all_namespaces_ptr = &all_namespaces; + lane_sync_u64(&all_namespaces_ptr, 0); + if(lane_idx() == 0) + { + for EachIndex(l_idx, lane_count()) + { + rdim_namespace_chunk_list_concat_in_place(all_namespaces_ptr, &lanes_namespaces[l_idx]); + } + } + lane_sync(); + all_namespaces = *all_namespaces_ptr; + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: convert symbols from all units + // + typedef struct ScopeNamespaceNode ScopeNamespaceNode; + struct ScopeNamespaceNode + { + ScopeNamespaceNode *next; + String8 string; + RDIM_Scope *scope; + }; + typedef struct ScopeNamespaceList ScopeNamespaceList; + struct ScopeNamespaceList + { + ScopeNamespaceNode *first; + ScopeNamespaceNode *last; + }; + RDIM_TypeChunkList *syms_typedefs = 0; + ScopeNamespaceList *syms_scopes_that_are_namespaces = 0; + ProfScope("produce symbols from all streams") + { +#define p2r_type_ptr_from_itype(itype) ((itype_type_ptrs && (itype) < itype_opl) ? (itype_type_ptrs[(itype_fwd_map[(itype)] ? itype_fwd_map[(itype)] : (itype))]) : 0) + + //////////////////////////// + //- rjf: set up + // + if(lane_idx() == 0) + { + syms_typedefs = push_array(arena, RDIM_TypeChunkList, all_syms_count); + syms_scopes_that_are_namespaces = push_array(arena, ScopeNamespaceList, all_syms_count); + } + lane_sync_u64(&syms_typedefs, 0); + lane_sync_u64(&syms_scopes_that_are_namespaces, 0); + + //////////////////////////// + //- rjf: fill outputs for all unit sym blocks in this lane + // + if(params->subset_flags & (RDIM_SubsetFlag_Procedures| + RDIM_SubsetFlag_GlobalVariables| + RDIM_SubsetFlag_ThreadVariables| + RDIM_SubsetFlag_Scopes| + RDIM_SubsetFlag_Locals| + RDIM_SubsetFlag_GlobalVariableNameMap| + RDIM_SubsetFlag_ThreadVariableNameMap| + RDIM_SubsetFlag_ProcedureNameMap| + RDIM_SubsetFlag_ConstantNameMap| + RDIM_SubsetFlag_LinkNameProcedureNameMap| + RDIM_SubsetFlag_Types)) + { + U64 *sym_take_counter = lane_idx() == 0 ? push_array(scratch.arena, U64, 1) : 0; + lane_sync_u64(&sym_take_counter, 0); + for(;;) + { + //- rjf: take next sym + U64 sym_idx = ins_atomic_u64_inc_eval(sym_take_counter) - 1; + if(sym_idx >= all_syms_count) + { + break; + } + + //- rjf: unpack sym + Temp scratch = scratch_begin(&arena, 1); + CV_SymParsed *sym = all_syms[sym_idx]; + RDIM_Unit *sym_unit = &all_units_ptr->first->v[sym_idx]; + RDIM_SymbolChunkList *sym_procedures = &sym_unit->procedures; + RDIM_SymbolChunkList *sym_global_variables = &sym_unit->global_variables; + RDIM_SymbolChunkList *sym_thread_variables = &sym_unit->thread_variables; + RDIM_SymbolChunkList *sym_constants = &sym_unit->constants; + RDIM_ScopeChunkList *sym_scopes = &sym_unit->scopes; + RDIM_InlineSiteChunkList *sym_inline_sites = &sym_unit->inline_sites; + RDIM_TypeChunkList *typedefs = &syms_typedefs[sym_idx]; + + ////////////////////////// + //- rjf: symbols pass 0: predict symbol chunk counts by record kinds + // + U64 sym_procedures_chunk_cap = sym->sym_ranges.count/4 + 1; + U64 sym_global_variables_chunk_cap = sym->sym_ranges.count/12 + 1; + U64 sym_thread_variables_chunk_cap = sym->sym_ranges.count/12 + 1; + U64 sym_constants_chunk_cap = sym->sym_ranges.count/6 + 1; + U64 sym_scopes_chunk_cap = sym->sym_ranges.count/4 + 1; + U64 sym_inline_sites_chunk_cap = sym->sym_ranges.count/6 + 1; + ProfScope("symbols pass 0: predict symbol chunk counts by record kinds") + { + U64 procedure_record_count = 0; + U64 global_variable_record_count = 0; + U64 thread_variable_record_count = 0; + U64 constant_record_count = 0; + U64 scope_record_count = 0; + U64 inline_site_record_count = 0; + for(CV_RecIter iter = {0}; cv_rec_next(sym->data, &sym->sym_ranges, 0, &iter);) + { + switch(iter.kind) + { + default:{}break; + case CV_SymKind_LPROC32: + case CV_SymKind_GPROC32: + case CV_SymKind_THUNK32: + { + procedure_record_count += 1; + scope_record_count += 1; + }break; + case CV_SymKind_BLOCK32: + { + scope_record_count += 1; + }break; + case CV_SymKind_INLINESITE: + { + scope_record_count += 1; + inline_site_record_count += 1; + }break; + case CV_SymKind_LDATA32: + case CV_SymKind_GDATA32: + { + global_variable_record_count += 1; + }break; + case CV_SymKind_CONSTANT: + { + constant_record_count += 1; + }break; + case CV_SymKind_LTHREAD32: + case CV_SymKind_GTHREAD32: + { + thread_variable_record_count += 1; + }break; + } + } + sym_procedures_chunk_cap = Max(1, procedure_record_count); + sym_global_variables_chunk_cap = Max(1, global_variable_record_count); + sym_thread_variables_chunk_cap = Max(1, thread_variable_record_count); + sym_constants_chunk_cap = Max(1, constant_record_count); + sym_scopes_chunk_cap = Max(1, scope_record_count); + sym_inline_sites_chunk_cap = Max(1, inline_site_record_count); + } + + ////////////////////////// + //- rjf: symbols pass 1: produce procedure frame info map (procedure -> frame info) + // + U64 procedure_frameprocs_count = 0; + U64 procedure_frameprocs_cap = sym->sym_ranges.count; + CV_SymFrameproc **procedure_frameprocs = push_array_no_zero(scratch.arena, CV_SymFrameproc *, procedure_frameprocs_cap); + ProfScope("symbols pass 1: produce procedure frame info map (procedure -> frame info)") + { + U64 procedure_num = 0; + for(CV_RecIter iter = {0}; cv_rec_next(sym->data, &sym->sym_ranges, 0, &iter);) + { + switch(iter.kind) + { + default:{}break; + + //- rjf: FRAMEPROC + case CV_SymKind_FRAMEPROC: + { + if(procedure_num == 0) { break; } + if(procedure_num > procedure_frameprocs_cap) { break; } + CV_SymFrameproc *frameproc = (CV_SymFrameproc *)iter.struct_base; + procedure_frameprocs[procedure_num-1] = frameproc; + procedure_frameprocs_count = Max(procedure_frameprocs_count, procedure_num); + }break; + + //- rjf: LPROC32/GPROC32 + case CV_SymKind_LPROC32: + case CV_SymKind_GPROC32: + { + procedure_num += 1; + }break; + } + } + U64 scratch_overkill = sizeof(procedure_frameprocs[0])*(procedure_frameprocs_cap-procedure_frameprocs_count); + arena_pop(scratch.arena, scratch_overkill); + } + + ////////////////////////// + //- rjf: symbols pass 2: construct all symbols, given procedure frame info map + // + ProfScope("symbols pass 2: construct all symbols, given procedure frame info map") + { + RDIM_Symbol *defrange_target = 0; + U64 procedure_num = 0; + U64 procedure_base_voff = 0; + CV_ProcFlags proc_flags = 0; + U64 regrel_idx = 0; + RDIM_Symbol *curr_proc_symbol = 0; + typedef struct CV2R_ScopeNode CV2R_ScopeNode; + struct CV2R_ScopeNode + { + CV2R_ScopeNode *next; + RDIM_Scope *scope; + }; + CV2R_ScopeNode *top_scope_node = 0; + CV2R_ScopeNode *free_scope_node = 0; + RDIM_LineTable *inline_site_line_table = sym_idx > 0 ? units_first_inline_site_line_tables[sym_idx-1] : 0; + for(CV_RecIter iter = {0}; cv_rec_next(sym->data, &sym->sym_ranges, 0, &iter);) + { + switch(iter.kind) + { + default:{}break; + + //- rjf: END + case CV_SymKind_END: + { + CV2R_ScopeNode *n = top_scope_node; + if(n != 0) + { + SLLStackPop(top_scope_node); + SLLStackPush(free_scope_node, n); + } + defrange_target = 0; + }break; + + //- rjf: BLOCK32 + case CV_SymKind_BLOCK32: + { + // rjf: unpack sym + CV_SymBlock32 *block32 = (CV_SymBlock32 *)iter.struct_base; + + // rjf: build scope, insert into current parent scope + RDIM_Scope *scope = rdim_scope_chunk_list_push(arena, sym_scopes, sym_scopes_chunk_cap); + { + if(top_scope_node == 0) + { + // TODO(rjf): log + } + if(top_scope_node != 0) + { + RDIM_Scope *top_scope = top_scope_node->scope; + SLLQueuePush_N(top_scope->first_child, top_scope->last_child, scope, next_sibling); + scope->parent_scope = top_scope; + scope->symbol = top_scope->symbol; + } + U64 voff_first = cv2r_voff_from_soff(sections, sections_count, block32->sec, block32->off); + if(voff_first != 0) + { + U64 voff_last = voff_first + block32->len; + RDIM_Rng1U64 voff_range = {voff_first, voff_last}; + rdim_scope_push_voff_range(arena, sym_scopes, scope, voff_range); + } + } + + // rjf: push this scope to scope stack + { + CV2R_ScopeNode *node = free_scope_node; + if(node != 0) { SLLStackPop(free_scope_node); } + else { node = push_array_no_zero(scratch.arena, CV2R_ScopeNode, 1); } + node->scope = scope; + SLLStackPush(top_scope_node, node); + } + }break; + + //- rjf: LDATA32/GDATA32 + case CV_SymKind_LDATA32: + case CV_SymKind_GDATA32: + { + // rjf: unpack sym + CV_SymData32 *data32 = (CV_SymData32 *)iter.struct_base; + String8 name = str8_cstring_capped(data32+1, iter.opl); + U64 voff = cv2r_voff_from_soff(sections, sections_count, data32->sec, data32->off); + + // rjf: determine if this is an exact duplicate global + // + // PDB likes to have duplicates of these spread across different + // symbol streams so we deduplicate across the entire translation + // context. + // + B32 is_duplicate = 0; + { + // TODO(rjf): @important global symbol dedup + } + + // rjf: is not duplicate -> push new global + if(!is_duplicate) + { + // rjf: unpack global variable's type + RDIM_Type *type = p2r_type_ptr_from_itype(data32->itype); + + // rjf: unpack global's container type + B32 got_container = 0; + RDIM_Type *container_type = 0; + U64 container_name_opl = p2r_end_of_cplusplus_container_name(name); + String8 container_name = str8_chop(str8_prefix(name, container_name_opl), 2); + if(!got_container && container_name.size != 0) + { + CV_TypeId cv_type_id = cv2r_first_itype_from_name(tpi_hash, tpi_leaf, container_name, 0); + container_type = p2r_type_ptr_from_itype(cv_type_id); + got_container = (container_type != 0); + } + + // rjf: unpack global's container scope + RDIM_Scope *container_scope = 0; + if(!got_container && top_scope_node != 0 && iter.kind == CV_SymKind_LDATA32) + { + container_scope = top_scope_node->scope; + got_container = (container_scope != 0); + } + + // rjf: unpack global's container namespace + RDIM_Namespace *container_namespace = 0; + if(!got_container && container_name.size != 0) + { + U64 hash = u64_hash_from_str8(container_name); + U64 slot_idx = hash%all_namespace_slots_count; + for(CV2R_NamespaceNode *n = all_namespace_slots[slot_idx]; n != 0; n = n->next) + { + if(str8_match(container_name, n->string, 0) && + n->ns != 0) + { + container_namespace = n->ns; + break; + } + } + } + + // rjf: un-namespaceify the symbol name + String8 name__maybe_partially_qualified = name; + if(got_container) + { + name__maybe_partially_qualified = str8_skip(name, container_name_opl); + } + + // rjf: build symbol + RDIM_Symbol *symbol = rdim_symbol_chunk_list_push(arena, sym_global_variables, sym_global_variables_chunk_cap); + symbol->is_extern = (iter.kind == CV_SymKind_GDATA32); + symbol->name = name__maybe_partially_qualified; + symbol->type = type; + symbol->container_scope = container_scope; + symbol->container_type = container_type; + symbol->container_namespace = container_namespace; + RDIM_Location loc = {.kind = RDI_LocationKind_ModuleOff, .offset = voff}; + RDIM_Rng1U64 range = {0, 0xffffffffffffffffull}; + rdim_location_case_list_push(arena, &symbol->location_cases, loc, range); + } + }break; + + //- rjf: UDT (typedefs) + case CV_SymKind_UDT: + if(sym == all_syms[0] && top_scope_node == 0) + { + if(params->subset_flags & (RDIM_SubsetFlag_Types|RDIM_SubsetFlag_UDTs|RDIM_SubsetFlag_TypeNameMap)) + { + CV_SymUDT *udt = (CV_SymUDT *)iter.struct_base; + String8 name = str8_cstring_capped(udt+1, iter.opl); + RDIM_Type *type = rdim_type_chunk_list_push(arena, typedefs, 4096); + type->kind = RDI_TypeKind_Alias; + type->name = name; + type->direct_type = p2r_type_ptr_from_itype(udt->itype); + if(type->direct_type != 0) + { + type->byte_size = type->direct_type->byte_size; + } + } + }break; + + //- rjf: LPROC32/GPROC32 + case CV_SymKind_LPROC32: + case CV_SymKind_GPROC32: + { + // rjf: unpack sym + CV_SymProc32 *proc32 = (CV_SymProc32 *)iter.struct_base; + String8 name = str8_cstring_capped(proc32+1, iter.opl); + RDIM_Type *type = p2r_type_ptr_from_itype(proc32->itype); + + // rjf: unpack proc's container type + B32 got_container = 0; + RDIM_Type *container_type = 0; + U64 container_name_opl = p2r_end_of_cplusplus_container_name(name); + String8 container_name = str8_chop(str8_prefix(name, container_name_opl), 2); + if(!got_container && container_name.size != 0 && tpi_hash != 0 && tpi_leaf != 0) + { + CV_TypeId cv_type_id = cv2r_first_itype_from_name(tpi_hash, tpi_leaf, container_name, 0); + container_type = p2r_type_ptr_from_itype(cv_type_id); + got_container = (container_type != 0); + } + + // rjf: unpack proc's container scope + RDIM_Scope *container_scope = 0; + if(!got_container && top_scope_node != 0) + { + container_scope = top_scope_node->scope; + got_container = 1; + } + + // rjf: unpack proc's container namespace + RDIM_Namespace *container_namespace = 0; + if(!got_container && container_name.size != 0) + { + U64 hash = u64_hash_from_str8(container_name); + U64 slot_idx = hash%all_namespace_slots_count; + for(CV2R_NamespaceNode *n = all_namespace_slots[slot_idx]; n != 0; n = n->next) + { + if(str8_match(container_name, n->string, 0) && + n->ns != 0) + { + container_namespace = n->ns; + break; + } + } + } + + // rjf: build procedure's root scope + // + // NOTE: even if there could be a containing scope at this point (which should be + // illegal in C/C++ but not necessarily in another language) we would not use + // it here because these scopes refer to the ranges of code that make up a + // procedure *not* the namespaces, so a procedure's root scope always has + // no parent. + RDIM_Scope *procedure_root_scope = 0; + if(params->subset_flags & RDIM_SubsetFlag_Scopes) + { + procedure_root_scope = rdim_scope_chunk_list_push(arena, sym_scopes, sym_scopes_chunk_cap); + U64 voff_first = cv2r_voff_from_soff(sections, sections_count, proc32->sec, proc32->off); + if(voff_first != 0) + { + U64 voff_last = voff_first + proc32->len; + RDIM_Rng1U64 voff_range = {voff_first, voff_last}; + rdim_scope_push_voff_range(arena, sym_scopes, procedure_root_scope, voff_range); + procedure_base_voff = voff_first; + } + } + + // rjf: root scope voff minimum range -> link name + String8 link_name = {0}; + if(procedure_root_scope && procedure_root_scope->voff_ranges.min != 0) + { + U64 voff = procedure_root_scope->voff_ranges.min; + U64 hash = p2r_hash_from_voff(voff); + U64 bucket_idx = hash%link_name_map->buckets_count; + CV2R_LinkNameNode *node = 0; + for(CV2R_LinkNameNode *n = link_name_map->buckets[bucket_idx]; n != 0; n = n->next) + { + if(n->voff == voff) + { + link_name = n->name; + ins_atomic_u64_inc_eval(&n->referencing_symbol_count); + break; + } + } + } + + // rjf: un-namespaceify the symbol name + String8 name__maybe_partially_qualified = name; + if(got_container) + { + name__maybe_partially_qualified = str8_skip(name, container_name_opl); + } + + // rjf: build procedure symbol + if(params->subset_flags & (RDIM_SubsetFlag_Procedures|RDIM_SubsetFlag_ProcedureNameMap)) + { + curr_proc_symbol = rdim_symbol_chunk_list_push(arena, sym_procedures, sym_procedures_chunk_cap); + curr_proc_symbol->is_extern = (iter.kind == CV_SymKind_GPROC32); + curr_proc_symbol->name = name__maybe_partially_qualified; + curr_proc_symbol->link_name = link_name; + curr_proc_symbol->type = type; + curr_proc_symbol->container_scope = container_scope; + curr_proc_symbol->container_type = container_type; + curr_proc_symbol->root_scope = procedure_root_scope; + if(procedure_root_scope != 0) + { + procedure_root_scope->symbol = curr_proc_symbol; + } + } + + // rjf: push scope to scope stack + if(procedure_root_scope) + { + CV2R_ScopeNode *node = free_scope_node; + if(node != 0) { SLLStackPop(free_scope_node); } + else { node = push_array_no_zero(scratch.arena, CV2R_ScopeNode, 1); } + node->scope = procedure_root_scope; + SLLStackPush(top_scope_node, node); + } + + // rjf: determine if this procedure is a namespace used for types; if so, gather + if(procedure_root_scope != 0) + { + U64 hash = u64_hash_from_str8(name); + U64 slot_idx = hash%all_namespace_slots_count; + for(CV2R_NamespaceNode *n = all_namespace_slots[slot_idx]; n != 0; n = n->next) + { + if(str8_match(n->string, name, 0)) + { + ScopeNamespaceNode *node = push_array(arena, ScopeNamespaceNode, 1); + node->scope = procedure_root_scope; + node->string = name; + SLLQueuePush(syms_scopes_that_are_namespaces[sym_idx].first, + syms_scopes_that_are_namespaces[sym_idx].last, + node); + break; + } + } + } + + // rjf: increment procedure counter + procedure_num += 1; + + // reset S_REGREL32 index + regrel_idx = 0; + + proc_flags = proc32->flags; + }break; + + //- rjf: THUNK32 + case CV_SymKind_THUNK32: + { + // rjf: unpack record + CV_SymThunk32 *thunk32 = (CV_SymThunk32 *)iter.struct_base; + String8 name = str8_cstring_capped(thunk32+1, iter.opl); + U64 voff_first = cv2r_voff_from_soff(sections, sections_count, thunk32->sec, thunk32->off); + U64 voff_opl = voff_first + thunk32->len; + + // rjf: build symbol / scope + RDIM_Symbol *symbol = rdim_symbol_chunk_list_push(arena, sym_procedures, sym_procedures_chunk_cap); + symbol->name = name; + symbol->is_thunk = 1; + if(voff_first != voff_opl) + { + RDIM_Scope *scope = rdim_scope_chunk_list_push(arena, sym_scopes, sym_scopes_chunk_cap); + RDIM_Rng1U64 range = {voff_first, voff_opl}; + rdim_scope_push_voff_range(arena, sym_scopes, scope, range); + scope->symbol = symbol; + symbol->root_scope = scope; + } + }break; + + //- rjf: REGREL32 + case CV_SymKind_REGREL32: + { + if(params->subset_flags & RDIM_SubsetFlag_Locals && !(proc_flags & CV_ProcFlag_OptDbgInfo)) + { + // TODO(rjf): apparently some of the information here may end up being + // redundant with "better" information from CV_SymKind_LOCAL record. + // we don't currently handle this, but if those cases arise then it + // will obviously be better to prefer the better information from both + // records. + + // rjf: no containing scope? -> malformed data; locals cannot be produced + // outside of a containing scope + if(top_scope_node == 0) + { + break; + } + + // rjf: unpack sym + CV_SymRegrel32 *regrel32 = (CV_SymRegrel32 *)iter.struct_base; + String8 name = str8_cstring_capped(regrel32+1, iter.opl); + RDIM_Type *type = p2r_type_ptr_from_itype(regrel32->itype); + CV_Reg cv_reg = regrel32->reg; + U32 var_off = regrel32->reg_off; + + // rjf: determine if this is a parameter + B32 is_param = (regrel_idx < curr_proc_symbol->type->count); + + // rjf: determine if we need an extra indirection to the value + B32 extra_indirection_to_value = 0; + if(type != 0) + { + switch(arch) + { + case RDI_Arch_X64: + { + extra_indirection_to_value = (is_param && (type->byte_size > 8 || !IsPow2OrZero(type->byte_size))); + }break; + } + } + + // If the return local does not fit in a register MSVC does not assign it a type. + // So we infer the return type from the signature. + // + // rjf: redirect type, if 0, and if outside frame, to the return type of the + // containing procedure + { + B32 is_stack_reg = 0; + switch(arch) + { + default:{}break; + case RDI_Arch_X64:{is_stack_reg = (cv_reg == CV_Regx64_RSP || cv_reg == CV_Regx64_RBP);}break; + } + if(is_stack_reg) + { + if(procedure_num != 0 && procedure_frameprocs[procedure_num-1] != 0 && procedure_num <= procedure_frameprocs_count) + { + CV_SymFrameproc *frameproc = procedure_frameprocs[procedure_num-1]; + if(var_off > frameproc->frame_size && regrel32->itype == 0 && + top_scope_node->scope->symbol != 0 && + top_scope_node->scope->symbol->type != 0) + { + type = top_scope_node->scope->symbol->type->direct_type; + extra_indirection_to_value = 1; + } + } + } + } + + // rjf: build local + { + RDIM_Scope *scope = top_scope_node->scope; + RDIM_Symbol *local = rdim_symbol_chunk_list_push(arena, &scope->locals, 8); + local->container_scope = scope; + local->is_param = is_param; + local->name = name; + local->type = type; + + // rjf: equip location info + { + // rjf: get raddbg register code + RDI_RegCode reg_code = p2r_rdi_reg_code_from_cv_reg_code(arch, cv_reg); + // TODO(rjf): real byte_size & byte_pos from cv_reg goes here + U32 byte_size = 8; + U32 byte_pos = 0; + + // rjf: build location + RDIM_Location loc = p2r_location_from_addr_reg_off(arena, arch, reg_code, byte_size, byte_pos, (S64)(S32)var_off, extra_indirection_to_value); + RDIM_Rng1U64 voff_range = {0, max_U64}; + rdim_location_case_list_push(arena, &local->location_cases, loc, voff_range); + } + } + } + + regrel_idx += 1; + }break; + + //- rjf: LTHREAD32/GTHREAD32 + case CV_SymKind_LTHREAD32: + case CV_SymKind_GTHREAD32: + if(params->subset_flags & (RDIM_SubsetFlag_ThreadVariables|RDIM_SubsetFlag_ThreadVariableNameMap)) + { + // rjf: unpack sym + CV_SymThread32 *thread32 = (CV_SymThread32 *)iter.struct_base; + String8 name = str8_cstring_capped(thread32+1, iter.opl); + U32 tls_off = thread32->tls_off; + RDIM_Type *type = p2r_type_ptr_from_itype(thread32->itype); + + // rjf: unpack thread variable's container type + B32 got_container = 0; + RDIM_Type *container_type = 0; + U64 container_name_opl = p2r_end_of_cplusplus_container_name(name); + String8 container_name = str8_chop(str8_prefix(name, container_name_opl), 2); + if(!got_container && container_name.size != 0) + { + CV_TypeId cv_type_id = cv2r_first_itype_from_name(tpi_hash, tpi_leaf, container_name, 0); + container_type = p2r_type_ptr_from_itype(cv_type_id); + got_container = (container_type != 0); + } + + // rjf: unpack thread variable's container symbol + RDIM_Scope *container_scope = 0; + if(!got_container && top_scope_node != 0) + { + got_container = 1; + container_scope = top_scope_node->scope; + } + + // rjf: unpack proc's container namespace + RDIM_Namespace *container_namespace = 0; + if(!got_container && container_name.size != 0) + { + U64 hash = u64_hash_from_str8(container_name); + U64 slot_idx = hash%all_namespace_slots_count; + for(CV2R_NamespaceNode *n = all_namespace_slots[slot_idx]; n != 0; n = n->next) + { + if(str8_match(container_name, n->string, 0) && + n->ns != 0) + { + container_namespace = n->ns; + break; + } + } + } + + // rjf: un-namespaceify the symbol name + String8 name__maybe_partially_qualified = name; + if(got_container) + { + name__maybe_partially_qualified = str8_skip(name, container_name_opl); + } + + // rjf: build symbol + RDIM_Symbol *tvar = rdim_symbol_chunk_list_push(arena, sym_thread_variables, sym_thread_variables_chunk_cap); + tvar->name = name__maybe_partially_qualified; + tvar->type = type; + tvar->is_extern = (iter.kind == CV_SymKind_GTHREAD32); + tvar->container_type = container_type; + tvar->container_scope = container_scope; + tvar->container_namespace = container_namespace; + RDIM_Location loc = {.kind = RDI_LocationKind_TLSOff, .offset = tls_off}; + RDIM_Rng1U64 range = {0, 0xffffffffffffffffull}; + rdim_location_case_list_push(arena, &tvar->location_cases, loc, range); + }break; + + //- rjf: LOCAL + case CV_SymKind_LOCAL: + if(params->subset_flags & (RDIM_SubsetFlag_Locals)) + { + // rjf: no containing scope? -> malformed data; locals cannot be produced + // outside of a containing scope + if(top_scope_node == 0) + { + break; + } + + // rjf: unpack sym + CV_SymLocal *slocal = (CV_SymLocal *)iter.struct_base; + String8 name = str8_cstring_capped(slocal+1, iter.opl); + RDIM_Type *type = p2r_type_ptr_from_itype(slocal->itype); + + // rjf: determine if this symbol encodes the beginning of a global modification + B32 is_global_modification = 0; + if((slocal->flags & CV_LocalFlag_Global) || + (slocal->flags & CV_LocalFlag_Static)) + { + is_global_modification = 1; + } + + // rjf: is global modification -> emit global modification symbol + if(is_global_modification) + { + // TODO(rjf): add global modification symbols + defrange_target = 0; + } + + // rjf: is not a global modification -> emit a local variable + if(!is_global_modification) + { + // rjf: build local + RDIM_Scope *scope = top_scope_node->scope; + RDIM_Symbol *local = rdim_symbol_chunk_list_push(arena, &scope->locals, 8); + { + local->container_scope = scope; + local->is_param = !!(slocal->flags & CV_LocalFlag_Param); + local->name = name; + local->type = type; + } + + // rjf: save defrange target, for subsequent defrange symbols + defrange_target = local; + } + }break; + + //- rjf: DEFRANGE_REGISTER + case CV_SymKind_DEFRANGE_REGISTER: + { + // rjf: no defrange target? -> somehow we got to a defrange symbol without first seeing + // a local - break immediately + if(defrange_target == 0) + { + break; + } + + // rjf: unpack sym + CV_SymDefrangeRegister *defrange_register = (CV_SymDefrangeRegister*)iter.struct_base; + CV_Reg cv_reg = defrange_register->reg; + CV_LvarAddrRange *range = &defrange_register->range; + U64 range_voff_first = cv2r_voff_from_soff(sections, sections_count, range->sec, range->off); + U64 range_voff_opl = range_voff_first + range->len; + Rng1U64 voff_range = r1u64(range_voff_first, range_voff_opl); + CV_LvarAddrGap *gaps = (CV_LvarAddrGap*)(defrange_register+1); + U64 gap_count = ((U8*)iter.opl - (U8*)gaps) / sizeof(*gaps); + RDI_RegCode reg_code = p2r_rdi_reg_code_from_cv_reg_code(arch, cv_reg); + + // rjf: build location + RDIM_Location loc = {RDI_LocationKind_ValReg, reg_code}; + + // rjf: emit locations over ranges + cv2r_location_case_list_push_over_lvar_addr_range(arena, &defrange_target->location_cases, loc, voff_range, gaps, gap_count); + }break; + + //- rjf: DEFRANGE_FRAMEPOINTER_REL + case CV_SymKind_DEFRANGE_FRAMEPOINTER_REL: + { + // rjf: no defrange target? -> somehow we got to a defrange symbol without first seeing + // a local - break immediately + if(defrange_target == 0) + { + break; + } + + // rjf: find current procedure's frameproc + CV_SymFrameproc *frameproc = 0; + if(procedure_num != 0 && procedure_num <= procedure_frameprocs_count && procedure_frameprocs[procedure_num-1] != 0) + { + frameproc = procedure_frameprocs[procedure_num-1]; + } + + // rjf: no current valid frameproc? -> somehow we got a to a framepointer-relative defrange + // without having an actually active procedure - break + if(frameproc == 0) + { + break; + } + + // rjf: unpack sym + CV_SymDefrangeFramepointerRel *defrange_fprel = (CV_SymDefrangeFramepointerRel*)iter.struct_base; + CV_LvarAddrRange *range = &defrange_fprel->range; + U64 range_voff_first = cv2r_voff_from_soff(sections, sections_count, range->sec, range->off); + U64 range_voff_opl = range_voff_first + range->len; + Rng1U64 voff_range = r1u64(range_voff_first, range_voff_opl); + CV_LvarAddrGap *gaps = (CV_LvarAddrGap*)(defrange_fprel + 1); + U64 gap_count = ((U8*)iter.opl - (U8*)gaps) / sizeof(*gaps); + + // rjf: select frame pointer register + CV_EncodedFramePtrReg encoded_fp_reg = cv_pick_fp_encoding(frameproc, defrange_target->is_param); + RDI_RegCode fp_register_code = cv2r_reg_code_from_arch_encoded_fp_reg(arch, encoded_fp_reg); + + // rjf: build location + B32 extra_indirection = 0; + U32 byte_size = rdi_addr_size_from_arch(arch); + U32 byte_pos = 0; + S64 var_off = (S64)defrange_fprel->off; + RDIM_Location location = cv2r_location_from_addr_reg_off(arena, arch, fp_register_code, byte_size, byte_pos, var_off, extra_indirection); + + // rjf: emit locations over ranges + cv2r_location_case_list_push_over_lvar_addr_range(arena, &defrange_target->location_cases, location, voff_range, gaps, gap_count); + }break; + + //- rjf: DEFRANGE_SUBFIELD_REGISTER + case CV_SymKind_DEFRANGE_SUBFIELD_REGISTER: + { + // rjf: no defrange target? -> somehow we got to a defrange symbol without first seeing + // a local - break immediately + if(defrange_target == 0) + { + break; + } + + // rjf: unpack sym + CV_SymDefrangeSubfieldRegister *defrange_subfield_register = (CV_SymDefrangeSubfieldRegister*)iter.struct_base; + CV_Reg cv_reg = defrange_subfield_register->reg; + CV_LvarAddrRange *range = &defrange_subfield_register->range; + U64 range_voff_first = cv2r_voff_from_soff(sections, sections_count, range->sec, range->off); + U64 range_voff_opl = range_voff_first + range->len; + Rng1U64 voff_range = r1u64(range_voff_first, range_voff_opl); + CV_LvarAddrGap *gaps = (CV_LvarAddrGap*)(defrange_subfield_register + 1); + U64 gap_count = ((U8*)iter.opl - (U8*)gaps) / sizeof(*gaps); + RDI_RegCode reg_code = p2r_rdi_reg_code_from_cv_reg_code(arch, cv_reg); + + // rjf: skip "subfield" location info - currently not supported + if(defrange_subfield_register->field_offset != 0) + { + break; + } + + // rjf: build location + RDIM_Location loc = {RDI_LocationKind_ValReg, reg_code}; + + // rjf: emit locations over ranges + cv2r_location_case_list_push_over_lvar_addr_range(arena, &defrange_target->location_cases, loc, voff_range, gaps, gap_count); + }break; + + //- rjf: DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE + case CV_SymKind_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: + { + // rjf: no defrange target? -> somehow we got to a defrange symbol without first seeing + // a local - break immediately + if(defrange_target == 0) + { + break; + } + + // rjf: find current procedure's frameproc + CV_SymFrameproc *frameproc = 0; + if(procedure_num != 0 && procedure_num <= procedure_frameprocs_count && procedure_frameprocs[procedure_num-1] != 0) + { + frameproc = procedure_frameprocs[procedure_num-1]; + } + + // rjf: no current valid frameproc? -> somehow we got a to a framepointer-relative defrange + // without having an actually active procedure - break + if(frameproc == 0) + { + break; + } + + // rjf: unpack sym + CV_SymDefrangeFramepointerRelFullScope *defrange_fprel_full_scope = (CV_SymDefrangeFramepointerRelFullScope*)iter.struct_base; + CV_EncodedFramePtrReg encoded_fp_reg = cv_pick_fp_encoding(frameproc, defrange_target->is_param); + RDI_RegCode fp_register_code = p2r_reg_code_from_arch_encoded_fp_reg(arch, encoded_fp_reg); + + // rjf: build location + B32 extra_indirection = 0; + U32 byte_size = rdi_addr_size_from_arch(arch); + U32 byte_pos = 0; + S64 var_off = (S64)defrange_fprel_full_scope->off; + RDIM_Location loc = p2r_location_from_addr_reg_off(arena, arch, fp_register_code, byte_size, byte_pos, var_off, extra_indirection); + + // rjf: emit location over ranges + RDIM_Rng1U64 voff_range = {0, max_U64}; + rdim_location_case_list_push(arena, &defrange_target->location_cases, loc, voff_range); + }break; + + //- rjf: DEFRANGE_REGISTER_REL + case CV_SymKind_DEFRANGE_REGISTER_REL: + { + // rjf: no defrange target? -> somehow we got to a defrange symbol without first seeing + // a local - break immediately + if(defrange_target == 0) + { + break; + } + + // rjf: unpack sym + CV_SymDefrangeRegisterRel *defrange_register_rel = (CV_SymDefrangeRegisterRel*)iter.struct_base; + CV_Reg cv_reg = defrange_register_rel->reg; + RDI_RegCode reg_code = cv2r_rdi_reg_code_from_cv_reg_code(arch, cv_reg); + CV_LvarAddrRange *range = &defrange_register_rel->range; + U64 range_voff_first = cv2r_voff_from_soff(sections, sections_count, range->sec, range->off); + U64 range_voff_opl = range_voff_first + range->len; + Rng1U64 voff_range = r1u64(range_voff_first, range_voff_opl); + CV_LvarAddrGap *gaps = (CV_LvarAddrGap*)(defrange_register_rel + 1); + U64 gap_count = ((U8*)iter.opl - (U8*)gaps) / sizeof(*gaps); + + // rjf: build location + // TODO(rjf): offset & size from cv_reg code + U32 byte_size = rdi_addr_size_from_arch(arch); + U32 byte_pos = 0; + B32 extra_indirection_to_value = 0; + S64 var_off = defrange_register_rel->reg_off; + RDIM_Location loc = cv2r_location_from_addr_reg_off(arena, arch, reg_code, byte_size, byte_pos, var_off, extra_indirection_to_value); + + // rjf: emit locations over ranges + cv2r_location_case_list_push_over_lvar_addr_range(arena, &defrange_target->location_cases, loc, voff_range, gaps, gap_count); + }break; + + //- rjf: FILESTATIC + case CV_SymKind_FILESTATIC: + { + CV_SymFileStatic *file_static = (CV_SymFileStatic*)iter.struct_base; + String8 name = str8_cstring_capped(file_static+1, iter.opl); + RDIM_Type *type = p2r_type_ptr_from_itype(file_static->itype); + // TODO(rjf): emit a global modifier symbol + defrange_target = 0; + }break; + + //- rjf: INLINESITE + case CV_SymKind_INLINESITE: + if(params->subset_flags & (RDIM_SubsetFlag_Scopes)) + { + // rjf: unpack sym + CV_SymInlineSite *sym = (CV_SymInlineSite *)iter.struct_base; + String8 binary_annots = str8((U8 *)(sym+1), (U64)((U8 *)iter.opl - (U8 *)(sym+1))); + + // rjf: extract external info about inline site + String8 name = {0}; + RDIM_Type *type = 0; + RDIM_Type *owner = 0; + if(ipi_leaf != 0 && ipi_leaf->itype_first <= sym->inlinee && sym->inlinee < ipi_leaf->itype_opl) + { + CV_RecRange rec_range = ipi_leaf->leaf_ranges.ranges[sym->inlinee - ipi_leaf->itype_first]; + String8 rec_data = str8_substr(ipi_leaf->data, rng_1u64(rec_range.off, rec_range.off + rec_range.hdr.size)); + void *raw_leaf = rec_data.str + sizeof(U16); + + // rjf: extract method inline info + if(rec_range.hdr.kind == CV_LeafKind_MFUNC_ID && + rec_range.hdr.size >= sizeof(CV_LeafMFuncId)) + { + CV_LeafMFuncId *mfunc_id = (CV_LeafMFuncId*)raw_leaf; + name = str8_cstring_capped(mfunc_id + 1, rec_data.str + rec_data.size); + type = p2r_type_ptr_from_itype(mfunc_id->itype); + owner = mfunc_id->owner_itype != 0 ? p2r_type_ptr_from_itype(mfunc_id->owner_itype) : 0; + } + + // rjf: extract non-method function inline info + else if(rec_range.hdr.kind == CV_LeafKind_FUNC_ID && + rec_range.hdr.size >= sizeof(CV_LeafFuncId)) + { + CV_LeafFuncId *func_id = (CV_LeafFuncId*)raw_leaf; + name = str8_cstring_capped(func_id + 1, rec_data.str + rec_data.size); + type = p2r_type_ptr_from_itype(func_id->itype); + owner = func_id->scope_string_id != 0 ? p2r_type_ptr_from_itype(func_id->scope_string_id) : 0; + } + } + + // rjf: build inline site + RDIM_InlineSite *inline_site = rdim_inline_site_chunk_list_push(arena, sym_inline_sites, sym_inline_sites_chunk_cap); + inline_site->name = name; + inline_site->type = type; + inline_site->owner = owner; + inline_site->line_table = inline_site_line_table; + + // rjf: increment to next inline site line table in this unit + if(inline_site_line_table != 0 && inline_site_line_table->chunk != 0) + { + RDIM_LineTableChunkNode *chunk = inline_site_line_table->chunk; + U64 current_idx = (U64)(inline_site_line_table - chunk->v); + if(current_idx+1 < chunk->count) + { + inline_site_line_table += 1; + } + else + { + chunk = chunk->next; + inline_site_line_table = 0; + if(chunk != 0) + { + inline_site_line_table = chunk->v; + } + } + } + + // rjf: build scope + RDIM_Scope *scope = rdim_scope_chunk_list_push(arena, sym_scopes, sym_scopes_chunk_cap); + scope->inline_site = inline_site; + if(top_scope_node == 0) + { + // TODO(rjf): log + } + if(top_scope_node != 0) + { + RDIM_Scope *top_scope = top_scope_node->scope; + SLLQueuePush_N(top_scope->first_child, top_scope->last_child, scope, next_sibling); + scope->parent_scope = top_scope; + scope->symbol = top_scope->symbol; + } + + // rjf: push this scope to scope stack + { + CV2R_ScopeNode *node = free_scope_node; + if(node != 0) { SLLStackPop(free_scope_node); } + else { node = push_array_no_zero(scratch.arena, CV2R_ScopeNode, 1); } + node->scope = scope; + SLLStackPush(top_scope_node, node); + } + + // rjf: parse offset ranges of this inline site - attach to scope + { + CV_C13InlineSiteDecoder decoder = cv_c13_inline_site_decoder_init(0, 0, procedure_base_voff); + for(;;) + { + CV_C13InlineSiteDecoderStep step = cv_c13_inline_site_decoder_step(&decoder, binary_annots); + + if(step.flags & CV_C13InlineSiteDecoderStepFlag_EmitRange) + { + // rjf: build new range & add to scope + RDIM_Rng1U64 voff_range = { step.range.min, step.range.max }; + rdim_scope_push_voff_range(arena, sym_scopes, scope, voff_range); + } + + if(step.flags & CV_C13InlineSiteDecoderStepFlag_ExtendLastRange) + { + if(scope->voff_ranges.last != 0) + { + scope->voff_ranges.last->v.max = step.range.max; + } + } + + if(step.flags == 0) + { + break; + } + } + } + }break; + + //- rjf: INLINESITE_END + case CV_SymKind_INLINESITE_END: + { + CV2R_ScopeNode *n = top_scope_node; + if(n != 0) + { + SLLStackPop(top_scope_node); + SLLStackPush(free_scope_node, n); + } + defrange_target = 0; + }break; + + //- rjf: CONSTANT + case CV_SymKind_CONSTANT: + if(params->subset_flags & RDIM_SubsetFlag_Constants) + { + // rjf: unpack + CV_SymConstant *sym = (CV_SymConstant *)iter.struct_base; + RDIM_Type *type = p2r_type_ptr_from_itype(sym->itype); + U8 *val_ptr = (U8 *)(sym+1); + CV_NumericParsed val = cv_numeric_from_data_range(val_ptr, iter.opl); + U64 val64 = cv_u64_from_numeric(&val); + U8 *name_ptr = val_ptr + val.encoded_size; + String8 name = str8_cstring_capped(name_ptr, iter.opl); + String8 val_data = str8_struct(&val64); + U64 container_name_opl = 0; + if(type != 0) + { + container_name_opl = p2r_end_of_cplusplus_container_name(type->name); + } + String8 name_qualified = name; + if(container_name_opl != 0) + { + name_qualified = push_str8f(arena, "%S%S", str8_prefix(type->name, container_name_opl), name); + } + + // rjf: build constant symbol + if(name_qualified.size != 0) + { + RDIM_Symbol *cnst = rdim_symbol_chunk_list_push(arena, sym_constants, sym_constants_chunk_cap); + cnst->name = name_qualified; + cnst->type = type; + RDIM_Location loc = {.kind = RDI_LocationKind_ConstantDataOff, .value_data = str8_copy(arena, val_data)}; + RDIM_Rng1U64 range = {0, 0xffffffffffffffffull}; + rdim_location_case_list_push(arena, &cnst->location_cases, loc, range); + } + }break; + } + } + } + + scratch_end(scratch); + } + } +#undef p2r_type_ptr_from_itype + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: produce stub procedures from all pub32 symbols + // + if(lane_idx() == 0) + { + CV_SymParsed *sym = all_syms[0]; + for(CV_RecIter iter = {0}; cv_rec_next(sym->data, &sym->sym_ranges, 0, &iter);) + { + RDIM_Unit *sym_unit = &all_units_ptr->first->v[0]; + RDIM_SymbolChunkList *sym_procedures = &sym_unit->procedures; + RDIM_SymbolChunkList *sym_global_variables = &sym_unit->global_variables; + RDIM_ScopeChunkList *sym_scopes = &sym_unit->scopes; + switch(iter.kind) + { + default:{}break; + case CV_SymKind_PUB32: + { + // rjf: unpack record + CV_SymPub32 *pub32 = (CV_SymPub32 *)iter.struct_base; + String8 name = str8_cstring_capped(pub32+1, iter.opl); + U64 voff = cv2r_voff_from_soff(sections, sections_count, pub32->sec, pub32->off); + + // rjf: determine if this pub32 was covered by another symbol record + B32 was_in_other_record = 0; + { + U64 hash = p2r_hash_from_voff(voff); + U64 slot_idx = hash%link_name_map->buckets_count; + for(CV2R_LinkNameNode *n = link_name_map->buckets[slot_idx]; n != 0; n = n->next) + { + if(n->voff == voff) + { + was_in_other_record = (n->referencing_symbol_count != 0); + break; + } + } + } + + // rjf: find compilation unit contribution to determine pub32's size + U64 voff_opl = voff+1; + if(!was_in_other_record) + { + CV2R_CompUnitContribution *contrib = cv2r_comp_unit_contribution_from_voff__binary_search(params->comp_unit_contributions, params->comp_unit_contributions_count, voff); + if(contrib != 0) + { + voff_opl = contrib->voff_opl; + } + } + + // rjf: if this pub32 was *not* covered by another symbol record, build a symbol for it + if(!was_in_other_record) + { + B32 is_procedure = (pub32->flags & (CV_Pub32Flag_Code|CV_Pub32Flag_Function)); + RDIM_SymbolChunkList *symbols = is_procedure ? sym_procedures : sym_global_variables; + RDIM_Symbol *symbol = rdim_symbol_chunk_list_push(arena, symbols, 64); + symbol->name = name; + symbol->link_name = name; + if(is_procedure) + { + RDIM_Scope *scope = rdim_scope_chunk_list_push(arena, sym_scopes, 64); + scope->symbol = symbol; + RDIM_Rng1U64 range = {voff, voff_opl}; + rdim_scope_push_voff_range(arena, sym_scopes, scope, range); + symbol->root_scope = scope; + } + } + }break; + } + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: upgrade namespace nodes with scopes info, if they match a scope + // + if(lane_idx() == 0) + { + for EachIndex(sym_idx, all_syms_count) + { + ScopeNamespaceList *scopes_that_are_namespaces = &syms_scopes_that_are_namespaces[sym_idx]; + for(ScopeNamespaceNode *scope_n = scopes_that_are_namespaces->first; scope_n != 0; scope_n = scope_n->next) + { + U64 hash = u64_hash_from_str8(scope_n->string); + U64 slot_idx = hash%all_namespace_slots_count; + for(CV2R_NamespaceNode *n = all_namespace_slots[slot_idx]; n != 0; n = n->next) + { + n->scope = scope_n->scope; + } + } + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: upgrade namespaces with container info; trim off container names + // + for EachNode(n, RDIM_NamespaceChunkNode, all_namespaces.first) + { + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) + { + RDIM_Namespace *ns = &n->v[n_idx]; + U64 container_name_opl = p2r_end_of_cplusplus_container_name(ns->name); + String8 container_name = str8_chop(str8_prefix(ns->name, container_name_opl), 2); + if(container_name.size != 0) + { + String8 leaf_name = str8_skip(ns->name, container_name_opl); + U64 hash = u64_hash_from_str8(container_name); + U64 slot_idx = hash%all_namespace_slots_count; + for(CV2R_NamespaceNode *ns_n = all_namespace_slots[slot_idx]; ns_n != 0; ns_n = ns_n->next) + { + if(str8_match(ns_n->string, container_name, 0)) + { + ns->parent_scope = ns_n->scope; + ns->parent_type = ns_n->type; + ns->parent_namespace = ns_n->ns; + break; + } + } + ns->name = leaf_name; + } + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: join extra types from units + // + RDIM_TypeChunkList all_types = {0}; + { + RDIM_TypeChunkList *all_types_ptr = 0; + if(lane_idx() == 0) + { + all_types_ptr = push_array(scratch.arena, RDIM_TypeChunkList, 1); + } + lane_sync_u64(&all_types_ptr, 0); + if(lane_idx() == lane_from_task_idx(7)) ProfScope("join typedefs") + { + for EachIndex(idx, all_syms_count) + { + rdim_type_chunk_list_concat_in_place(all_types__pre_typedefs_ptr, &syms_typedefs[idx]); + } + *all_types_ptr = *all_types__pre_typedefs_ptr; + } + lane_sync(); + all_types = *all_types_ptr; + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: build UDTs + // + RDIM_UDTChunkList *lanes_udts = 0; + ProfScope("build UDTs") + { +#define p2r_type_ptr_from_itype(itype) ((itype_type_ptrs && (itype) < tpi_leaf->itype_opl) ? (itype_type_ptrs[(itype_fwd_map[(itype)] ? itype_fwd_map[(itype)] : (itype))]) : 0) + + //- rjf: gather this lane's UDTs + RDIM_UDTChunkList lane_udts = {0}; + if(params->subset_flags & RDIM_SubsetFlag_Types && + params->subset_flags & RDIM_SubsetFlag_UDTs) + { + U64 udts_chunk_cap = 4096; + RDIM_UDTChunkList *udts = &lane_udts; + Rng1U64 range = lane_range(itype_opl); + for EachInRange(idx, range) + { + //- rjf: skip basics + CV_TypeId itype = (CV_TypeId)idx; + if(itype < itype_first) { continue; } + + //- rjf: grab type for this itype - skip if empty + RDIM_Type *dst_type = itype_type_ptrs[itype]; + if(dst_type == 0) { continue; } + + //- rjf: unpack itype leaf range - skip if out-of-range + CV_RecRange *range = &tpi_leaf->leaf_ranges.ranges[itype-tpi_leaf->itype_first]; + CV_LeafKind kind = range->hdr.kind; + U64 header_struct_size = cv_header_struct_size_from_leaf_kind(kind); + U8 *itype_leaf_first = tpi_leaf->data.str + range->off+2; + U8 *itype_leaf_opl = itype_leaf_first + range->hdr.size-2; + if(range->off+range->hdr.size > tpi_leaf->data.size || + range->off+2+header_struct_size > tpi_leaf->data.size || + range->hdr.size < 2) + { + continue; + } + + //- rjf: build UDT + String8 udt_name = {0}; + CV_TypeId field_itype = 0; + switch(kind) + { + default:{}break; + + //////////////////////// + //- rjf: structs/unions/classes -> equip members + // + case CV_LeafKind_CLASS: + case CV_LeafKind_STRUCTURE: + { + CV_LeafStruct *lf = (CV_LeafStruct *)itype_leaf_first; + if(lf->props & CV_TypeProp_FwdRef) + { + break; + } + field_itype = lf->field_itype; + U8 *numeric_ptr = (U8*)(lf + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U8 *name_ptr = numeric_ptr + size.encoded_size; + udt_name = str8_cstring_capped(name_ptr, itype_leaf_opl); + }goto equip_members; + case CV_LeafKind_UNION: + { + CV_LeafUnion *lf = (CV_LeafUnion *)itype_leaf_first; + if(lf->props & CV_TypeProp_FwdRef) + { + break; + } + field_itype = lf->field_itype; + U8 *numeric_ptr = (U8*)(lf + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U8 *name_ptr = numeric_ptr + size.encoded_size; + udt_name = str8_cstring_capped(name_ptr, itype_leaf_opl); + }goto equip_members; + case CV_LeafKind_CLASS2: + case CV_LeafKind_STRUCT2: + { + CV_LeafStruct2 *lf = (CV_LeafStruct2 *)itype_leaf_first; + if(lf->props & CV_TypeProp_FwdRef) + { + break; + } + U8 *numeric_ptr = (U8*)(lf + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U8 *name_ptr = numeric_ptr + size.encoded_size; + udt_name = str8_cstring_capped(name_ptr, itype_leaf_opl); + field_itype = lf->field_itype; + }goto equip_members; + equip_members: + { + Temp scratch = scratch_begin(&arena, 1); + + //- rjf: grab UDT info + RDIM_UDT *dst_udt = dst_type->udt; + if(dst_udt == 0) + { + dst_udt = dst_type->udt = rdim_udt_chunk_list_push(arena, udts, udts_chunk_cap); + dst_udt->self_type = dst_type; + } + + //- rjf: gather all fields + typedef struct FieldListTask FieldListTask; + struct FieldListTask + { + FieldListTask *next; + CV_TypeId itype; + }; + FieldListTask start_fl_task = {0, field_itype}; + FieldListTask *fl_todo_stack = &start_fl_task; + FieldListTask *fl_done_stack = 0; + for(;fl_todo_stack != 0;) + { + //- rjf: take & unpack task + FieldListTask *fl_task = fl_todo_stack; + SLLStackPop(fl_todo_stack); + SLLStackPush(fl_done_stack, fl_task); + CV_TypeId field_list_itype = fl_task->itype; + + //- rjf: skip bad itypes + if(field_list_itype < tpi_leaf->itype_first || tpi_leaf->itype_opl <= field_list_itype) + { + continue; + } + + //- rjf: field list itype -> range + CV_RecRange *range = &tpi_leaf->leaf_ranges.ranges[field_list_itype-tpi_leaf->itype_first]; + + //- rjf: skip bad headers + if(range->off+range->hdr.size > tpi_leaf->data.size || + range->hdr.size < 2 || + range->hdr.kind != CV_LeafKind_FIELDLIST) + { + continue; + } + + //- rjf: loop over all fields + { + U8 *field_list_first = tpi_leaf->data.str+range->off+2; + U8 *field_list_opl = field_list_first+range->hdr.size-2; + for(U8 *read_ptr = field_list_first, *next_read_ptr = field_list_opl; + read_ptr < field_list_opl; + read_ptr = next_read_ptr) + { + // rjf: unpack field + CV_LeafKind field_kind = *(CV_LeafKind *)read_ptr; + U64 field_leaf_header_size = cv_header_struct_size_from_leaf_kind(field_kind); + U8 *field_leaf_first = read_ptr+2; + U8 *field_leaf_opl = field_list_opl; + next_read_ptr = field_leaf_opl; + + // rjf: skip out-of-bounds fields + if(field_leaf_first+field_leaf_header_size > field_list_opl) + { + continue; + } + + // rjf: process field + switch(field_kind) + { + //- rjf: unhandled/invalid cases + default: + { + // TODO(rjf): log + }break; + + //- rjf: INDEX + case CV_LeafKind_INDEX: + { + // rjf: unpack leaf + CV_LeafIndex *lf = (CV_LeafIndex *)field_leaf_first; + CV_TypeId new_itype = lf->itype; + + // rjf: bump next read pointer past header + next_read_ptr = (U8 *)(lf+1); + + // rjf: determine if index itype is new + B32 is_new = 1; + for(FieldListTask *t = fl_done_stack; t != 0; t = t->next) + { + if(t->itype == new_itype) + { + is_new = 0; + break; + } + } + + // rjf: if new -> push task to follow new itype + if(is_new) + { + FieldListTask *new_task = push_array(scratch.arena, FieldListTask, 1); + SLLStackPush(fl_todo_stack, new_task); + new_task->itype = new_itype; + } + }break; + + //- rjf: MEMBER + case CV_LeafKind_MEMBER: + { + // TODO(rjf): log on bad offset + + // rjf: unpack leaf + CV_LeafMember *lf = (CV_LeafMember *)field_leaf_first; + U8 *offset_ptr = (U8 *)(lf+1); + CV_NumericParsed offset = cv_numeric_from_data_range(offset_ptr, field_leaf_opl); + U64 offset64 = cv_u64_from_numeric(&offset); + U8 *name_ptr = offset_ptr + offset.encoded_size; + String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = name.str+name.size+1; + + // rjf: emit member + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_DataField; + mem->name = name; + mem->type = p2r_type_ptr_from_itype(lf->itype); + mem->off = (U32)offset64; + }break; + + //- rjf: STMEMBER + case CV_LeafKind_STMEMBER: + { + // TODO(rjf): handle attribs + + // rjf: unpack leaf + CV_LeafStMember *lf = (CV_LeafStMember *)field_leaf_first; + U8 *name_ptr = (U8 *)(lf+1); + String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = name.str+name.size+1; + + // rjf: emit member + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_StaticData; + mem->name = name; + mem->type = p2r_type_ptr_from_itype(lf->itype); + }break; + + //- rjf: METHOD + case CV_LeafKind_METHOD: + { + // rjf: unpack leaf + CV_LeafMethod *lf = (CV_LeafMethod *)field_leaf_first; + U8 *name_ptr = (U8 *)(lf+1); + String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = name.str+name.size+1; + + //- rjf: method list itype -> range + CV_RecRange *method_list_range = &tpi_leaf->leaf_ranges.ranges[lf->list_itype-tpi_leaf->itype_first]; + + //- rjf: skip bad method lists + if(method_list_range->off+method_list_range->hdr.size > tpi_leaf->data.size || + method_list_range->hdr.size < 2 || + method_list_range->hdr.kind != CV_LeafKind_METHODLIST) + { + break; + } + + //- rjf: loop through all methods & emit members + U8 *method_list_first = tpi_leaf->data.str + method_list_range->off + 2; + U8 *method_list_opl = method_list_first + method_list_range->hdr.size-2; + for(U8 *method_read_ptr = method_list_first, *next_method_read_ptr = method_list_opl; + method_read_ptr < method_list_opl; + method_read_ptr = next_method_read_ptr) + { + CV_LeafMethodListMember *method = (CV_LeafMethodListMember*)method_read_ptr; + CV_MethodProp prop = CV_FieldAttribs_Extract_MethodProp(method->attribs); + RDIM_Type *method_type = p2r_type_ptr_from_itype(method->itype); + next_method_read_ptr = (U8 *)(method+1); + + // TODO(allen): PROBLEM + // We only get offsets for virtual functions (the "vbaseoff") from + // "Intro" and "PureIntro". In C++ inheritance, when we have a chain + // of inheritance (let's just talk single inheritance for now) the + // first class in the chain that introduces a new virtual function + // has this "Intro" method. If a later class in the chain redefines + // the virtual function it only has a "Virtual" method which does + // not update the offset. There is a "Virtual" and "PureVirtual" + // variant of "Virtual". The "Pure" in either case means there + // is no concrete procedure. When there is no "Pure" the method + // should have a corresponding procedure symbol id. + // + // The issue is we will want to mark all of our virtual methods as + // virtual and give them an offset, but that means we have to do + // some extra figuring to propogate offsets from "Intro" methods + // to "Virtual" methods in inheritance trees. That is - IF we want + // to start preserving the offsets of virtuals. There is room in + // the method struct to make this work, but for now I've just + // decided to drop this information. It is not urgently useful to + // us and greatly complicates matters. + + // rjf: read vbaseoff + U32 vbaseoff = 0; + if(prop == CV_MethodProp_Intro || prop == CV_MethodProp_PureIntro) + { + if(next_method_read_ptr+4 <= method_list_opl) + { + vbaseoff = *(U32 *)next_method_read_ptr; + } + next_method_read_ptr += 4; + } + + // rjf: emit method + switch(prop) + { + default: + { + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_Method; + mem->name = name; + mem->type = method_type; + }break; + case CV_MethodProp_Static: + { + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_StaticMethod; + mem->name = name; + mem->type = method_type; + }break; + case CV_MethodProp_Virtual: + case CV_MethodProp_PureVirtual: + case CV_MethodProp_Intro: + case CV_MethodProp_PureIntro: + { + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_VirtualMethod; + mem->name = name; + mem->type = method_type; + }break; + } + } + + }break; + + //- rjf: ONEMETHOD + case CV_LeafKind_ONEMETHOD: + { + // TODO(rjf): handle attribs + + // rjf: unpack leaf + CV_LeafOneMethod *lf = (CV_LeafOneMethod *)field_leaf_first; + CV_MethodProp prop = CV_FieldAttribs_Extract_MethodProp(lf->attribs); + U8 *vbaseoff_ptr = (U8 *)(lf+1); + U8 *vbaseoff_opl_ptr = vbaseoff_ptr; + U32 vbaseoff = 0; + if(prop == CV_MethodProp_Intro || prop == CV_MethodProp_PureIntro) + { + vbaseoff = *(U32 *)(vbaseoff_ptr); + vbaseoff_opl_ptr += sizeof(U32); + } + U8 *name_ptr = vbaseoff_opl_ptr; + String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); + RDIM_Type *method_type = p2r_type_ptr_from_itype(lf->itype); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = name.str+name.size+1; + + // rjf: emit method + switch(prop) + { + default: + { + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_Method; + mem->name = name; + mem->type = method_type; + }break; + case CV_MethodProp_Static: + { + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_StaticMethod; + mem->name = name; + mem->type = method_type; + }break; + case CV_MethodProp_Virtual: + case CV_MethodProp_PureVirtual: + case CV_MethodProp_Intro: + case CV_MethodProp_PureIntro: + { + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_VirtualMethod; + mem->name = name; + mem->type = method_type; + }break; + } + }break; + + //- rjf: NESTTYPE + case CV_LeafKind_NESTTYPE: + { + // rjf: unpack leaf + CV_LeafNestType *lf = (CV_LeafNestType *)field_leaf_first; + U8 *name_ptr = (U8 *)(lf+1); + String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = name.str+name.size+1; + + // rjf: emit member + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_NestedType; + mem->name = name; + mem->type = p2r_type_ptr_from_itype(lf->itype); + }break; + + //- rjf: NESTTYPEEX + case CV_LeafKind_NESTTYPEEX: + { + // TODO(rjf): handle attribs + + // rjf: unpack leaf + CV_LeafNestTypeEx *lf = (CV_LeafNestTypeEx *)field_leaf_first; + U8 *name_ptr = (U8 *)(lf+1); + String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = name.str+name.size+1; + + // rjf: emit member + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_NestedType; + mem->name = name; + mem->type = p2r_type_ptr_from_itype(lf->itype); + }break; + + //- rjf: BCLASS + case CV_LeafKind_BCLASS: + { + // TODO(rjf): log on bad offset + + // rjf: unpack leaf + CV_LeafBClass *lf = (CV_LeafBClass *)field_leaf_first; + U8 *offset_ptr = (U8 *)(lf+1); + CV_NumericParsed offset = cv_numeric_from_data_range(offset_ptr, field_leaf_opl); + U64 offset64 = cv_u64_from_numeric(&offset); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = offset_ptr+offset.encoded_size; + + // rjf: emit member + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_Base; + mem->type = p2r_type_ptr_from_itype(lf->itype); + mem->off = (U32)offset64; + }break; + + //- rjf: VBCLASS/IVBCLASS + case CV_LeafKind_VBCLASS: + case CV_LeafKind_IVBCLASS: + { + // TODO(rjf): log on bad offsets + // TODO(rjf): handle attribs + // TODO(rjf): offsets? + + // rjf: unpack leaf + CV_LeafVBClass *lf = (CV_LeafVBClass *)field_leaf_first; + U8 *num1_ptr = (U8 *)(lf+1); + CV_NumericParsed num1 = cv_numeric_from_data_range(num1_ptr, field_leaf_opl); + U8 *num2_ptr = num1_ptr + num1.encoded_size; + CV_NumericParsed num2 = cv_numeric_from_data_range(num2_ptr, field_leaf_opl); + + // rjf: bump next read pointer past header + next_read_ptr = (U8 *)(lf+1); + + // rjf: emit member + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_VirtualBase; + mem->type = p2r_type_ptr_from_itype(lf->itype); + }break; + + //- rjf: VFUNCTAB + case CV_LeafKind_VFUNCTAB: + { + CV_LeafVFuncTab *lf = (CV_LeafVFuncTab *)field_leaf_first; + + // rjf: bump next read pointer past header + next_read_ptr = (U8 *)(lf+1); + + // NOTE(rjf): currently no-op this case + (void)lf; + }break; + } + + // rjf: align-up next field + next_read_ptr = (U8 *)AlignPow2((U64)next_read_ptr, 4); + } + } + } + + scratch_end(scratch); + }break; + + //////////////////////// + //- rjf: enums -> equip enumerates + // + case CV_LeafKind_ENUM: + { + CV_LeafEnum *lf = (CV_LeafEnum *)itype_leaf_first; + if(lf->props & CV_TypeProp_FwdRef) + { + break; + } + U8 *name_ptr = (U8 *)(lf + 1); + udt_name = str8_cstring_capped(name_ptr, itype_leaf_opl); + field_itype = lf->field_itype; + }goto equip_enum_vals; + equip_enum_vals:; + { + Temp scratch = scratch_begin(&arena, 1); + + //- rjf: grab UDT info + RDIM_UDT *dst_udt = dst_type->udt; + if(dst_udt == 0) + { + dst_udt = dst_type->udt = rdim_udt_chunk_list_push(arena, udts, udts_chunk_cap); + dst_udt->self_type = dst_type; + } + + //- rjf: gather all fields + typedef struct FieldListTask FieldListTask; + struct FieldListTask + { + FieldListTask *next; + CV_TypeId itype; + }; + FieldListTask start_fl_task = {0, field_itype}; + FieldListTask *fl_todo_stack = &start_fl_task; + FieldListTask *fl_done_stack = 0; + for(;fl_todo_stack != 0;) + { + //- rjf: take & unpack task + FieldListTask *fl_task = fl_todo_stack; + SLLStackPop(fl_todo_stack); + SLLStackPush(fl_done_stack, fl_task); + CV_TypeId field_list_itype = fl_task->itype; + + //- rjf: skip bad itypes + if(field_list_itype < tpi_leaf->itype_first || tpi_leaf->itype_opl <= field_list_itype) + { + continue; + } + + //- rjf: field list itype -> range + CV_RecRange *range = &tpi_leaf->leaf_ranges.ranges[field_list_itype-tpi_leaf->itype_first]; + + //- rjf: skip bad headers + if(range->off+range->hdr.size > tpi_leaf->data.size || + range->hdr.size < 2 || + range->hdr.kind != CV_LeafKind_FIELDLIST) + { + continue; + } + + //- rjf: loop over all fields + { + U8 *field_list_first = tpi_leaf->data.str+range->off+2; + U8 *field_list_opl = field_list_first+range->hdr.size-2; + for(U8 *read_ptr = field_list_first, *next_read_ptr = field_list_opl; + read_ptr < field_list_opl; + read_ptr = next_read_ptr) + { + // rjf: unpack field + CV_LeafKind field_kind = *(CV_LeafKind *)read_ptr; + U64 field_leaf_header_size = cv_header_struct_size_from_leaf_kind(field_kind); + U8 *field_leaf_first = read_ptr+2; + U8 *field_leaf_opl = field_leaf_first+range->hdr.size-2; + next_read_ptr = field_leaf_opl; + + // rjf: skip out-of-bounds fields + if(field_leaf_first+field_leaf_header_size > field_list_opl) + { + continue; + } + + // rjf: process field + switch(field_kind) + { + //- rjf: unhandled/invalid cases + default: + { + // TODO(rjf): log + }break; + + //- rjf: INDEX + case CV_LeafKind_INDEX: + { + // rjf: unpack leaf + CV_LeafIndex *lf = (CV_LeafIndex *)field_leaf_first; + CV_TypeId new_itype = lf->itype; + + // rjf: determine if index itype is new + B32 is_new = 1; + for(FieldListTask *t = fl_done_stack; t != 0; t = t->next) + { + if(t->itype == new_itype) + { + is_new = 0; + break; + } + } + + // rjf: if new -> push task to follow new itype + if(is_new) + { + FieldListTask *new_task = push_array(scratch.arena, FieldListTask, 1); + SLLStackPush(fl_todo_stack, new_task); + new_task->itype = new_itype; + } + }break; + + //- rjf: ENUMERATE + case CV_LeafKind_ENUMERATE: + { + // TODO(rjf): attribs + + // rjf: unpack leaf + CV_LeafEnumerate *lf = (CV_LeafEnumerate *)field_leaf_first; + U8 *val_ptr = (U8 *)(lf+1); + CV_NumericParsed val = cv_numeric_from_data_range(val_ptr, field_leaf_opl); + U64 val64 = cv_u64_from_numeric(&val); + U8 *name_ptr = val_ptr + val.encoded_size; + String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = name.str+name.size+1; + + // rjf: emit member + RDIM_UDTEnumVal *enum_val = rdim_udt_push_enum_val(arena, udts, dst_udt); + enum_val->name = name; + enum_val->val = val64; + }break; + } + + // rjf: align-up next field + next_read_ptr = (U8 *)AlignPow2((U64)next_read_ptr, 4); + } + } + } + + scratch_end(scratch); + }break; + } + + //- rjf: find container, given name's namespaces + if(dst_type != 0 && dst_type->udt != 0 && udt_name.size != 0) + { + // rjf: unpack fully qualified namespace from udt name + U64 container_name_opl = p2r_end_of_cplusplus_container_name(udt_name); + String8 container_name = str8_chop(str8_prefix(udt_name, container_name_opl), 2); + String8 leaf_name = str8_skip(udt_name, container_name_opl); + + // rjf: look up namespace node associated with namespace + CV2R_NamespaceNode *ns_node = 0; + { + U64 hash = u64_hash_from_str8(container_name); + U64 slot_idx = hash%all_namespace_slots_count; + for(CV2R_NamespaceNode *n = all_namespace_slots[slot_idx]; n != 0; n = n->next) + { + if(str8_match(n->string, container_name, 0)) + { + ns_node = n; + break; + } + } + } + + // rjf: equip container info to udt, equip partial name to type + if(ns_node != 0) + { + RDIM_UDT *udt = dst_type->udt; + if(ns_node->scope != 0) + { + udt->container_scope = ns_node->scope; + } + else if(ns_node->ns != 0) + { + udt->container_namespace = ns_node->ns; + } + else if(ns_node->type != 0) + { + udt->container_type = ns_node->type; + } + dst_type->name = leaf_name; + } + } + } + } + + //- rjf: collect all lanes + if(lane_idx() == 0) + { + lanes_udts = push_array(scratch.arena, RDIM_UDTChunkList, lane_count()); + } + lane_sync_u64(&lanes_udts, 0); + lanes_udts[lane_idx()] = lane_udts; +#undef p2r_type_ptr_from_itype + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: join all UDTs + // + RDIM_UDTChunkList all_udts = {0}; + ProfScope("join all UDTs") if(lane_idx() == 0) + { + for EachIndex(idx, lane_count()) + { + rdim_udt_chunk_list_concat_in_place(&all_udts, &lanes_udts[idx]); + } + } + lane_sync(); + RDIM_UDTChunkList *all_udts_ptr = &all_udts; + lane_sync_u64(&all_udts_ptr, 0); + all_udts = *all_udts_ptr; + + ////////////////////////////////////////////////////////////// + //- rjf: bundle all outputs + // + RDIM_BakeParams result = {0}; + { + //- rjf: produce top-level-info + RDIM_TopLevelInfo top_level_info = {0}; + { + top_level_info.arch = arch; + top_level_info.exe_name = str8_skip_last_slash(params->exe_name); + top_level_info.exe_hash = exe_hash; + top_level_info.voff_max = params->exe_voff_max; + if(!params->deterministic) + { + MemoryCopy(&top_level_info.guid, ¶ms->guid, Min(sizeof top_level_info.guid, sizeof params->guid)); + top_level_info.producer_name = str8_lit(BUILD_TITLE_STRING_LITERAL); + } + } + + //- rjf: build binary sections list + RDIM_BinarySectionList binary_sections = {0}; + if(params->subset_flags & RDIM_SubsetFlag_BinarySections) ProfScope("build binary section list") + { + for EachIndex(idx, sections_count) + { + CV2R_Section *src_sec = §ions[idx]; + RDIM_BinarySection *sec = rdim_binary_section_list_push(arena, &binary_sections); + sec->name = src_sec->name; + sec->flags = src_sec->flags; + sec->voff_first = src_sec->voff; + sec->voff_opl = src_sec->voff+src_sec->vsize; + sec->foff_first = src_sec->foff; + sec->foff_opl = src_sec->foff+src_sec->fsize; + } + } + + //- rjf: fill + result.subset_flags = params->subset_flags; + result.top_level_info = top_level_info; + result.binary_sections = binary_sections; + result.units = all_units; + result.namespaces = all_namespaces; + result.types = all_types; + result.udts = all_udts; + result.src_files = all_src_files; + result.line_tables = all_line_tables; + } + + lane_sync(); + scratch_end(scratch); + return result; +} diff --git a/src/rdi_from_codeview/rdi_from_codeview.h b/src/rdi_from_codeview/rdi_from_codeview.h new file mode 100644 index 00000000..724c4e08 --- /dev/null +++ b/src/rdi_from_codeview/rdi_from_codeview.h @@ -0,0 +1,223 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef RDI_FROM_CODEVIEW_H +#define RDI_FROM_CODEVIEW_H + +//////////////////////////////// +//~ rjf: Conversion Parameters + +typedef struct CV2R_CompUnit CV2R_CompUnit; +struct CV2R_CompUnit +{ + String8 obj_name; + String8 group_name; + RDIM_Rng1U64ChunkList ranges; +}; + +typedef struct CV2R_Section CV2R_Section; +struct CV2R_Section +{ + String8 name; + RDI_BinarySectionFlags flags; + U64 voff; + U64 vsize; + U64 foff; + U64 fsize; +}; + +typedef struct CV2R_StringTable CV2R_StringTable; +struct CV2R_StringTable +{ + String8 data; + U32 bucket_count; + U32 strblock_min; + U32 strblock_max; + U32 buckets_min; + U32 buckets_max; +}; + +typedef struct CV2R_TPIHashBlock CV2R_TPIHashBlock; +struct CV2R_TPIHashBlock +{ + CV2R_TPIHashBlock *next; + U32 local_count; + CV_TypeId itypes[13]; // 13 = (64 - 12)/4 +}; + +typedef struct CV2R_TPIHash CV2R_TPIHash; +struct CV2R_TPIHash +{ + String8 data; + String8 aux_data; + CV2R_TPIHashBlock **buckets; + U32 bucket_count; + U32 bucket_mask; +}; + +typedef struct CV2R_CompUnitContribution CV2R_CompUnitContribution; +struct CV2R_CompUnitContribution +{ + U32 mod; + U64 voff_first; + U64 voff_opl; +}; + +typedef struct CV2R_ConvertParams CV2R_ConvertParams; +struct CV2R_ConvertParams +{ + // rjf: info extracted from either linker / pdb + String8 exe_name; + String8 exe_data; + Guid guid; + U64 all_syms_count; + CV_SymParsed **all_syms; + CV_C13Parsed **all_c13s; + U64 comp_units_count; + CV2R_CompUnit *comp_units; + U64 comp_unit_contributions_count; + CV2R_CompUnitContribution *comp_unit_contributions; + U64 sections_count; + CV2R_Section *sections; + U64 exe_voff_max; + CV2R_StringTable *strtbl; + CV_LeafParsed *tpi_leaf; + CV_LeafParsed *ipi_leaf; + CV2R_TPIHash *tpi_hash; + CV2R_TPIHash *ipi_hash; + + // rjf: generation params + RDIM_SubsetFlags subset_flags; + B32 deterministic; +}; + +//////////////////////////////// +//~ rjf: Conversion Helper Types + +//- rjf: link name map (voff -> string) + +typedef struct CV2R_LinkNameNode CV2R_LinkNameNode; +struct CV2R_LinkNameNode +{ + CV2R_LinkNameNode *next; + U64 voff; + String8 name; + U64 referencing_symbol_count; +}; + +typedef struct CV2R_LinkNameMap CV2R_LinkNameMap; +struct CV2R_LinkNameMap +{ + CV2R_LinkNameNode **buckets; + U64 buckets_count; + U64 bucket_collision_count; + U64 link_name_count; +}; + +//- rjf: deduplicated namespace map type + +typedef struct CV2R_NamespaceNode CV2R_NamespaceNode; +struct CV2R_NamespaceNode +{ + CV2R_NamespaceNode *next; + String8 string; + B32 corresponds_to_scope; + RDIM_Scope *scope; + RDIM_Type *type; + RDIM_Namespace *ns; +}; + +//- rjf: normalized file path -> source file map + +typedef struct CV2R_SrcFileStub CV2R_SrcFileStub; +struct CV2R_SrcFileStub +{ + String8 file_path; + CV_C13ChecksumKind checksum_kind; + String8 checksum; +}; + +typedef struct CV2R_SrcFileStubArray CV2R_SrcFileStubArray; +struct CV2R_SrcFileStubArray +{ + CV2R_SrcFileStub *v; + U64 count; +}; + +typedef struct CV2R_SrcFileStubNode CV2R_SrcFileStubNode; +struct CV2R_SrcFileStubNode +{ + CV2R_SrcFileStubNode *next; + CV2R_SrcFileStub v; +}; + +typedef struct CV2R_SrcFileNode CV2R_SrcFileNode; +struct CV2R_SrcFileNode +{ + CV2R_SrcFileNode *next; + RDIM_SrcFile *src_file; +}; + +typedef struct CV2R_SrcFileMap CV2R_SrcFileMap; +struct CV2R_SrcFileMap +{ + CV2R_SrcFileNode **slots; + U64 slots_count; +}; + +//- rjf: itype chains + +typedef struct CV2R_TypeIdChain CV2R_TypeIdChain; +struct CV2R_TypeIdChain +{ + CV2R_TypeIdChain *next; + CV_TypeId itype; +}; + +//////////////////////////////// +//~ rjf: Basic Helpers + +internal U64 cv2r_end_of_cplusplus_container_name(String8 str); +internal U64 cv2r_hash_from_voff(U64 voff); +internal int cv2r_namespace_node_is_before(void *raw_a, void *raw_b); +internal U64 cv2r_voff_from_soff(CV2R_Section *sections, U64 sections_count, U64 sec_idx, U64 soff); + +//////////////////////////////// +//~ rjf: String Table Functions + +internal String8 cv2r_string_from_off(CV2R_StringTable *strtbl, U64 off); + +//////////////////////////////// +//~ rjf: Compilation Unit Contribution Functions + +internal CV2R_CompUnitContribution *cv2r_comp_unit_contribution_from_voff__binary_search(CV2R_CompUnitContribution *contributions, U64 contributions_count, U64 voff); + +//////////////////////////////// +//~ rjf: TPI Hash Table Functions + +internal U32 cv2r_tpi_hash_from_data(String8 string); +internal CV_TypeIdArray cv2r_itypes_from_name(Arena *arena, CV2R_TPIHash *tpi_hash, CV_LeafParsed *leaf, String8 name, B32 compare_unique_name, U32 output_cap); +internal CV_TypeId cv2r_first_itype_from_name(CV2R_TPIHash *tpi_hash, CV_LeafParsed *tpi_leaf, String8 name, B32 compare_unique_name); + +//////////////////////////////// +//~ rjf: CodeView => RDI Canonical Conversions + +internal RDI_Arch cv2r_rdi_arch_from_cv_arch(CV_Arch arch); +internal RDI_RegCode cv2r_rdi_reg_code_from_cv_reg_code(RDI_Arch arch, CV_Reg reg_code); +internal RDI_Language cv2r_rdi_language_from_cv_language(CV_Language language); +internal RDI_TypeKind cv2r_rdi_type_kind_from_cv_basic_type(CV_BasicType basic_type); +internal RDI_ChecksumKind cv2r_rdi_from_cv_c13_checksum_kind(CV_C13ChecksumKind k); + +//////////////////////////////// +//~ rjf: Location Info Building Helpers + +internal RDI_RegCode cv2r_reg_code_from_arch_encoded_fp_reg(RDI_Arch arch, CV_EncodedFramePtrReg encoded_reg); +internal RDIM_Location cv2r_location_from_addr_reg_off(Arena *arena, RDI_Arch arch, RDI_RegCode reg_code, U32 reg_byte_size, U32 reg_byte_pos, S64 offset, B32 extra_indirection); +internal void cv2r_location_case_list_push_over_lvar_addr_range(Arena *arena, RDIM_LocationCaseList *loc_cases, RDIM_Location loc, Rng1U64 voff_range, CV_LvarAddrGap *gaps, U64 gap_count); + +//////////////////////////////// +//~ rjf: Top-Level Conversion Entry Point + +internal RDIM_BakeParams cv2r_convert(Arena *arena, CV2R_ConvertParams *params); + +#endif // RDI_FROM_CODEVIEW_H