Files
raddebugger/src/rdi_from_pdb/rdi_from_pdb.c
T

4511 lines
198 KiB
C

// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
// TODO(rjf): eliminate redundant null checks, just always allocate
// empty results, and have nulls gracefully fall through
//
// (search for != 0 instances, inserted to prevent prior crashes)
////////////////////////////////
//~ rjf: Basic Helpers
internal U64
p2r_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)
{
for(U64 i = str.size; i >= 2; i -= 1)
{
if(str.str[i - 2] == ':' && str.str[i - 1] == ':')
{
result = i;
break;
}
}
}
return(result);
}
internal U64
p2r_hash_from_voff(U64 voff)
{
U64 hash = (voff >> 3) ^ ((7 & voff) << 6);
return hash;
}
////////////////////////////////
//~ rjf: COFF <-> RDI Canonical Conversions
internal RDI_BinarySectionFlags
p2r_rdi_binary_section_flags_from_coff_section_flags(COFF_SectionFlags flags)
{
RDI_BinarySectionFlags result = 0;
if(flags & COFF_SectionFlag_MemRead)
{
result |= RDI_BinarySectionFlag_Read;
}
if(flags & COFF_SectionFlag_MemWrite)
{
result |= RDI_BinarySectionFlag_Write;
}
if(flags & COFF_SectionFlag_MemExecute)
{
result |= RDI_BinarySectionFlag_Execute;
}
return(result);
}
////////////////////////////////
//~ rjf: CodeView <-> RDI Canonical Conversions
internal RDI_Arch
p2r_rdi_arch_from_cv_arch(CV_Arch cv_arch)
{
RDI_Arch result = 0;
switch(cv_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
p2r_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
p2r_rdi_language_from_cv_language(CV_Language cv_language)
{
RDI_Language result = 0;
switch(cv_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
p2r_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
p2r_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
p2r_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
p2r_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_AddrAddrRegPlusU16;
result.reg_code = reg_code;
result.offset = offset;
}
else
{
result.kind = RDI_LocationKind_AddrRegPlusU16;
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
p2r_location_case_list_push_over_lvar_addr_range(Arena *arena, RDIM_LocationCaseList *loc_cases, RDIM_Location loc, CV_LvarAddrRange *range, COFF_SectionHeader *section, CV_LvarAddrGap *gaps, U64 gap_count)
{
//- rjf: extract range info
U64 voff_first = 0;
U64 voff_opl = 0;
if(section != 0)
{
voff_first = section->voff + range->off;
voff_opl = voff_first + range->len;
}
//- rjf: emit location for ranges not coverd by gaps
CV_LvarAddrGap *gap_ptr = gaps;
U64 voff_cursor = voff_first;
for(U64 i = 0; i < gap_count; i += 1, gap_ptr += 1)
{
U64 voff_gap_first = voff_first + gap_ptr->off;
U64 voff_gap_opl = voff_gap_first + gap_ptr->len;
if(voff_cursor < voff_gap_first)
{
RDIM_Rng1U64 voff_range = {voff_cursor, voff_gap_first};
rdim_location_case_list_push(arena, loc_cases, loc, voff_range);
}
voff_cursor = voff_gap_opl;
}
//- rjf: emit remaining range
if(voff_cursor < voff_opl)
{
RDIM_Rng1U64 voff_range = {voff_cursor, voff_opl};
rdim_location_case_list_push(arena, loc_cases, loc, voff_range);
}
}
////////////////////////////////
//~ rjf: Top-Level Conversion Entry Point
internal RDIM_BakeParams
p2r_convert(Arena *arena, P2R_ConvertParams *params)
{
Temp scratch = scratch_begin(&arena, 1);
//////////////////////////////////////////////////////////////
//- rjf: do base MSF parse
//
MSF_Parsed *msf = 0;
{
Temp scratch2 = scratch_begin(&scratch.arena, 1);
// rjf: setup output buckets
MSF_RawStreamTable *msf_raw_stream_table = 0;
U64 *msf_stream_take_counter = 0;
if(lane_idx() == 0)
{
msf_raw_stream_table = msf_raw_stream_table_from_data(scratch2.arena, params->input_pdb_data);
msf = push_array(scratch.arena, MSF_Parsed, 1);
msf->page_size = msf_raw_stream_table->page_size;
msf->page_count = msf_raw_stream_table->total_page_count;
msf->stream_count = msf_raw_stream_table->stream_count;
msf->streams = push_array(scratch.arena, String8, msf->stream_count);
msf_stream_take_counter = push_array(scratch.arena, U64, 1);
}
lane_sync_u64(&msf, 0);
lane_sync_u64(&msf_raw_stream_table, 0);
// rjf: do wide fill
{
lane_sync_u64(&msf_stream_take_counter, 0);
for(;;)
{
U64 stream_idx = ins_atomic_u64_inc_eval(msf_stream_take_counter) - 1;
if(stream_idx >= msf->stream_count)
{
break;
}
msf->streams[stream_idx] = msf_data_from_stream_number(arena, params->input_pdb_data, msf_raw_stream_table, stream_idx);
}
}
lane_sync();
scratch_end(scratch2);
}
lane_sync();
//////////////////////////////////////////////////////////////
//- rjf: do top-level MSF/PDB extraction
//
PDB_Info *pdb_info = 0;
PDB_NamedStreamTable *named_streams = 0;
ProfScope("do top-level MSF/PDB extraction") if(lane_idx() == 0)
{
ProfScope("parse PDB info")
{
String8 info_data = msf_data_from_stream(msf, PDB_FixedStream_Info);
pdb_info = pdb_info_from_data(scratch.arena, info_data);
if(pdb_info->features & PDB_FeatureFlag_MINIMAL_DBG_INFO)
{
log_user_error(str8_lit("PDB was linked with /DEBUG:FASTLINK; partial debug info is not supported. Please relink using /DEBUG:FULL."));
}
}
ProfScope("parse named streams table")
{
named_streams = pdb_named_stream_table_from_info(scratch.arena, pdb_info);
}
}
lane_sync_u64(&pdb_info, 0);
lane_sync_u64(&named_streams, 0);
//////////////////////////////////////////////////////////////
//- rjf: parse PDB strtbl & top-level streams
//
PDB_Strtbl *strtbl = 0;
String8 raw_strtbl = {0};
PDB_DbiParsed *dbi = 0;
PDB_TpiParsed *tpi = 0;
PDB_TpiParsed *ipi = 0;
ProfScope("parse PDB strtbl & top-level streams")
{
if(lane_idx() == lane_from_task_idx(0)) ProfScope("parse PDB strtbl")
{
MSF_StreamNumber strtbl_sn = named_streams->sn[PDB_NamedStream_StringTable];
String8 strtbl_data = msf_data_from_stream(msf, strtbl_sn);
strtbl = pdb_strtbl_from_data(scratch.arena, strtbl_data);
raw_strtbl = str8_substr(strtbl_data, rng_1u64(strtbl->strblock_min, strtbl->strblock_max));
}
if(lane_idx() == lane_from_task_idx(1)) ProfScope("parse DBI")
{
String8 dbi_data = msf_data_from_stream(msf, PDB_FixedStream_Dbi);
dbi = pdb_dbi_from_data(scratch.arena, dbi_data);
}
if(lane_idx() == lane_from_task_idx(2)) ProfScope("parse TPI")
{
String8 tpi_data = msf_data_from_stream(msf, PDB_FixedStream_Tpi);
tpi = pdb_tpi_from_data(scratch.arena, tpi_data);
}
if(lane_idx() == lane_from_task_idx(3)) ProfScope("parse IPI")
{
String8 ipi_data = msf_data_from_stream(msf, PDB_FixedStream_Ipi);
ipi = pdb_tpi_from_data(scratch.arena, ipi_data);
}
}
lane_sync_u64(&strtbl, lane_from_task_idx(0));
lane_sync_u64(&raw_strtbl.size, lane_from_task_idx(0));
lane_sync_u64(&raw_strtbl.str, lane_from_task_idx(0));
lane_sync_u64(&dbi, lane_from_task_idx(1));
lane_sync_u64(&tpi, lane_from_task_idx(2));
lane_sync_u64(&ipi, lane_from_task_idx(3));
//////////////////////////////////////////////////////////////
//- rjf: unpack DBI
//
COFF_SectionHeaderArray coff_sections = {0};
PDB_GsiParsed *gsi = 0;
PDB_GsiParsed *psi_gsi_part = 0;
ProfScope("unpack DBI")
{
if(lane_idx() == lane_from_task_idx(0)) ProfScope("parse COFF sections")
{
MSF_StreamNumber section_stream = dbi->dbg_streams[PDB_DbiStream_SECTION_HEADER];
String8 section_data = msf_data_from_stream(msf, section_stream);
coff_sections = pdb_coff_section_array_from_data(scratch.arena, section_data);
}
if(lane_idx() == lane_from_task_idx(1)) ProfScope("parse GSI")
{
String8 gsi_data = msf_data_from_stream(msf, dbi->gsi_sn);
gsi = pdb_gsi_from_data(scratch.arena, gsi_data);
}
if(lane_idx() == lane_from_task_idx(2)) ProfScope("parse GSI part of PSI")
{
String8 psi_data = msf_data_from_stream(msf, dbi->psi_sn);
String8 psi_data_gsi_part = str8_range(psi_data.str + sizeof(PDB_PsiHeader), psi_data.str + psi_data.size);
psi_gsi_part = pdb_gsi_from_data(scratch.arena, psi_data_gsi_part);
}
}
lane_sync_u64(&coff_sections.v, lane_from_task_idx(0));
lane_sync_u64(&coff_sections.count, lane_from_task_idx(0));
lane_sync_u64(&gsi, lane_from_task_idx(1));
lane_sync_u64(&psi_gsi_part, lane_from_task_idx(2));
//////////////////////////////////////////////////////////////
//- rjf: hash EXE, parse TPI/IPI hash/leaf & global symbol stream & comp units
//
U64 exe_hash = 0;
PDB_TpiHashParsed *tpi_hash = 0;
CV_LeafParsed *tpi_leaf = 0;
PDB_TpiHashParsed *ipi_hash = 0;
CV_LeafParsed *ipi_leaf = 0;
PDB_CompUnitArray *comp_units = 0;
PDB_CompUnitContributionArray *comp_unit_contributions = 0;
ProfScope("hash EXE, parse TPI/IPI hash/leaf & global symbol stream & comp units")
{
if(lane_idx() == lane_from_task_idx(0)) ProfScope("hash EXE")
{
exe_hash = rdi_hash(params->input_exe_data.str, params->input_exe_data.size);
}
if(lane_idx() == lane_from_task_idx(1)) ProfScope("parse TPI hash")
{
String8 hash_data = msf_data_from_stream(msf, tpi->hash_sn);
String8 aux_data = msf_data_from_stream(msf, tpi->hash_sn_aux);
if(!(params->subset_flags & (RDIM_SubsetFlag_Types|RDIM_SubsetFlag_UDTs)))
{
hash_data = aux_data = str8_zero();
}
tpi_hash = pdb_tpi_hash_from_data(scratch.arena, strtbl, tpi, hash_data, aux_data);
}
if(lane_idx() == lane_from_task_idx(2)) ProfScope("parse TPI leaf")
{
String8 leaf_data = pdb_leaf_data_from_tpi(tpi);
tpi_leaf = cv_leaf_from_data(scratch.arena, leaf_data, tpi->itype_first);
}
if(lane_idx() == lane_from_task_idx(3)) ProfScope("parse IPI hash")
{
String8 hash_data = msf_data_from_stream(msf, ipi->hash_sn);
String8 aux_data = msf_data_from_stream(msf, ipi->hash_sn_aux);
if(!(params->subset_flags & (RDIM_SubsetFlag_Types|RDIM_SubsetFlag_UDTs)))
{
hash_data = aux_data = str8_zero();
}
ipi_hash = pdb_tpi_hash_from_data(scratch.arena, strtbl, ipi, hash_data, aux_data);
}
if(lane_idx() == lane_from_task_idx(4)) ProfScope("parse IPI leaf")
{
String8 leaf_data = pdb_leaf_data_from_tpi(ipi);
ipi_leaf = cv_leaf_from_data(scratch.arena, leaf_data, ipi->itype_first);
}
if(lane_idx() == lane_from_task_idx(5)) ProfScope("parse compilation units")
{
String8 comp_units_data = pdb_data_from_dbi_range(dbi, PDB_DbiRange_ModuleInfo);
comp_units = pdb_comp_unit_array_from_data(scratch.arena, comp_units_data);
}
if(lane_idx() == lane_from_task_idx(6)) ProfScope("parse compilation unit contributions")
{
String8 contribs_data = pdb_data_from_dbi_range(dbi, PDB_DbiRange_SecCon);
comp_unit_contributions = push_array(scratch.arena, PDB_CompUnitContributionArray, 1);
comp_unit_contributions[0] = pdb_comp_unit_contribution_array_from_data(scratch.arena, contribs_data, coff_sections);
}
}
lane_sync_u64(&exe_hash, lane_from_task_idx(0));
lane_sync_u64(&tpi_hash, lane_from_task_idx(1));
lane_sync_u64(&tpi_leaf, lane_from_task_idx(2));
lane_sync_u64(&ipi_hash, lane_from_task_idx(3));
lane_sync_u64(&ipi_leaf, lane_from_task_idx(4));
lane_sync_u64(&comp_units, lane_from_task_idx(5));
lane_sync_u64(&comp_unit_contributions, lane_from_task_idx(6));
//////////////////////////////////////////////////////////////
//- rjf: bucket compilation unit contributions
//
RDIM_Rng1U64ChunkList *unit_ranges = 0;
ProfScope("bucket compilation unit contributions") if(lane_idx() == 0)
{
unit_ranges = push_array(scratch.arena, RDIM_Rng1U64ChunkList, comp_units->count + 1);
for(U64 comp_unit_idx = 0; comp_unit_idx < comp_unit_contributions->count; comp_unit_idx += 1)
{
PDB_CompUnitContribution *contribution = &comp_unit_contributions->contributions[comp_unit_idx];
if(contribution->mod < comp_units->count)
{
RDIM_Rng1U64 r = {contribution->voff_first, contribution->voff_opl};
rdim_rng1u64_chunk_list_push(arena, &unit_ranges[contribution->mod + 1], 256, r);
}
}
}
lane_sync_u64(&unit_ranges, 0);
//////////////////////////////////////////////////////////////
//- rjf: parse all syms & c13 line info streams
//
U64 all_syms_count = 0;
CV_SymParsed **all_syms = 0;
CV_C13Parsed **all_c13s = 0;
ProfScope("parse all syms & c13 line info streams")
{
//- rjf: setup outputs
U64 *task_counter = 0;
if(lane_idx() == 0)
{
all_syms_count = comp_units->count+1; // +1 for global symbol stream from DBI
all_syms = push_array(scratch.arena, CV_SymParsed *, all_syms_count);
all_c13s = push_array(scratch.arena, CV_C13Parsed *, all_syms_count);
task_counter = push_array(scratch.arena, U64, 1);
}
lane_sync_u64(&all_syms_count, 0);
lane_sync_u64(&all_syms, 0);
lane_sync_u64(&all_c13s, 0);
//- rjf: wide fill
{
lane_sync_u64(&task_counter, 0);
U64 task_count = all_syms_count;
for(;;)
{
U64 task_idx = ins_atomic_u64_inc_eval(task_counter) - 1;
if(task_idx >= task_count)
{
break;
}
if(task_idx > 0)
{
PDB_CompUnit *unit = comp_units->units[task_idx-1];
String8 unit_sym_data = pdb_data_from_unit_range(msf, unit, PDB_DbiCompUnitRange_Symbols);
String8 unit_c13_data = pdb_data_from_unit_range(msf, unit, PDB_DbiCompUnitRange_C13);
all_syms[task_idx] = cv_sym_from_data(arena, unit_sym_data, 4);
all_c13s[task_idx] = cv_c13_parsed_from_data(arena, unit_c13_data, raw_strtbl, coff_sections);
}
else
{
String8 global_sym_data = msf_data_from_stream(msf, dbi->sym_sn);
all_syms[task_idx] = cv_sym_from_data(arena, global_sym_data, 4);
}
}
}
}
lane_sync();
//////////////////////////////////////////////////////////////
//- rjf: calculate EXE's max voff
//
U64 exe_voff_max = 0;
{
Temp scratch2 = scratch_begin(&scratch.arena, 1);
// rjf: set up
U64 *lane_voff_maxes = 0;
if(lane_idx() == 0)
{
lane_voff_maxes = push_array(scratch2.arena, U64, lane_count());
}
lane_sync_u64(&lane_voff_maxes, 0);
// rjf: compute each lane's max
{
U64 lane_voff_max = 0;
COFF_SectionHeader *sections = coff_sections.v;
Rng1U64 range = lane_range(coff_sections.count);
for EachInRange(idx, range)
{
lane_voff_max = Max(lane_voff_max, sections[idx].voff + sections[idx].vsize);
}
lane_voff_maxes[lane_idx()] = lane_voff_max;
}
lane_sync();
// rjf: find max from all lanes
U64 exe_voff_max = 0;
for EachIndex(l_idx, lane_count())
{
exe_voff_max = Max(exe_voff_max, lane_voff_maxes[l_idx]);
}
lane_sync();
scratch_end(scratch2);
}
//////////////////////////////////////////////////////////////
//- rjf: determine architecture
//
RDI_Arch arch = RDI_Arch_NULL;
U64 arch_addr_size = 0;
{
//
// TODO(rjf): in some cases, the first compilation unit has a zero
// architecture, as it's sometimes used as a "nil" unit. this causes bugs
// in later stages of conversion - particularly, this was detected via
// busted location info. so i've converted this to a scan-until-we-find-an-
// architecture. however, this may still be fundamentally insufficient,
// because Nick has informed me that x86 units can be linked with x64
// units, meaning the appropriate architecture at any point in time is not
// a top-level concept, and is rather dependent on to which compilation
// unit particular symbols belong. so in the future, to support that (odd)
// case, we'll need to not only have this be a top-level "contextual" piece
// of info, but to use the appropriate compilation unit's architecture when
// possible. assuming, of course, that we care about supporting that case.
//
for EachIndex(idx, all_syms_count)
{
arch = p2r_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
//
P2R_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, P2R_LinkNameMap, 1);
link_name_map->buckets_count = Max(1, symbol_count_prediction);
link_name_map->buckets = push_array(scratch.arena, P2R_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);
COFF_SectionHeader *section = (0 < pub32->sec && pub32->sec <= coff_sections.count) ? &coff_sections.v[pub32->sec-1] : 0;
U64 voff = 0;
if(section != 0)
{
voff = section->voff + pub32->off;
}
// rjf: commit to link name map
U64 hash = p2r_hash_from_voff(voff);
U64 bucket_idx = hash%link_name_map->buckets_count;
P2R_LinkNameNode *node = push_array(scratch.arena, P2R_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
//
P2R_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, P2R_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)
{
PDB_CompUnit *comp_unit = comp_units->units[unit_idx-1];
obj_name = comp_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(scratch2.arena, str8_chop_last_slash(obj_name));
//- rjf: find all inline site symbols & gather file stubs
P2R_SrcFileStubNode *first_src_file_stub = 0;
P2R_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;
COFF_SectionHeader *section = (0 < proc32->sec && proc32->sec <= coff_sections.count) ? &coff_sections.v[proc32->sec-1] : 0;
if(section != 0)
{
base_voff = section->voff + 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 = pdb_strtbl_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;
P2R_SrcFileStubNode *stub_n = push_array(scratch2.arena, P2R_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;
P2R_SrcFileStubNode *stub_n = push_array(scratch2.arena, P2R_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, P2R_SrcFileStub, unit_file_stubs[unit_idx].count);
{
U64 idx = 0;
for EachNode(n, P2R_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;
P2R_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, P2R_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, P2R_SrcFileNode *, src_file_map->slots_count);
}
//- rjf: fill table
ProfScope("fill table")
{
for EachIndex(idx, comp_units->count)
{
P2R_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;
P2R_SrcFileNode *src_file_node = 0;
for(P2R_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, P2R_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];
PDB_CompUnit *src_unit = (unit_idx > 0 ? comp_units->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 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;
P2R_SrcFileNode *src_file_node = 0;
if(lines->line_count != 0)
{
for(P2R_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 = p2r_rdi_language_from_cv_language(src_unit_sym->info.language);
dst_unit->line_table = line_table;
dst_unit->voff_ranges = unit_ranges[unit_idx];
}
//- 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;
COFF_SectionHeader *section = (0 < proc32->sec && proc32->sec <= coff_sections.count) ? &coff_sections.v[proc32->sec-1] : 0;
if(section != 0)
{
base_voff = section->voff + 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 = pdb_strtbl_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;
P2R_SrcFileNode *src_file_node = 0;
for(P2R_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 = pdb_tpi_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 = pdb_tpi_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 = pdb_tpi_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 = pdb_tpi_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.
//
P2R_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, P2R_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
{
P2R_TypeIdChain *c = push_array(scratch.arena, P2R_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);
P2R_TypeIdChain start_walk_task = {0, itype};
P2R_TypeIdChain *first_walk_task = &start_walk_task;
P2R_TypeIdChain *last_walk_task = &start_walk_task;
for(P2R_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)
{
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
{
P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1);
c->itype = lf->itype;
SLLStackPush(itype_chains[itype], c);
}
// rjf: push task to walk dependency itype
{
P2R_TypeIdChain *c = push_array(scratch2.arena, P2R_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
{
P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1);
c->itype = lf->itype;
SLLStackPush(itype_chains[itype], c);
}
// rjf: push task to walk dependency itype
{
P2R_TypeIdChain *c = push_array(scratch2.arena, P2R_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
{
P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1);
c->itype = lf->ret_itype;
SLLStackPush(itype_chains[itype], c);
}
// rjf: push task to walk return itype
{
P2R_TypeIdChain *c = push_array(scratch2.arena, P2R_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)
{
P2R_TypeIdChain *c = push_array(scratch.arena, P2R_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)
{
P2R_TypeIdChain *c = push_array(scratch2.arena, P2R_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
{
P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1);
c->itype = lf->ret_itype;
SLLStackPush(itype_chains[itype], c);
}
{
P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1);
c->itype = lf->arg_itype;
SLLStackPush(itype_chains[itype], c);
}
{
P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1);
c->itype = lf->this_itype;
SLLStackPush(itype_chains[itype], c);
}
// rjf: push task to walk dependency itypes
{
P2R_TypeIdChain *c = push_array(scratch2.arena, P2R_TypeIdChain, 1);
c->itype = lf->ret_itype;
SLLQueuePush(first_walk_task, last_walk_task, c);
}
{
P2R_TypeIdChain *c = push_array(scratch2.arena, P2R_TypeIdChain, 1);
c->itype = lf->arg_itype;
SLLQueuePush(first_walk_task, last_walk_task, c);
}
{
P2R_TypeIdChain *c = push_array(scratch2.arena, P2R_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)
{
P2R_TypeIdChain *c = push_array(scratch.arena, P2R_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)
{
P2R_TypeIdChain *c = push_array(scratch2.arena, P2R_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
{
P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1);
c->itype = lf->itype;
SLLStackPush(itype_chains[itype], c);
}
// rjf: push task to walk dependency itype
{
P2R_TypeIdChain *c = push_array(scratch2.arena, P2R_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
{
P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1);
c->itype = lf->entry_itype;
SLLStackPush(itype_chains[itype], c);
}
{
P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1);
c->itype = lf->index_itype;
SLLStackPush(itype_chains[itype], c);
}
// rjf: push task to walk dependency itypes
{
P2R_TypeIdChain *c = push_array(scratch2.arena, P2R_TypeIdChain, 1);
c->itype = lf->entry_itype;
SLLQueuePush(first_walk_task, last_walk_task, c);
}
{
P2R_TypeIdChain *c = push_array(scratch2.arena, P2R_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
{
P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1);
c->itype = lf->base_itype;
SLLStackPush(itype_chains[itype], c);
}
// rjf: push task to walk dependency itypes
{
P2R_TypeIdChain *c = push_array(scratch2.arena, P2R_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
//
typedef struct NamespaceNode NamespaceNode;
struct NamespaceNode
{
NamespaceNode *next;
String8 string;
RDIM_Scope *scope;
};
NamespaceNode **all_type_namespace_slots = 0;
U64 all_type_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();
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: 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)
{
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;
namespace_count += 1;
}
}
}
}
//- 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_type_namespace_slots_count = *total_namespace_count_ptr / 3;
all_type_namespace_slots = push_array(scratch.arena, NamespaceNode *, all_type_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_type_namespace_slots_count;
B32 already_exists = 0;
for(NamespaceNode *dst_n = all_type_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)
{
NamespaceNode *dst_n = push_array(scratch.arena, NamespaceNode, 1);
dst_n->string = n->string;
SLLStackPush(all_type_namespace_slots[dst_slot_idx], dst_n);
}
}
}
}
}
lane_sync_u64(&all_type_namespace_slots_count, 0);
lane_sync_u64(&all_type_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(P2R_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->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: 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: 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];
U64 sym_locations_chunk_cap = 4096;
U64 sym_procedures_chunk_cap = 2048;
U64 sym_global_variables_chunk_cap = 2048;
U64 sym_thread_variables_chunk_cap = 2048;
U64 sym_constants_chunk_cap = 2048;
U64 sym_scopes_chunk_cap = 4096;
U64 sym_inline_sites_chunk_cap = 2048;
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 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 P2R_ScopeNode P2R_ScopeNode;
struct P2R_ScopeNode
{
P2R_ScopeNode *next;
RDIM_Scope *scope;
};
P2R_ScopeNode *top_scope_node = 0;
P2R_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:
{
P2R_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;
}
COFF_SectionHeader *section = (0 < block32->sec && block32->sec <= coff_sections.count) ? &coff_sections.v[block32->sec-1] : 0;
if(section != 0)
{
U64 voff_first = section->voff + block32->off;
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
{
P2R_ScopeNode *node = free_scope_node;
if(node != 0) { SLLStackPop(free_scope_node); }
else { node = push_array_no_zero(scratch.arena, P2R_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);
COFF_SectionHeader *section = (0 < data32->sec && data32->sec <= coff_sections.count) ? &coff_sections.v[data32->sec-1] : 0;
U64 voff = (section ? section->voff : 0) + 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
RDIM_Type *container_type = 0;
U64 container_name_opl = p2r_end_of_cplusplus_container_name(name);
if(container_name_opl > 2)
{
String8 container_name = str8(name.str, container_name_opl - 2);
CV_TypeId cv_type_id = pdb_tpi_first_itype_from_name(tpi_hash, tpi_leaf, container_name, 0);
container_type = p2r_type_ptr_from_itype(cv_type_id);
}
// rjf: unpack global's container scope
RDIM_Scope *container_scope = 0;
if(container_type == 0 && top_scope_node != 0 && iter.kind == CV_SymKind_LDATA32)
{
container_scope = top_scope_node->scope;
}
// 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;
symbol->type = type;
symbol->container_scope = container_scope;
symbol->container_type = container_type;
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
RDIM_Type *container_type = 0;
U64 container_name_opl = p2r_end_of_cplusplus_container_name(name);
if(container_name_opl > 2 && tpi_hash != 0 && tpi_leaf != 0)
{
String8 container_name = str8(name.str, container_name_opl - 2);
CV_TypeId cv_type_id = pdb_tpi_first_itype_from_name(tpi_hash, tpi_leaf, container_name, 0);
container_type = p2r_type_ptr_from_itype(cv_type_id);
}
// rjf: unpack proc's container scope
RDIM_Scope *container_scope = 0;
if(container_type == 0 && top_scope_node != 0)
{
container_scope = top_scope_node->scope;
}
// 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);
COFF_SectionHeader *section = (0 < proc32->sec && proc32->sec <= coff_sections.count) ? &coff_sections.v[proc32->sec-1] : 0;
if(section != 0)
{
U64 voff_first = section->voff + proc32->off;
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;
P2R_LinkNameNode *node = 0;
for(P2R_LinkNameNode *n = link_name_map->buckets[bucket_idx]; n != 0; n = n->next)
{
if(n->voff == voff)
{
link_name = n->name;
break;
}
}
}
// 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;
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)
{
P2R_ScopeNode *node = free_scope_node;
if(node != 0) { SLLStackPop(free_scope_node); }
else { node = push_array_no_zero(scratch.arena, P2R_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_type_namespace_slots_count;
for(NamespaceNode *n = all_type_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: 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->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
RDIM_Type *container_type = 0;
U64 container_name_opl = p2r_end_of_cplusplus_container_name(name);
if(container_name_opl > 2)
{
String8 container_name = str8(name.str, container_name_opl - 2);
CV_TypeId cv_type_id = pdb_tpi_first_itype_from_name(tpi_hash, tpi_leaf, container_name, 0);
container_type = p2r_type_ptr_from_itype(cv_type_id);
}
// rjf: unpack thread variable's container symbol
RDIM_Scope *container_scope = 0;
if(container_type == 0 && top_scope_node != 0)
{
container_scope = top_scope_node->scope;
}
// rjf: build symbol
RDIM_Symbol *tvar = rdim_symbol_chunk_list_push(arena, sym_thread_variables, sym_thread_variables_chunk_cap);
tvar->name = name;
tvar->type = type;
tvar->is_extern = (iter.kind == CV_SymKind_GTHREAD32);
tvar->container_type = container_type;
tvar->container_scope = container_scope;
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->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;
COFF_SectionHeader *range_section = (0 < range->sec && range->sec <= coff_sections.count) ? &coff_sections.v[range->sec-1] : 0;
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
p2r_location_case_list_push_over_lvar_addr_range(arena, &defrange_target->location_cases, loc, range, range_section, 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;
COFF_SectionHeader *range_section = (0 < range->sec && range->sec <= coff_sections.count) ? &coff_sections.v[range->sec-1] : 0;
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 = 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->off;
RDIM_Location location = p2r_location_from_addr_reg_off(arena, arch, fp_register_code, byte_size, byte_pos, var_off, extra_indirection);
// rjf: emit locations over ranges
p2r_location_case_list_push_over_lvar_addr_range(arena, &defrange_target->location_cases, location, range, range_section, 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;
COFF_SectionHeader *range_section = (0 < range->sec && range->sec <= coff_sections.count) ? &coff_sections.v[range->sec-1] : 0;
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
p2r_location_case_list_push_over_lvar_addr_range(arena, &defrange_target->location_cases, loc, range, range_section, 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 = p2r_rdi_reg_code_from_cv_reg_code(arch, cv_reg);
CV_LvarAddrRange *range = &defrange_register_rel->range;
COFF_SectionHeader *range_section = (0 < range->sec && range->sec <= coff_sections.count) ? &coff_sections.v[range->sec-1] : 0;
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 = p2r_location_from_addr_reg_off(arena, arch, reg_code, byte_size, byte_pos, var_off, extra_indirection_to_value);
// rjf: emit locations over ranges
p2r_location_case_list_push_over_lvar_addr_range(arena, &defrange_target->location_cases, loc, range, range_section, 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
{
P2R_ScopeNode *node = free_scope_node;
if(node != 0) { SLLStackPop(free_scope_node); }
else { node = push_array_no_zero(scratch.arena, P2R_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:
{
P2R_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: 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_type_namespace_slots_count;
for(NamespaceNode *n = all_type_namespace_slots[slot_idx]; n != 0; n = n->next)
{
n->scope = scope_n->scope;
}
}
}
}
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_type_namespace_slots_count);
for EachInRange(slot_idx, range)
{
for(NamespaceNode *n = all_type_namespace_slots[slot_idx]; n != 0; n = n->next)
{
if(n->scope != 0) { continue; }
String8 string = n->string;
RDIM_Namespace *ns = rdim_namespace_chunk_list_push(arena, &lane_namespaces, 32);
ns->name = string;
}
}
// 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: 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
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;
}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;
}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;
}
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;
}
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: 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->input_exe_name);
top_level_info.exe_hash = exe_hash;
top_level_info.voff_max = exe_voff_max;
if(!params->deterministic)
{
MemoryCopy(&top_level_info.guid.u8[0], &pdb_info->auth_guid.v[0], Min(sizeof top_level_info.guid.u8, sizeof pdb_info->auth_guid.v));
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")
{
COFF_SectionHeader *coff_ptr = coff_sections.v;
COFF_SectionHeader *coff_opl = coff_ptr + coff_sections.count;
for(;coff_ptr < coff_opl; coff_ptr += 1)
{
char *name_first = (char *)coff_ptr->name;
char *name_opl = name_first + sizeof(coff_ptr->name);
RDIM_BinarySection *sec = rdim_binary_section_list_push(arena, &binary_sections);
sec->name = str8_cstring_capped(name_first, name_opl);
sec->flags = p2r_rdi_binary_section_flags_from_coff_section_flags(coff_ptr->flags);
sec->voff_first = coff_ptr->voff;
sec->voff_opl = coff_ptr->voff+coff_ptr->vsize;
sec->foff_first = coff_ptr->foff;
sec->foff_opl = coff_ptr->foff+coff_ptr->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;
}
scratch_end(scratch);
return result;
}