Files
raddebugger/src/rdi_from_dwarf/rdi_from_dwarf.c
T
2026-06-02 12:11:16 -07:00

4393 lines
187 KiB
C

// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Helpers
internal int
d2r_unique_tag_node_is_less_than(D2R_UniqueTagNode **l, D2R_UniqueTagNode **r)
{
int is_less_than = (l[0]->hash < r[0]->hash);
return is_less_than;
}
////////////////////////////////
//~ rjf: Main Conversion Entry Point (New)
internal RDIM_BakeParams
d2r_convert(Arena *arena, D2R_ConvertParams *params)
{
Temp scratch = scratch_begin(&arena, 1);
DW_Raw *raw = &params->raw;
RDIM_BinarySectionList binary_sections = params->binary_sections;
Arch arch = params->arch;
ARCH_Info *arch_info = arch_info_from_arch(arch);
U64 base_vaddr = params->base_vaddr;
U64 arch_addr_size = byte_size_from_arch(arch);
////////////////////////////
//- rjf: determine acceptable address range
//
// in many cases, linkers seem to trample over addresses in various DWARF sections,
// potentially due to optimizations. we'd like to filter out those busted addresses
// from our final debug info - a good enough heuristic is to disqualify them by
// whether or not they actually fall into the ranges covered by the binary sections.
//
Rng1U64 acceptable_vaddr_range = {0};
{
acceptable_vaddr_range.min = max_U64;
acceptable_vaddr_range.max = 0;
for EachNode(n, RDIM_BinarySectionNode, binary_sections.first)
{
acceptable_vaddr_range.min = Min(base_vaddr + n->v.voff_first, acceptable_vaddr_range.min);
acceptable_vaddr_range.max = Max(base_vaddr + n->v.voff_opl, acceptable_vaddr_range.max);
}
}
////////////////////////////
//- rjf: compute exe hash
//
U64 exe_hash = 0;
ProfScope("compute exe hash")
{
if(lane_idx() == 0)
{
exe_hash = rdi_hash(params->exe_data.str, params->exe_data.size);
}
lane_sync_u64(&exe_hash, 0);
}
////////////////////////////
//- rjf: produce top-level-info
//
RDIM_TopLevelInfo top_level_info = {0};
ProfScope("produce top-level-info")
{
top_level_info.arch = rdi_arch_from_arch(arch);
top_level_info.exe_name = params->exe_name;
top_level_info.exe_hash = exe_hash;
top_level_info.voff_max = acceptable_vaddr_range.max - base_vaddr;
if(!params->deterministic)
{
// TODO(rjf): top_level_info.guid = ...;
top_level_info.producer_name = str8_lit(BUILD_TITLE_STRING_LITERAL);
}
}
////////////////////////////
//- rjf: gather unit ranges from .debug_info, .debug_aranges
//
Rng1U64Array *unit_info_ranges = 0;
Rng1U64Array *unit_arange_ranges = 0;
ProfScope("gather unit ranges from .debug_info, .debug_aranges") if(lane_idx() == 0)
{
unit_info_ranges = push_array(scratch.arena, Rng1U64Array, 1);
*unit_info_ranges = dw2_unit_ranges_from_data(scratch.arena, raw->sec[DW_SectionKind_Info].data);
unit_arange_ranges = push_array(scratch.arena, Rng1U64Array, 1);
*unit_arange_ranges = dw2_unit_ranges_from_data(scratch.arena, raw->sec[DW_SectionKind_ARanges].data);
}
lane_sync_u64(&unit_info_ranges, 0);
lane_sync_u64(&unit_arange_ranges, 0);
U64 unit_count = unit_info_ranges->count;
////////////////////////////
//- rjf: parse all .debug_info unit headers
//
DW2_UnitHeader *unit_headers = 0;
Rng1U64 *unit_info_tag_ranges = 0;
ProfScope("parse all unit headers")
{
// rjf: set up
if(lane_idx() == 0)
{
unit_headers = push_array(scratch.arena, DW2_UnitHeader, unit_count);
unit_info_tag_ranges = push_array(scratch.arena, Rng1U64, unit_count);
}
lane_sync_u64(&unit_headers, 0);
lane_sync_u64(&unit_info_tag_ranges, 0);
// rjf: parse all unit headers
String8 data = raw->sec[DW_SectionKind_Info].data;
Rng1U64 range = lane_range(unit_count);
for EachInRange(idx, range)
{
Rng1U64 unit_info_range = unit_info_ranges->v[idx];
U64 bytes_read = dw2_read_unit_header(str8_substr(data, unit_info_range), 0, &unit_headers[idx]);
unit_info_tag_ranges[idx] = r1u64(unit_info_range.min + bytes_read, unit_info_range.max);
}
}
lane_sync();
Rng1U64Array unit_info_tag_ranges_array = {unit_info_tag_ranges, unit_count};
////////////////////////////
//- rjf: parse all units from .debug_aranges
//
U64 *arange_info_offs = 0;
RDIM_Rng1U64ChunkList *arange_voff_ranges = 0;
{
if(lane_idx() == 0)
{
arange_info_offs = push_array(scratch.arena, U64, unit_arange_ranges->count);
arange_voff_ranges = push_array(scratch.arena, RDIM_Rng1U64ChunkList, unit_arange_ranges->count);
}
lane_sync_u64(&arange_info_offs, 0);
lane_sync_u64(&arange_voff_ranges, 0);
U64 unit_take_idx_ = 0;
U64 *unit_take_idx_ptr = &unit_take_idx_;
lane_sync_u64(&unit_take_idx_ptr, 0);
String8 data = raw->sec[DW_SectionKind_ARanges].data;
for(;;)
{
// rjf: take unit
U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr)-1;
if(unit_idx >= unit_arange_ranges->count)
{
break;
}
// rjf: unpack
Rng1U64 arange_range = unit_arange_ranges->v[unit_idx];
U64 off = arange_range.min;
// rjf: read unit data size / format
U64 unit_size = 0;
DW_Format fmt = 0;
off += dw2_read_initial_length(data, off, &unit_size, &fmt);
U64 unit_opl = off + unit_size;
// rjf: read version
DW_Version version = 0;
U64 version_off = off;
off += str8_deserial_read_struct(data, off, &version);
// rjf: warn on non-version 2
if(version != DW_Version_2)
{
log_infof("[.debug_aranges@0x%I64x] DWARF version for unit #%I64d was expected to be 2, but it was read as %i.\n", version_off, unit_idx, (S32)version);
}
// rjf: read .debug_info off for this unit
U64 info_off = 0;
off += dw2_read_fmt_u64(data, off, fmt, &info_off);
// rjf: read address / segment selector size
U8 addr_size = 0;
U8 segment_selector_size = 0;
off += str8_deserial_read_struct(data, off, &addr_size);
off += str8_deserial_read_struct(data, off, &segment_selector_size);
// rjf: round up past padding
{
U64 tuple_size = addr_size*2 + segment_selector_size;
off += tuple_size - (off%tuple_size);
}
// rjf: parse ranges
RDIM_Rng1U64ChunkList voff_ranges = {0};
if(segment_selector_size != 0)
{
log_infof("[.debug_aranges@0x%I64x] Non-zero (%i) segment selector size parsed; this form of addressing is not currently supported in DWARF info.\n", off, (S32)segment_selector_size);
}
else for(;off < unit_opl;)
{
U64 start_off = off;
U64 base_addr = 0;
U64 range_size = 0;
off += str8_deserial_read(data, off, &base_addr, addr_size, addr_size);
off += str8_deserial_read(data, off, &range_size, addr_size, addr_size);
if(base_addr == 0 && range_size == 0)
{
break;
}
if(base_addr < base_vaddr)
{
log_infof("[.debug_aranges@0x%I64x] Address (0x%I64x) parsed which was less than the image base address (0x%I64x). Skipping.\n", start_off, base_addr, base_vaddr);
}
else
{
U64 voff_first = (base_addr - base_vaddr);
U64 voff_opl = voff_first + range_size;
RDIM_Rng1U64 range = {voff_first, voff_opl};
rdim_rng1u64_chunk_list_push(arena, &voff_ranges, 256, range);
}
if(off == start_off)
{
break;
}
}
// rjf: store
arange_info_offs[unit_idx] = info_off;
arange_voff_ranges[unit_idx] = voff_ranges;
}
lane_sync();
}
////////////////////////////
//- rjf: produce info_off -> list(voff_range) map from aranges units;
// we must do this because technically we can't guarantee that unit_idxs
// inside of .debug_aranges are the same as unit_idxs inside of .debug_info,
// nor can we guarantee that they'd be in the same order, so we need to
// correllate via the encoded .debug_info offset from .debug_aranges.
//
// more excellence.
//
typedef struct D2R_ARangeUnitNode D2R_ARangeUnitNode;
struct D2R_ARangeUnitNode
{
D2R_ARangeUnitNode *next;
U64 info_off;
RDIM_Rng1U64ChunkList *ranges;
};
U64 arange_unit_from_info_off_map_slots_count = unit_arange_ranges->count;
D2R_ARangeUnitNode **arange_unit_from_info_off_map_slots = 0;
{
if(lane_idx() == 0)
{
arange_unit_from_info_off_map_slots = push_array(scratch.arena, D2R_ARangeUnitNode *, arange_unit_from_info_off_map_slots_count);
}
lane_sync_u64(&arange_unit_from_info_off_map_slots, 0);
for EachIndex(arange_unit_idx, unit_arange_ranges->count)
{
U64 info_off = arange_info_offs[arange_unit_idx];
RDIM_Rng1U64ChunkList *ranges = &arange_voff_ranges[arange_unit_idx];
U64 hash = u64_hash_from_str8(str8_struct(&info_off));
U64 slot_idx = hash%arange_unit_from_info_off_map_slots_count;
D2R_ARangeUnitNode *n = push_array(scratch.arena, D2R_ARangeUnitNode, 1);
SLLStackPush(arange_unit_from_info_off_map_slots[slot_idx], n);
n->info_off = info_off;
n->ranges = ranges;
}
}
lane_sync();
////////////////////////////
//- rjf: build all abbreviation maps, build (unit -> abbrev map)
//
DW2_UnitAbbrevMapMap unit_abbrev_map_map = dw2_unit_abbrev_map_map_from_data(scratch.arena, raw->sec[DW_SectionKind_Abbrev].data, unit_headers, unit_count);
////////////////////////////
//- rjf: parse all unit offsets tables
//
// on .debug_str_offsets, as one example:
//
// in an incredible twist of fate, DWARF decided to decouple these from
// compilation units. compilation units *do* contain a
// DW_AttribKind_StrOffsetsBase attribute. but this base offset does *not*
// point to the beginning of a table in .debug_str_offsets! it instead points
// PAST THE INITIAL VARIABLE-WIDTH LENGTH AND FORMAT ENCODING! this means
// you can't actually use the StrOffsetsBase attribute for ANYTHING other
// than correllating a unit to its associated string offset table - but you
// *necessarily needed to have parsed that table beforehand*, completely
// independently from units.
//
// so, we have to parse all the string offset tables up-front, then
// *binary search* their ranges to determine which unit has which table.
//
// of course, in practice, it's perhaps likely/expected that these match
// one-to-one with units, and in the same order, because that is what is
// most natural for generators. but, the format does not *guarantee this*,
// and instead specced something far more arbitrary.
//
// thank you, again, DWARF.
//
DW2_OffsetTableSet *offset_tables = 0;
ProfScope("parse all offset tables (.debug_rnglists, .debug_str_offsets, .debug_addr, .debug_loclists)") if(lane_idx() == 0)
{
offset_tables = push_array(scratch.arena, DW2_OffsetTableSet, 1);
offset_tables[0] = dw2_offset_table_set_from_raw(scratch.arena, raw);
}
lane_sync_u64(&offset_tables, 0);
////////////////////////////
//- rjf: build per-unit parsing contexts, parse compilation unit tags
//
DW2_ParseCtx *unit_parse_ctxs = 0;
DW2_Tag *unit_root_tags = 0;
{
if(lane_idx() == 0)
{
unit_parse_ctxs = push_array(scratch.arena, DW2_ParseCtx, unit_count);
unit_root_tags = push_array(scratch.arena, DW2_Tag, unit_count);
}
lane_sync_u64(&unit_parse_ctxs, 0);
lane_sync_u64(&unit_root_tags, 0);
Rng1U64 range = lane_range(unit_count);
for EachInRange(unit_idx, range)
{
DW2_UnitHeader *hdr = &unit_headers[unit_idx];
DW2_ParseCtx *ctx = &unit_parse_ctxs[unit_idx];
// rjf: equip initial info - just enough to do a bootstrapping parse of the root tag
dw2_parse_ctx_equip_unit_header(ctx, hdr, unit_info_ranges->v[unit_idx].min);
dw2_parse_ctx_equip_unit_abbrev_map(ctx, &unit_abbrev_map_map, unit_idx);
// rjf: do bootstrapping parse
//
// an incredible NOTE: the above context info is actually not sufficient for general
// parsing, because this "bootstrapping" tag parse is what informs us which
// .debug_str_offsets table each unit should be associated with (via the
// StrOffsetsBase attribute), we actually don't have the right string offset table
// *before* we parse this, which means we can't resolve some of the string attribute
// values.
//
// so, in another incredible twist of fate, we actually must do this *twice*. first,
// to find *just the StrOffsetsBase*, and then, to actually fully resolve everything.
//
// (all of the above is also true for RngListsBase)
//
DW2_Tag root_tag_bootstrap = {0};
dw2_read_tag(scratch.arena, raw, ctx, raw->sec[DW_SectionKind_Info].data, unit_info_tag_ranges[unit_idx].min, &root_tag_bootstrap);
// rjf: equip info extracted from bootstrapping parse
dw2_parse_ctx_equip_unit_root_tag(ctx, &root_tag_bootstrap, offset_tables);
// rjf: do non-bootstrapping parse
dw2_read_tag(scratch.arena, raw, ctx, raw->sec[DW_SectionKind_Info].data, unit_info_tag_ranges[unit_idx].min, &unit_root_tags[unit_idx]);
// rjf: equip info extracted from non-bootstrapping parse
dw2_parse_ctx_equip_unit_root_tag(ctx, &unit_root_tags[unit_idx], offset_tables);
}
}
lane_sync();
////////////////////////////
//- rjf: parse each unit's line table header
//
DW2_LineTableHeader *unit_line_table_headers = 0;
ProfScope("parse each unit's line table header")
{
if(lane_idx() == 0)
{
unit_line_table_headers = push_array(scratch.arena, DW2_LineTableHeader, unit_count);
}
lane_sync_u64(&unit_line_table_headers, 0);
U64 unit_take_idx = 0;
U64 *unit_take_idx_ptr = &unit_take_idx;
lane_sync_u64(&unit_take_idx_ptr, 0);
for(;;)
{
U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr) - 1;
if(unit_idx >= unit_count)
{
break;
}
DW2_ParseCtx *ctx = &unit_parse_ctxs[unit_idx];
DW2_Tag *unit_root_tag = &unit_root_tags[unit_idx];
DW2_Attrib *stmt_list = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_StmtList);
U64 line_info_off = stmt_list->val.u128.u64[0];
String8 line_info_data = raw->sec[DW_SectionKind_Line].data;
dw2_read_line_table_header(scratch.arena, raw, ctx, line_info_data, line_info_off, &unit_line_table_headers[unit_idx]);
}
lane_sync();
}
////////////////////////////
//- rjf: deduplicate all source files
//
typedef struct UnitSrcFileMap UnitSrcFileMap;
struct UnitSrcFileMap
{
RDIM_SrcFile **v;
};
RDIM_SrcFileChunkList *all_src_files = 0;
UnitSrcFileMap *unit_src_file_maps = 0;
ProfScope("deduplicate all source files") if(lane_idx() == 0)
{
Temp scratch2 = scratch_begin(&scratch.arena, 1);
//- rjf: count all files in all units
U64 total_file_count = 0;
for EachIndex(unit_idx, unit_count)
{
total_file_count += unit_line_table_headers[unit_idx].files.count;
}
//- rjf: set up path -> src file map
typedef struct SrcFileNode SrcFileNode;
struct SrcFileNode
{
SrcFileNode *next;
String8 full_path;
RDIM_SrcFile *src_file;
};
all_src_files = push_array(scratch.arena, RDIM_SrcFileChunkList, 1);
U64 slots_count = 1 + (total_file_count * 3) / 4;
SrcFileNode **slots = push_array(scratch2.arena, SrcFileNode *, slots_count);
//- rjf: set up unit -> src file maps
unit_src_file_maps = push_array(scratch.arena, UnitSrcFileMap, unit_count);
for EachIndex(unit_idx, unit_count)
{
unit_src_file_maps[unit_idx].v = push_array(scratch.arena, RDIM_SrcFile *, unit_line_table_headers[unit_idx].files.count);
}
//- rjf: build path -> src file map
for EachIndex(unit_idx, unit_count)
{
DW2_LineTableHeader *hdr = &unit_line_table_headers[unit_idx];
for EachIndex(file_idx, hdr->files.count)
{
DW2_LineTableFile *f = &hdr->files.v[file_idx];
DW2_LineTableFile *dir = &hdr->dirs.v[f->dir_idx];
String8 full_file_path = str8f(scratch2.arena, "%S%s%S", dir->file_name, dir->file_name.size != 0 ? "/" : "", f->file_name);
U64 hash = u64_hash_from_str8(full_file_path);
U64 slot_idx = hash%slots_count;
SrcFileNode *node = 0;
for(SrcFileNode *n = slots[slot_idx]; n != 0; n = n->next)
{
if(str8_match(n->full_path, full_file_path, 0))
{
node = n;
break;
}
}
if(!node)
{
node = push_array(scratch2.arena, SrcFileNode, 1);
node->full_path = full_file_path;
node->src_file = rdim_src_file_chunk_list_push(arena, all_src_files, slots_count);
node->src_file->path = str8_copy(arena, full_file_path);
if(f->flags & DW2_LineTableFileFlag_HasMD5)
{
node->src_file->checksum_kind = RDI_ChecksumKind_MD5;
node->src_file->checksum = str8_copy(arena, str8_struct(&f->md5));
}
else if(f->flags & DW2_LineTableFileFlag_HasModifyTime)
{
node->src_file->checksum_kind = RDI_ChecksumKind_Timestamp;
node->src_file->checksum = str8_copy(arena, str8_struct(&f->modify_time));
}
}
unit_src_file_maps[unit_idx].v[file_idx] = node->src_file;
}
}
scratch_end(scratch2);
}
lane_sync_u64(&all_src_files, 0);
lane_sync_u64(&unit_src_file_maps, 0);
////////////////////////////
//- rjf: parse each unit's line info; produce line tables
//
RDIM_LineTableChunkList *unit_line_table_chunk_lists = 0;
RDIM_LineTable **unit_line_tables = 0;
ProfScope("parse each unit's line info; produce line tables")
{
//- rjf: prep outputs
typedef struct LineSeqChunk LineSeqChunk;
struct LineSeqChunk
{
LineSeqChunk *next;
U64 *voffs;
U32 *line_nums;
U16 *col_nums;
U64 line_count;
U64 line_cap;
};
typedef struct FileSeqNode FileSeqNode;
struct FileSeqNode
{
FileSeqNode *next;
RDIM_SrcFile *src_file;
RDIM_SrcFileLineMapFragment *first_line_map_fragment;
RDIM_SrcFileLineMapFragment *last_line_map_fragment;
};
typedef struct FileSeqMap FileSeqMap;
struct FileSeqMap
{
U64 slots_count;
FileSeqNode **slots;
};
FileSeqMap *unit_file_seq_maps = 0;
if(lane_idx() == 0)
{
unit_line_table_chunk_lists = push_array(scratch.arena, RDIM_LineTableChunkList, unit_count);
unit_line_tables = push_array(scratch.arena, RDIM_LineTable *, unit_count);
unit_file_seq_maps = push_array(scratch.arena, FileSeqMap, unit_count);
}
lane_sync_u64(&unit_line_table_chunk_lists, 0);
lane_sync_u64(&unit_line_tables, 0);
lane_sync_u64(&unit_file_seq_maps, 0);
//- rjf: wide per-unit parse
U64 unit_take_idx = 0;
U64 *unit_take_idx_ptr = &unit_take_idx;
lane_sync_u64(&unit_take_idx_ptr, 0);
for(;;)
{
//- rjf: take unit
U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr) - 1;
if(unit_idx >= unit_count)
{
break;
}
Temp scratch2 = scratch_begin(&scratch.arena, 1);
//- rjf: unpack unit info
DW2_LineTableHeader *line_table_header = &unit_line_table_headers[unit_idx];
RDIM_LineTableChunkList *dst_line_tables = &unit_line_table_chunk_lists[unit_idx];
DW2_Tag *unit_root_tag = &unit_root_tags[unit_idx];
DW2_Attrib *stmt_list = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_StmtList);
U64 line_info_off = stmt_list->val.u128.u64[0];
String8 all_line_info_data = raw->sec[DW_SectionKind_Line].data;
String8 unit_line_table_data = str8_substr(all_line_info_data, r1u64(line_table_header->line_program_off, line_info_off + line_table_header->total_unit_data_size));
//- rjf: build unit's line table
RDIM_LineTable *dst_line_table = rdim_line_table_chunk_list_push(arena, dst_line_tables, 1);
unit_line_tables[unit_idx] = dst_line_table;
//- rjf: set up per-unit file sequence map
unit_file_seq_maps[unit_idx].slots_count = line_table_header->files.count + 1;
unit_file_seq_maps[unit_idx].slots = push_array(scratch.arena, FileSeqNode *, unit_file_seq_maps[unit_idx].slots_count);
//- rjf: set up vm registers
DW2_LineVMRegs vm_regs = {0};
{
vm_regs.file_index = 1;
vm_regs.line = 1;
vm_regs.is_stmt = line_table_header->default_is_stmt;
}
//- rjf: run the line opcode program
B32 emit_line = 0;
RDIM_SrcFile *line_seq_src_file = 0;
LineSeqChunk *first_line_seq_chunk = 0;
LineSeqChunk *last_line_seq_chunk = 0;
U64 total_line_seq_count = 0;
for(U64 off = 0, next_off = 0; off <= unit_line_table_data.size; off = next_off)
{
next_off = unit_line_table_data.size;
//- rjf: read next opcode
U64 op_read_off = off;
U8 opcode = 0;
str8_deserial_read_struct(unit_line_table_data, op_read_off, &opcode);
op_read_off += 1;
//- rjf: apply "special opcodes" (DWARF v5 6.2.5.1)
if(opcode >= line_table_header->opcode_base)
{
U32 adjusted_opcode = (U32)(opcode - line_table_header->opcode_base);
U32 op_advance = adjusted_opcode / line_table_header->line_range;
S64 line_advance = (S64)line_table_header->line_base + (S64)adjusted_opcode%(S64)line_table_header->line_range;
U64 addr_advance = line_table_header->min_inst_length * (vm_regs.vliw_op_index + op_advance) / line_table_header->max_ops_per_inst;
vm_regs.address += addr_advance;
vm_regs.vliw_op_index = (vm_regs.vliw_op_index + op_advance) % line_table_header->max_ops_per_inst;
vm_regs.line += line_advance;
emit_line = 1;
}
//- rjf: apply standard opcode
else
{
U64 op_advance = 0;
switch(opcode)
{
//- rjf: skip unknown opcode
default:
{
for EachIndex(uleb_idx, line_table_header->opcode_lengths[opcode - 1])
{
U64 v = 0;
op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &v);
}
}break;
//- rjf: line emissions
case DW_StdOpcode_Copy:
{
emit_line = 1;
}break;
//- rjf: PC advances
case DW_StdOpcode_AdvancePc:
{
op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &op_advance);
}goto advance_pc;
case DW_StdOpcode_ConstAddPc:
{
op_advance = (0xffu - line_table_header->opcode_base) / line_table_header->line_range;
}goto advance_pc;
advance_pc:;
{
U64 op_index = vm_regs.vliw_op_index + op_advance;
vm_regs.address += line_table_header->min_inst_length * (op_index / line_table_header->max_ops_per_inst);
vm_regs.vliw_op_index = op_index % line_table_header->max_ops_per_inst;
}break;
//- rjf: fixed PC advances
case DW_StdOpcode_FixedAdvancePc:
{
U16 fixed_advance = 0;
str8_deserial_read_struct(unit_line_table_data, op_read_off, &fixed_advance);
op_read_off += sizeof(U16);
vm_regs.address += fixed_advance;
vm_regs.vliw_op_index = 0;
}break;
//- rjf: line number advance
case DW_StdOpcode_AdvanceLine:
{
S64 advance = 0;
op_read_off += str8_deserial_read_sleb128(unit_line_table_data, op_read_off, &advance);
vm_regs.line += advance;
}break;
//- rjf: set file
case DW_StdOpcode_SetFile:
{
op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &vm_regs.file_index);
}break;
//- rjf: set column
case DW_StdOpcode_SetColumn:
{
op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &vm_regs.column);
}break;
//- rjf: negate statment
case DW_StdOpcode_NegateStmt:
{
vm_regs.is_stmt = !vm_regs.is_stmt;
}break;
//- rjf: flag sets
case DW_StdOpcode_SetBasicBlock:
{
vm_regs.basic_block = 1;
}break;
case DW_StdOpcode_SetPrologueEnd:
{
vm_regs.prologue_end = 1;
}break;
case DW_StdOpcode_SetEpilogueBegin:
{
vm_regs.epilogue_begin = 1;
}break;
//- rjf: isa
case DW_StdOpcode_SetIsa:
{
op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &vm_regs.isa);
}break;
//- rjf: extended opcode
case DW_StdOpcode_ExtendedOpcode:
{
// rjf: read extended opcode size
U64 ext_opcode_size = 0;
op_read_off += str8_deserial_read_uleb128(unit_line_table_data, op_read_off, &ext_opcode_size);
// rjf: read extended opcode
U8 ext_opcode = 0;
op_read_off += str8_deserial_read_struct(unit_line_table_data, op_read_off, &ext_opcode);
// rjf: grab truncated data for just this extended opcode
String8 ext_op_data = str8_prefix(unit_line_table_data, op_read_off + ext_opcode_size);
// rjf: do extended opcode
switch(ext_opcode)
{
default:
case DW_ExtOpcode_Undefined:
case DW_ExtOpcode_UserLo:
case DW_ExtOpcode_UserHi:
{}break;
case DW_ExtOpcode_EndSequence:
{
emit_line = 1;
vm_regs.end_sequence = 1;
}break;
case DW_ExtOpcode_SetAddress:
{
U64 addr = 0;
op_read_off += str8_deserial_read(ext_op_data, op_read_off, &addr, line_table_header->addr_size, line_table_header->addr_size);
vm_regs.address = addr;
vm_regs.vliw_op_index = 0;
}break;
case DW_ExtOpcode_DefineFile:
{
String8 file_name = {0};
U64 dir_idx = 0;
U64 modify_time = 0;
U64 file_size = 0;
op_read_off += str8_deserial_read_cstr(ext_op_data, op_read_off, &file_name);
op_read_off += str8_deserial_read_uleb128(ext_op_data, op_read_off, &dir_idx);
op_read_off += str8_deserial_read_uleb128(ext_op_data, op_read_off, &modify_time);
op_read_off += str8_deserial_read_uleb128(ext_op_data, op_read_off, &file_size);
//
// TODO(rjf): this is a real problem, because at this point, we've already gathered & deduped
// all source files, but now we're in a situation where a per-lane line info parse can produce
// new source files, which may - of course - be duplicates of files defined in other unit line
// info.
//
}break;
case DW_ExtOpcode_SetDiscriminator:
{
op_read_off += str8_deserial_read_uleb128(ext_op_data, op_read_off, &vm_regs.discriminator);
}break;
}
}break;
}
}
//- rjf: advance to next op
if(op_read_off > off)
{
next_off = op_read_off;
}
//- rjf: map file index -> rdim src file
RDIM_SrcFile *src_file = 0;
if(vm_regs.file_index < line_table_header->files.count)
{
src_file = unit_src_file_maps[unit_idx].v[vm_regs.file_index];
}
//- rjf: sequence ended explicitly, or file change, or end of stream? -> push to line table
if(line_seq_src_file != 0 && (vm_regs.end_sequence || (src_file != line_seq_src_file && first_line_seq_chunk != 0) || off >= unit_line_table_data.size))
{
// rjf: combine voffs/lines/cols
U64 seq_line_count = total_line_seq_count;
U64 *seq_voffs = push_array(arena, U64, seq_line_count+1);
U32 *seq_lines = push_array(arena, U32, seq_line_count);
U16 *seq_cols = push_array(arena, U16, 2*seq_line_count);
{
U64 voff_idx = 0;
U64 line_idx = 0;
U64 col_idx = 0;
for(LineSeqChunk *c = first_line_seq_chunk; c != 0; c = c->next)
{
MemoryCopy(seq_voffs + voff_idx, c->voffs, sizeof(c->voffs[0]) * c->line_count);
MemoryCopy(seq_lines + line_idx, c->line_nums, sizeof(c->line_nums[0]) * c->line_count);
MemoryCopy(seq_cols + col_idx, c->col_nums, sizeof(c->col_nums[0]) * 2 * c->line_count);
voff_idx += c->line_count;
line_idx += c->line_count;
col_idx += 2*c->line_count;
}
seq_voffs[seq_line_count] = vm_regs.address - base_vaddr;
}
// rjf: push sequence to line table
RDIM_LineSequence *seq = rdim_line_table_push_sequence(arena, dst_line_tables, dst_line_table, line_seq_src_file, seq_voffs, seq_lines, seq_cols, seq_line_count);
// rjf: map src file -> file seq node
FileSeqNode *file_seq_n = 0;
{
U64 hash = u64_hash_from_str8(str8_struct(&line_seq_src_file));
U64 slot_idx = hash%unit_file_seq_maps[unit_idx].slots_count;
for(FileSeqNode *n = unit_file_seq_maps[unit_idx].slots[slot_idx]; n != 0; n = n->next)
{
if(n->src_file == line_seq_src_file)
{
file_seq_n = n;
break;
}
}
if(file_seq_n == 0)
{
file_seq_n = push_array(scratch.arena, FileSeqNode, 1);
SLLStackPush(unit_file_seq_maps[unit_idx].slots[slot_idx], file_seq_n);
file_seq_n->src_file = line_seq_src_file;
}
}
// rjf: record sequence in file seq node
{
RDIM_SrcFileLineMapFragment *f = push_array(scratch.arena, RDIM_SrcFileLineMapFragment, 1);
SLLQueuePush(file_seq_n->first_line_map_fragment, file_seq_n->last_line_map_fragment, f);
f->seq = seq;
}
// rjf: reset
first_line_seq_chunk = last_line_seq_chunk = 0;
total_line_seq_count = 0;
line_seq_src_file = 0;
if(vm_regs.end_sequence)
{
MemoryZeroStruct(&vm_regs);
vm_regs.file_index = 1;
vm_regs.line = 1;
vm_regs.is_stmt = line_table_header->default_is_stmt;
}
}
//- rjf: emit lines
if(emit_line && vm_regs.address != 0 && vm_regs.line != 0)
{
emit_line = 0;
// rjf: grab the last emitted line info
U32 last_line = 0;
U16 last_col = 0;
if(last_line_seq_chunk != 0 && last_line_seq_chunk->line_count > 0)
{
last_line = last_line_seq_chunk->line_nums[last_line_seq_chunk->line_count-1];
last_col = last_line_seq_chunk->col_nums[last_line_seq_chunk->line_count-1];
}
// rjf: determine if we need a new line
//
// TODO(rjf): in some cases this can trigger w/ DWARF due to columnar changes;
// we need to adjust this when we want to start correctly supporting columnar
// line info / stepping
//
B32 need_new_line = (last_line != (U32)vm_regs.line);
// rjf: need new line -> grab chunk
LineSeqChunk *chunk = last_line_seq_chunk;
if(need_new_line && (chunk == 0 || chunk->line_count >= chunk->line_cap))
{
chunk = push_array(scratch.arena, LineSeqChunk, 1);
SLLQueuePush(first_line_seq_chunk, last_line_seq_chunk, chunk);
chunk->line_cap = 64;
chunk->voffs = push_array(scratch.arena, U64, chunk->line_cap + 1);
chunk->line_nums = push_array(scratch.arena, U32, chunk->line_cap);
chunk->col_nums = push_array(scratch.arena, U16, 2*chunk->line_cap);
}
// rjf: need new line -> push
if(need_new_line)
{
U64 chunk_line_idx = chunk->line_count;
chunk->voffs[chunk_line_idx] = vm_regs.address - base_vaddr;
chunk->line_nums[chunk_line_idx] = (U32)vm_regs.line;
chunk->col_nums[chunk_line_idx] = (U16)vm_regs.column;
chunk->line_count += 1;
total_line_seq_count += 1;
line_seq_src_file = src_file;
// NOTE(rjf): use for comparing against llvm-dwarfdump --debug-line
#if 0
printf("0x%016I64x %6i %6i %6i %3i %13I64x %7i %s%s%s%s\n",
vm_regs.address,
(int)vm_regs.line,
(int)vm_regs.column,
(int)vm_regs.file_index,
(int)vm_regs.isa,
vm_regs.discriminator,
(int)vm_regs.vliw_op_index,
vm_regs.is_stmt ? " is_stmt" : "",
vm_regs.prologue_end ? " prologue_end" : "",
vm_regs.epilogue_begin ? " epilogue_begin" : "",
vm_regs.end_sequence ? " end_sequence" : "");
#endif
}
vm_regs.discriminator = 0;
vm_regs.basic_block = 0;
vm_regs.prologue_end = 0;
vm_regs.epilogue_begin = 0;
}
}
scratch_end(scratch2);
}
lane_sync();
//- rjf: equip source files with their fragments
//
// TODO(rjf): this can *almost* be wide, but we are relying on a few top-level
// summations inside the RDIM_SrcFileChunkList when we push a sequence to a src
// file. if we just summed those later when baking (probably fine), then this
// could go wide across all src files, which would be very nice. just lane-0ing
// for now. when possible, we can probably do the same thing in PDB too.
//
if(lane_idx() == 0)
{
for EachIndex(unit_idx, unit_count)
{
FileSeqMap *file_seq_map = &unit_file_seq_maps[unit_idx];
for EachIndex(slot_idx, file_seq_map->slots_count)
{
for(FileSeqNode *n = file_seq_map->slots[slot_idx]; n != 0; n = n->next)
{
for(RDIM_SrcFileLineMapFragment *f = n->first_line_map_fragment; f != 0; f = f->next)
{
rdim_src_file_push_line_sequence(arena, all_src_files, n->src_file, f->seq);
}
}
}
}
}
lane_sync();
}
////////////////////////////
//- rjf: join all line tables
//
RDIM_LineTableChunkList *all_line_tables = 0;
if(lane_idx() == 0)
{
all_line_tables = push_array(scratch.arena, RDIM_LineTableChunkList, 1);
for EachIndex(unit_idx, unit_count)
{
rdim_line_table_chunk_list_concat_in_place(all_line_tables, &unit_line_table_chunk_lists[unit_idx]);
}
}
lane_sync_u64(&all_line_tables, 0);
////////////////////////////
//- rjf: find all offsets of top-level tag trees across all units
//
U64Array *unit_info_root_tag_offs = 0;
U64 total_root_tag_count = 0;
U64 *total_root_tag_count_ptr = &total_root_tag_count;
lane_sync_u64(&total_root_tag_count_ptr, 0);
ProfScope("find all offsets of top-level tag trees across all units")
{
if(lane_idx() == 0)
{
unit_info_root_tag_offs = push_array(scratch.arena, U64Array, unit_count);
}
lane_sync_u64(&unit_info_root_tag_offs, 0);
typedef struct D2R_UnitTagTreeOffChunkNode D2R_UnitTagTreeOffChunkNode;
struct D2R_UnitTagTreeOffChunkNode
{
D2R_UnitTagTreeOffChunkNode *next;
U64 *v;
U64 count;
U64 cap;
};
typedef struct D2R_UnitTagTreeOffChunkList D2R_UnitTagTreeOffChunkList;
struct D2R_UnitTagTreeOffChunkList
{
D2R_UnitTagTreeOffChunkNode *first;
D2R_UnitTagTreeOffChunkNode *last;
U64 chunk_count;
U64 total_count;
};
U64 unit_take_idx_ = 0;
U64 *unit_take_idx_ptr = &unit_take_idx_;
lane_sync_u64(&unit_take_idx_ptr, 0);
for(;;)
{
//- rjf: take the next unit
U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr)-1;
if(unit_idx >= unit_count)
{
break;
}
Temp scratch2 = scratch_begin(&scratch.arena, 1);
//- rjf: unpack this unit
Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx];
DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx];
//- rjf: gather all offsets of root-level trees
D2R_UnitTagTreeOffChunkList tree_offs = {0};
S64 depth = 0;
for(U64 off = unit_info_tag_range.min; contains_1u64(unit_info_tag_range, off);)
{
U64 start_off = off;
Temp scratch3 = scratch_begin(&scratch2.arena, 1);
//- rjf: if this next tag is root-level, gather
if(depth == 1)
{
D2R_UnitTagTreeOffChunkNode *chunk = tree_offs.last;
if(chunk == 0 || chunk->count >= chunk->cap)
{
chunk = push_array(scratch2.arena, D2R_UnitTagTreeOffChunkNode, 1);
chunk->cap = 512;
chunk->v = push_array(scratch2.arena, U64, chunk->cap);
SLLQueuePush(tree_offs.first, tree_offs.last, chunk);
tree_offs.chunk_count += 1;
}
chunk->v[chunk->count] = off;
chunk->count += 1;
tree_offs.total_count += 1;
}
//- rjf: reading the next tag
DW2_Tag tag = {0};
off += dw2_read_tag(scratch3.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag);
//- rjf: do tree navigations
if(tag.has_children)
{
depth += 1;
}
if(tag.kind == DW_TagKind_Null)
{
depth -= 1;
}
scratch_end(scratch3);
if(off == start_off)
{
break;
}
}
//- rjf: flatten & store
{
U64Array tree_offs_array = {0};
tree_offs_array.count = tree_offs.total_count;
tree_offs_array.v = push_array(scratch.arena, U64, tree_offs_array.count);
{
U64 idx = 0;
for EachNode(n, D2R_UnitTagTreeOffChunkNode, tree_offs.first)
{
MemoryCopy(tree_offs_array.v + idx, n->v, sizeof(n->v[0])*n->count);
idx += n->count;
}
}
unit_info_root_tag_offs[unit_idx] = tree_offs_array;
ins_atomic_u64_add_eval(total_root_tag_count_ptr, tree_offs_array.count);
}
scratch_end(scratch2);
}
lane_sync();
total_root_tag_count = *total_root_tag_count_ptr;
}
////////////////////////////
//- rjf: produce list of (unit * range(root_tag_idx)), so that we can easily
// subdivide work from all units across all lanes
//
typedef struct D2R_SubUnitWork D2R_SubUnitWork;
struct D2R_SubUnitWork
{
U64 unit_idx;
Rng1U64 root_tag_idx_range;
};
U64 sub_unit_works_count = 0;
D2R_SubUnitWork *sub_unit_works = 0;
ProfScope("produce sub-unit work division") if(lane_idx() == 0)
{
U64 root_tags_per_work = 1024;
for(B32 build = 0; build <= 1; build += 1)
{
U64 sub_unit_work_idx = 0;
for EachIndex(unit_idx, unit_count)
{
U64 root_tags_in_this_unit = unit_info_root_tag_offs[unit_idx].count;
U64 works_per_this_unit = (root_tags_in_this_unit+root_tags_per_work-1) / root_tags_per_work;
if(build)
{
D2R_SubUnitWork *works = sub_unit_works + sub_unit_work_idx;
U64 works_count = works_per_this_unit;
U64 root_tag_idx = 0;
for EachIndex(work_idx, works_count)
{
U64 root_tag_idx_opl = root_tag_idx + root_tags_per_work;
root_tag_idx_opl = ClampTop(root_tag_idx_opl, root_tags_in_this_unit);
works[work_idx].unit_idx = unit_idx;
works[work_idx].root_tag_idx_range = r1u64(root_tag_idx, root_tag_idx_opl);
root_tag_idx = root_tag_idx_opl;
}
}
sub_unit_work_idx += works_per_this_unit;
}
if(!build)
{
sub_unit_works_count = sub_unit_work_idx;
sub_unit_works = push_array(scratch.arena, D2R_SubUnitWork, sub_unit_works_count);
}
}
}
lane_sync_u64(&sub_unit_works_count, 0);
lane_sync_u64(&sub_unit_works, 0);
////////////////////////////
//- rjf: build built-in types
//
RDIM_TypeChunkList *builtin_types = 0;
RDIM_Type **builtin_type_from_kind_map = 0; // [RDI_TypeKind_LastBuiltIn - RDI_TypeKind_FirstBuiltIn + 1]
U64 builtin_type_count = RDI_TypeKind_LastBuiltIn - RDI_TypeKind_FirstBuiltIn + 1;
ProfScope("build built-in types") if(lane_idx() == 0)
{
builtin_types = push_array(scratch.arena, RDIM_TypeChunkList, 1);
builtin_type_from_kind_map = push_array(scratch.arena, RDIM_Type *, builtin_type_count);
for(RDI_TypeKind k = RDI_TypeKind_FirstBuiltIn; k <= RDI_TypeKind_LastBuiltIn; k += 1)
{
RDIM_Type *type = rdim_type_chunk_list_push(arena, builtin_types, builtin_type_count);
type->kind = k;
type->name.str = rdi_string_from_type_kind(k, &type->name.size);
type->byte_size = rdi_size_from_basic_type_kind(k);
if(type->byte_size == max_U32) { type->byte_size = byte_size_from_arch(arch); }
builtin_type_from_kind_map[k - RDI_TypeKind_FirstBuiltIn] = type;
}
}
lane_sync_u64(&builtin_types, 0);
lane_sync_u64(&builtin_type_from_kind_map, 0);
#define d2r_type_from_builtin_kind(k) ((RDI_TypeKind_FirstBuiltIn <= (k) && (k) <= RDI_TypeKind_LastBuiltIn) ? builtin_type_from_kind_map[k - RDI_TypeKind_FirstBuiltIn] : builtin_type_from_kind_map[RDI_TypeKind_Void])
////////////////////////////
//- rjf: predict the total number of tags in all units
//
U64 total_tag_count_estimate = 1;
{
U64 tag_size_estimate = 32;
for EachIndex(unit_idx, unit_count)
{
total_tag_count_estimate += dim_1u64(unit_info_tag_ranges[unit_idx]) / tag_size_estimate;
}
}
////////////////////////////
//- rjf: gather all unique, to-be-deduplicated tags across all units (types, namespaces)
//
D2R_UniqueTagNode **unique_tag_slots = 0;
U64 unique_tag_slots_count = total_tag_count_estimate/8 + 1;
D2R_UnitDedupedTagMap *unit_deduped_tag_maps = 0;
ProfScope("gather all unique, to-be-deduplicated tags across all units (types, namespaces)")
{
//- rjf: set up tables
if(lane_idx() == 0)
{
unique_tag_slots = push_array(scratch.arena, D2R_UniqueTagNode *, unique_tag_slots_count);
unit_deduped_tag_maps = push_array(scratch.arena, D2R_UnitDedupedTagMap, unit_count);
}
lane_sync_u64(&unique_tag_slots, 0);
lane_sync_u64(&unit_deduped_tag_maps, 0);
//- rjf: set up per-unit deduplicated tag maps
{
Rng1U64 range = lane_range(unit_count);
for EachInRange(unit_idx, range)
{
Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx];
unit_deduped_tag_maps[unit_idx].slots_count = dim_1u64(unit_info_tag_range) / 256 + 1;
unit_deduped_tag_maps[unit_idx].slots = push_array(scratch.arena, D2R_UnitDedupedTagNode *, unit_deduped_tag_maps[unit_idx].slots_count);
}
lane_sync();
}
//- rjf: gather unique tags
{
U64 work_take_idx_ = 0;
U64 *work_take_idx_ptr = &work_take_idx_;
lane_sync_u64(&work_take_idx_ptr, 0);
for(;;)
{
//- rjf: take next work
U64 work_idx = ins_atomic_u64_inc_eval(work_take_idx_ptr) - 1;
if(work_idx >= sub_unit_works_count)
{
break;
}
Temp work_scratch = scratch_begin(&scratch.arena, 1);
//- rjf: unpack work
U64 origin_unit_idx = sub_unit_works[work_idx].unit_idx;
Rng1U64 origin_unit_root_tag_idx_range = sub_unit_works[work_idx].root_tag_idx_range;
//- rjf: unpack unit info
DW2_ParseCtx *origin_unit_parse_ctx = &unit_parse_ctxs[origin_unit_idx];
Rng1U64 origin_unit_info_tag_range = unit_info_tag_ranges[origin_unit_idx];
//- rjf: gather all tags which we want to deduplicate
typedef struct D2R_TagNode D2R_TagNode;
struct D2R_TagNode
{
D2R_TagNode *next;
D2R_UniqueTagKind kind;
U64 info_off;
U64 container_ancestor_info_off;
U64 hash_seed;
};
D2R_TagNode *first_tag_to_dedup = 0;
D2R_TagNode *last_tag_to_dedup = 0;
for(U64 root_tag_idx = origin_unit_root_tag_idx_range.min;
root_tag_idx < origin_unit_root_tag_idx_range.max;
root_tag_idx += 1)
{
U64 root_tag_start_off = unit_info_root_tag_offs[origin_unit_idx].v[root_tag_idx];
typedef struct D2R_ParentTagNode D2R_ParentTagNode;
struct D2R_ParentTagNode
{
D2R_ParentTagNode *next;
DW_TagKind tag_kind;
U64 info_off;
U64 hash_seed;
};
D2R_ParentTagNode *top_parent = 0;
D2R_ParentTagNode *free_parent = 0;
S64 depth = 0;
for(U64 off = root_tag_start_off; contains_1u64(origin_unit_info_tag_range, off) && (depth > 0 || off == root_tag_start_off);)
{
Temp tag_scratch = scratch_begin(&work_scratch.arena, 1);
U64 start_off = off;
// rjf: read tag
DW2_Tag tag = {0};
off += dw2_read_tag(tag_scratch.arena, raw, origin_unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag);
// rjf: is this one of the tags we'd like to dedup? -> gather
if(tag.kind == DW_TagKind_ArrayType ||
tag.kind == DW_TagKind_ClassType ||
tag.kind == DW_TagKind_EnumerationType ||
tag.kind == DW_TagKind_PointerType ||
tag.kind == DW_TagKind_ReferenceType ||
tag.kind == DW_TagKind_StringType ||
tag.kind == DW_TagKind_StructureType ||
tag.kind == DW_TagKind_SubroutineType ||
tag.kind == DW_TagKind_SubProgram ||
tag.kind == DW_TagKind_Typedef ||
tag.kind == DW_TagKind_UnionType ||
tag.kind == DW_TagKind_PtrToMemberType ||
tag.kind == DW_TagKind_SetType ||
tag.kind == DW_TagKind_BaseType ||
tag.kind == DW_TagKind_ConstType ||
tag.kind == DW_TagKind_FileType ||
tag.kind == DW_TagKind_PackedType ||
tag.kind == DW_TagKind_VolatileType ||
tag.kind == DW_TagKind_RestrictType ||
tag.kind == DW_TagKind_InterfaceType ||
tag.kind == DW_TagKind_UnspecifiedType ||
tag.kind == DW_TagKind_SharedType ||
tag.kind == DW_TagKind_RValueReferenceType ||
tag.kind == DW_TagKind_CoarrayType ||
tag.kind == DW_TagKind_DynamicType ||
tag.kind == DW_TagKind_AtomicType ||
tag.kind == DW_TagKind_ImmutableType ||
tag.kind == DW_TagKind_Namespace)
{
U64 container_ancestor_info_off = 0;
for EachNode(n, D2R_ParentTagNode, top_parent)
{
if(n->tag_kind == DW_TagKind_StructureType ||
n->tag_kind == DW_TagKind_UnionType ||
n->tag_kind == DW_TagKind_ClassType ||
n->tag_kind == DW_TagKind_SubProgram ||
n->tag_kind == DW_TagKind_LexicalBlock ||
n->tag_kind == DW_TagKind_Namespace)
{
container_ancestor_info_off = n->info_off;
break;
}
}
D2R_TagNode *n = push_array(work_scratch.arena, D2R_TagNode, 1);
n->kind = (tag.kind == DW_TagKind_Namespace ? D2R_UniqueTagKind_Namespace : D2R_UniqueTagKind_Type);
n->info_off = start_off;
n->container_ancestor_info_off = container_ancestor_info_off;
n->hash_seed = top_parent ? top_parent->hash_seed : 0;
SLLQueuePush(first_tag_to_dedup, last_tag_to_dedup, n);
}
// rjf: compute hash seed for this tag
U64 hash_seed = (top_parent ? top_parent->hash_seed : 0);
if(tag.kind == DW_TagKind_SubProgram ||
tag.kind == DW_TagKind_StructureType ||
tag.kind == DW_TagKind_UnionType ||
tag.kind == DW_TagKind_ClassType ||
tag.kind == DW_TagKind_Namespace)
{
DW2_Attrib *name_attrib = dw2_attrib_from_kind(&tag, DW_AttribKind_Name);
if(name_attrib != &dw2_attrib_nil)
{
hash_seed = u64_hash_from_str8(name_attrib->val.string);
}
}
// rjf: update depth / parent stack
if(tag.has_children)
{
depth += 1;
D2R_ParentTagNode *n = free_parent;
if(n != 0)
{
SLLStackPop(free_parent);
}
else
{
n = push_array(work_scratch.arena, D2R_ParentTagNode, 1);
}
SLLStackPush(top_parent, n);
n->tag_kind = tag.kind;
n->info_off = start_off;
n->hash_seed = hash_seed;
}
if(tag.kind == DW_TagKind_Null)
{
depth -= 1;
depth = Max(0, depth);
if(top_parent != 0)
{
D2R_ParentTagNode *popped = top_parent;
SLLStackPop(top_parent);
SLLStackPush(free_parent, popped);
}
}
scratch_end(tag_scratch);
if(off == start_off)
{
break;
}
}
}
//- rjf: hash all tags we need to deduplicate & gather
for(D2R_TagNode *tag_n = first_tag_to_dedup; tag_n != 0; tag_n = tag_n->next)
{
Temp dedup_root_scratch = scratch_begin(&scratch.arena, 1);
U64 start_off = tag_n->info_off;
D2R_UniqueTagKind unique_tag_kind = tag_n->kind;
U64 container_ancestor_info_off = tag_n->container_ancestor_info_off;
U64 hash_seed = tag_n->hash_seed;
//- rjf: hash all tags & dependency tag trees
U64 hash = hash_seed;
{
typedef struct TagTask TagTask;
struct TagTask
{
TagTask *next;
U64 unit_idx;
U64 off;
U64 order_idx;
};
TagTask start_task = {0, origin_unit_idx, start_off};
TagTask *top_task = &start_task;
TagTask *free_task = 0;
U64 seen_task_slots_count = 16;
TagTask **seen_task_slots = push_array(dedup_root_scratch.arena, TagTask *, seen_task_slots_count);
for(TagTask *t = top_task, *next = 0; t != 0; t = next)
{
next = 0;
U64 t_off = t->off;
// rjf: record this task in our seen task table
{
U64 hash = u64_hash_from_str8(str8_struct(&t->off));
U64 slot_idx = hash%seen_task_slots_count;
SLLStackPush(seen_task_slots[slot_idx], t);
}
// rjf: unpack unit
Rng1U64 unit_info_tag_range = unit_info_tag_ranges[t->unit_idx];
DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[t->unit_idx];
// rjf: read/hash the full tag tree at `t_off`; kick off additional
// tasks for referenced dependency types
S64 depth = 0;
B32 hash_should_include_children = 1;
for(;unit_info_tag_range.min <= t_off && t_off < unit_info_tag_range.max && (depth > 0 || t_off == t->off);)
{
Temp tag_scratch = scratch_begin(&dedup_root_scratch.arena, 1);
U64 t_start_off = t_off;
// rjf: read tag
DW2_Tag tag = {0};
t_off += dw2_read_tag(tag_scratch.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, t_off, &tag);
// rjf: determine if tag should be skipped from hashing
B32 should_skip_tag = (tag.kind == DW_TagKind_LexicalBlock ||
tag.kind == DW_TagKind_Variable);
// rjf: determine if we want to consider tag children as part of the content hash
if(t_start_off == t->off && tag.kind == DW_TagKind_Namespace)
{
hash_should_include_children = 0;
}
// rjf: if we want to combine this tag into the hash -> combine
if(!should_skip_tag)
{
// rjf: combine tag's kind
hash = u64_hash_from_seed_str8(hash, str8_struct(&tag.kind));
// rjf: combine non-reference attributes (references could be different,
// because of deduping, but they could match ultimately). for any
// referenced dependency types, kick them off
for(DW2_AttribNode *n = tag.attribs.first; n != 0; n = n->next)
{
// rjf: non-reference? -> combine attribute value info
if(n->v.val.kind != DW_FormKind_RefAddr &&
n->v.val.kind != DW_FormKind_Ref1 &&
n->v.val.kind != DW_FormKind_Ref2 &&
n->v.val.kind != DW_FormKind_Ref4 &&
n->v.val.kind != DW_FormKind_Ref8 &&
n->v.val.kind != DW_FormKind_RefUData &&
n->v.val.kind != DW_FormKind_RefSup4 &&
n->v.val.kind != DW_FormKind_RefSig8 &&
((tag.kind != DW_TagKind_SubProgram &&
tag.kind != DW_TagKind_FormalParameter) ||
(n->v.attrib_kind != DW_AttribKind_Name &&
n->v.attrib_kind != DW_AttribKind_DeclFile &&
n->v.attrib_kind != DW_AttribKind_DeclLine &&
n->v.attrib_kind != DW_AttribKind_Prototyped &&
n->v.attrib_kind != DW_AttribKind_External &&
n->v.attrib_kind != DW_AttribKind_FrameBase &&
n->v.attrib_kind != DW_AttribKind_Location &&
n->v.attrib_kind != DW_AttribKind_LowPc &&
n->v.attrib_kind != DW_AttribKind_HighPc)))
{
hash = u64_hash_from_seed_str8(hash, str8_struct(&n->v.val.kind));
if(n->v.val.string.size != 0)
{
hash = u64_hash_from_seed_str8(hash, n->v.val.string);
}
else if(n->v.val.addr != 0)
{
hash = u64_hash_from_seed_str8(hash, str8_struct(&n->v.val.addr));
}
else
{
hash = u64_hash_from_seed_str8(hash, str8_struct(&n->v.val.u128));
}
}
// rjf: type reference? -> if seen, combine the order; if not, recurse
if(n->v.attrib_kind == DW_AttribKind_Type)
{
// rjf: unpack reference
U64 ref_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &n->v.val);
// rjf: determine if we've seen this reference
B32 already_seen = 0;
U64 already_seen_order_idx = 0;
{
U64 off_hash = u64_hash_from_str8(str8_struct(&ref_info_off));
U64 off_slot_idx = off_hash%seen_task_slots_count;
for(TagTask *t = seen_task_slots[off_slot_idx]; t != 0; t = t->next)
{
if(t->off == ref_info_off)
{
already_seen = 1;
already_seen_order_idx = t->order_idx;
break;
}
}
}
// rjf: if we've seen -> hash the order
if(already_seen)
{
hash = u64_hash_from_seed_str8(hash, str8_struct(&already_seen_order_idx));
}
// rjf: if we've not seen -> descend
if(!already_seen)
{
TagTask *dependency_task = free_task;
if(dependency_task != 0)
{
SLLStackPop(free_task);
}
else
{
dependency_task = push_array(dedup_root_scratch.arena, TagTask, 1);
}
dependency_task->next = next;
next = dependency_task;
dependency_task->off = ref_info_off;
dependency_task->unit_idx = t->unit_idx;
if(!contains_1u64(unit_info_tag_range, dependency_task->off))
{
U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, dependency_task->off);
if(0 < new_unit_num && new_unit_num <= unit_count)
{
dependency_task->unit_idx = new_unit_num-1;
}
}
dependency_task->order_idx = t->order_idx+1;
}
}
}
}
// rjf: tree nav if we need to
if(hash_should_include_children)
{
if(tag.has_children)
{
depth += 1;
}
if(tag.kind == DW_TagKind_Null)
{
depth -= 1;
depth = Max(0, depth);
}
}
scratch_end(tag_scratch);
}
}
}
//- rjf: atomically gather this hash if not already gathered
{
B32 gathered = 0;
U64 slot_idx = hash%unique_tag_slots_count;
for(;!gathered;)
{
// rjf: read existing slot head pointer *before* we lookup / insert
U64 slot_head_val = (U64)ins_atomic_u64_eval(&unique_tag_slots[slot_idx]);
// rjf: determine if this hash has been gathered
D2R_UniqueTagNode *already_gathered_node = 0;
for(D2R_UniqueTagNode *n = (D2R_UniqueTagNode *)slot_head_val; n != 0; n = n->next)
{
if(n->hash == hash)
{
gathered = 1;
already_gathered_node = n;
break;
}
}
// rjf: if this hash *has* been gathered, we want to prefer a
// deterministic unit/info offset - so we'll just prefer the
// lowest unit/offset.
//
// TODO(rjf): this seems to only be necessary because of a flaw
// in the deduplicator logic (without this part). in theory, if we
// trust the hash (all experiments suggested that we should - e.g.
// no hash collisions w/ different data were observed), then the
// same hash should imply an identical type graph. this may be false
// for reasons I do not currently understand, or due to a mistake
// somewhere. but for now, I've added this, to ensure that when there
// are many type tag trees which map to an identical hash, we always
// parse the one from the lowest .debug_info offset, just to make
// sure it's deterministic. in a multi-threaded conversion, this
// is not true in general without this step; one thread which finds
// the same type with a higher .debug_info offset may win, and so
// where exactly one type hash is parsed is not deterministic. but,
// the theory is, that shouldn't matter - the type graphs should
// match nonetheless. in any case, if that turns out to be a broken
// theory, this should be a relatively cheap step to guarantee
// deterministic (hash -> .debug_info offset), which is likely a
// desirable property regardless. but I wanted to leave this here
// so that I can more closely verify what is going on later.
//
if(gathered && already_gathered_node != 0)
{
B32 minimized = 0;
for(;!minimized;)
{
U64 stored_info_off = ins_atomic_u64_eval(&already_gathered_node->info_off);
if(start_off < stored_info_off)
{
if(ins_atomic_u64_eval_cond_assign(&already_gathered_node->info_off, start_off, stored_info_off) == stored_info_off)
{
minimized = 1;
}
}
else
{
minimized = 1;
}
}
}
// rjf: if this hash has *not* been gathered, try an insert. we:
//
// 1. allocate/fill a node
// 2. set it up to point to the old head
// 3. compare/exchange the old head with the new head - IFF the head matches what we expect from above
// 4. if we fail, another thread has touched this slot, we pop the allocated node & try again
// (we may find that another thread has filled this hash, so we'll just be done)
//
if(!gathered)
{
Temp insert_temp = temp_begin(scratch.arena);
D2R_UniqueTagNode *n = push_array(scratch.arena, D2R_UniqueTagNode, 1);
n->next = (D2R_UniqueTagNode *)slot_head_val;
n->kind = unique_tag_kind;
n->hash = hash;
n->info_off = start_off;
n->container_ancestor_info_off = container_ancestor_info_off;
U64 new_head_val = (U64)n;
if(slot_head_val == ins_atomic_u64_eval_cond_assign(&unique_tag_slots[slot_idx], new_head_val, slot_head_val))
{
gathered = 1;
}
else
{
temp_end(insert_temp);
}
}
}
}
//- rjf: atomically record this (info_off -> hash) mapping, so that when we have
// later references to this type, we can redirect to the deduplicated type with
// the right hash later.
{
U64 info_off_hash = u64_hash_from_str8(str8_struct(&start_off));
U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_maps[origin_unit_idx].slots_count;
D2R_UnitDedupedTagNode *n = push_array(scratch.arena, D2R_UnitDedupedTagNode, 1);
n->src_info_off = start_off;
n->dst_hash = hash;
for(B32 inserted = 0; !inserted;)
{
U64 slot_head_val = ins_atomic_u64_eval(&unit_deduped_tag_maps[origin_unit_idx].slots[info_off_slot_idx]);
n->next = (D2R_UnitDedupedTagNode *)slot_head_val;
if(slot_head_val == ins_atomic_u64_eval_cond_assign(&unit_deduped_tag_maps[origin_unit_idx].slots[info_off_slot_idx], (U64)n, slot_head_val))
{
inserted = 1;
}
}
}
scratch_end(dedup_root_scratch);
}
scratch_end(work_scratch);
}
lane_sync();
}
}
////////////////////////////
//- rjf: sort all unique tag table slots
//
ProfScope("sort all unique tag table slots")
{
Rng1U64 range = lane_range(unique_tag_slots_count);
for EachInRange(slot_idx, range)
{
Temp scratch2 = scratch_begin(&scratch.arena, 1);
D2R_UniqueTagNode **slot = &unique_tag_slots[slot_idx];
// rjf: count nodes in this slot
U64 node_count = 0;
for EachNode(n, D2R_UniqueTagNode, slot[0])
{
node_count += 1;
}
// rjf: flatten
D2R_UniqueTagNode **slot_nodes_array = push_array(scratch2.arena, D2R_UniqueTagNode *, node_count);
{
U64 idx = 0;
for EachNode(n, D2R_UniqueTagNode, slot[0])
{
slot_nodes_array[idx] = n;
idx += 1;
}
}
// rjf: sort
radsort(slot_nodes_array, node_count, (int (*)(void *, void *))d2r_unique_tag_node_is_less_than);
// rjf: re-order in the slot
{
unique_tag_slots[slot_idx] = 0;
for EachIndex(idx, node_count)
{
SLLStackPush(unique_tag_slots[slot_idx], slot_nodes_array[idx]);
}
}
scratch_end(scratch2);
}
lane_sync();
}
////////////////////////////
//- rjf: produce [0...n) <-> unique-tag-node mapping for all categories of deduplicated tags
//
U64 *deduped_tag_counts = 0; // [D2R_UniqueTagKind_COUNT]
D2R_UniqueTagNode ***deduped_tag_nodes = 0; // [D2R_UniqueTagKind_COUNT]
ProfScope("produce [0...n) -> hash mapping for all categories of deduplicated tags") if(lane_idx() == 0)
{
deduped_tag_counts = push_array(scratch.arena, U64, D2R_UniqueTagKind_COUNT);
deduped_tag_nodes = push_array(scratch.arena, D2R_UniqueTagNode **, D2R_UniqueTagKind_COUNT);
for EachIndex(slot_idx, unique_tag_slots_count)
{
for EachNode(n, D2R_UniqueTagNode, unique_tag_slots[slot_idx])
{
deduped_tag_counts[n->kind] += 1;
}
}
for EachEnumVal(D2R_UniqueTagKind, k)
{
deduped_tag_nodes[k] = push_array(scratch.arena, D2R_UniqueTagNode *, deduped_tag_counts[k]);
}
U64 idxs[D2R_UniqueTagKind_COUNT] = {0};
for EachIndex(slot_idx, unique_tag_slots_count)
{
for EachNode(n, D2R_UniqueTagNode, unique_tag_slots[slot_idx])
{
deduped_tag_nodes[n->kind][idxs[n->kind]] = n;
n->order_idx = idxs[n->kind];
idxs[n->kind] += 1;
}
}
}
lane_sync_u64(&deduped_tag_counts, 0);
lane_sync_u64(&deduped_tag_nodes, 0);
U64 type_count = deduped_tag_counts[D2R_UniqueTagKind_Type];
D2R_UniqueTagNode **type_tag_nodes = deduped_tag_nodes[D2R_UniqueTagKind_Type];
U64 namespace_count = deduped_tag_counts[D2R_UniqueTagKind_Namespace];
D2R_UniqueTagNode **namespace_tag_nodes = deduped_tag_nodes[D2R_UniqueTagKind_Namespace];
////////////////////////////
//- rjf: gather per-type dependency chains
//
typedef struct TypeDepChain TypeDepChain;
struct TypeDepChain
{
TypeDepChain *next;
U64 type_idx;
};
U64 *type_dep_chains_counts = 0;
ProfScope("gather per-type dependency chains")
{
if(lane_idx() == 0)
{
type_dep_chains_counts = push_array(scratch.arena, U64, type_count);
}
lane_sync_u64(&type_dep_chains_counts, 0);
Rng1U64 range = lane_range(type_count);
for EachInRange(root_type_idx, range)
{
typedef struct TypeChainTask TypeChainTask;
struct TypeChainTask
{
TypeChainTask *next;
U64 hash;
};
TypeChainTask start_task = {0, type_tag_nodes[root_type_idx]->hash};
TypeChainTask *top_task = &start_task;
TypeChainTask *free_task = 0;
TypeChainTask *last_t = 0;
for(TypeChainTask *t = top_task; t != 0; (last_t = t, t = t->next))
{
Temp scratch2 = scratch_begin(&scratch.arena, 1);
// rjf: recycle old tasks
if(last_t != 0)
{
SLLStackPush(free_task, last_t);
}
// rjf: unpack task
U64 hash = t->hash;
// rjf: hash -> unique type tag node
D2R_UniqueTagNode *type_tag_node = 0;
{
U64 slot_idx = hash%unique_tag_slots_count;
for(D2R_UniqueTagNode *n = unique_tag_slots[slot_idx]; n != 0; n = n->next)
{
if(n->hash == hash)
{
type_tag_node = n;
break;
}
}
}
// rjf: unpack type tag node
U64 info_off = 0;
U64 unit_idx = 0;
if(type_tag_node != 0)
{
info_off = type_tag_node->info_off;
U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off);
unit_idx = unit_num > 0 ? unit_num-1 : 0;
}
// rjf: record this type in the dependency chain count
if(type_tag_node != 0)
{
type_dep_chains_counts[root_type_idx] += 1;
}
// rjf: unpack unit
DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx];
Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx];
// rjf: parse this type's tag
U64 read_off = info_off;
DW2_Tag tag = {0};
read_off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, read_off, &tag);
// rjf: find direct types from this type tag
typedef struct DirectTypeNode DirectTypeNode;
struct DirectTypeNode
{
DirectTypeNode *next;
U64 info_off;
U64 unit_idx;
};
DirectTypeNode *first_direct_type = 0;
DirectTypeNode *last_direct_type = 0;
switch(tag.kind)
{
default:{}break;
case DW_TagKind_PointerType:
case DW_TagKind_ReferenceType:
case DW_TagKind_RValueReferenceType:
case DW_TagKind_RestrictType:
case DW_TagKind_VolatileType:
case DW_TagKind_ConstType:
case DW_TagKind_ArrayType:
case DW_TagKind_Typedef:
case DW_TagKind_SubProgram:
case DW_TagKind_SubroutineType:
case DW_TagKind_EnumerationType:
{
// rjf: gather direct type
{
DW2_Attrib *direct_type_attrib = dw2_attrib_from_kind(&tag, DW_AttribKind_Type);
U64 direct_type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &direct_type_attrib->val);
U64 direct_type_unit_idx = unit_idx;
if(!contains_1u64(unit_info_tag_range, direct_type_info_off))
{
U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, direct_type_info_off);
if(0 < new_unit_num && new_unit_num <= unit_count)
{
direct_type_unit_idx = new_unit_num-1;
}
}
DirectTypeNode *n = push_array(scratch2.arena, DirectTypeNode, 1);
n->info_off = direct_type_info_off;
n->unit_idx = direct_type_unit_idx;
SLLQueuePush(first_direct_type, last_direct_type, n);
}
// rjf: functions -> gather parameters
if(tag.has_children && (tag.kind == DW_TagKind_SubProgram || tag.kind == DW_TagKind_SubroutineType))
{
S64 depth = 1;
for(;depth > 0 && contains_1u64(unit_info_tag_range, read_off);)
{
U64 start_read_off = read_off;
// rjf: read child tag
DW2_Tag child_tag = {0};
read_off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, read_off, &child_tag);
// rjf: formal parameters -> gather direct types
if(depth == 1 && child_tag.kind == DW_TagKind_FormalParameter)
{
DW2_Attrib *direct_type_attrib = dw2_attrib_from_kind(&child_tag, DW_AttribKind_Type);
U64 direct_type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &direct_type_attrib->val);
U64 direct_type_unit_idx = unit_idx;
if(!contains_1u64(unit_info_tag_range, direct_type_info_off))
{
U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, direct_type_info_off);
if(0 < new_unit_num && new_unit_num <= unit_count)
{
direct_type_unit_idx = new_unit_num-1;
}
}
DirectTypeNode *n = push_array(scratch2.arena, DirectTypeNode, 1);
n->info_off = direct_type_info_off;
n->unit_idx = direct_type_unit_idx;
SLLQueuePush(first_direct_type, last_direct_type, n);
}
// rjf: tree navigations
if(child_tag.has_children)
{
depth += 1;
}
if(child_tag.kind == DW_TagKind_Null)
{
depth -= 1;
}
if(read_off == start_read_off)
{
break;
}
}
}
}break;
}
// rjf: for each dependency type, look up their hash, + spawn new tasks for them
for EachNode(n, DirectTypeNode, first_direct_type)
{
U64 direct_type_info_off = n->info_off;
U64 direct_type_unit_idx = n->unit_idx;
// rjf: direct type info offset -> hash
U64 direct_type_hash = 0;
{
U64 off_hash = u64_hash_from_str8(str8_struct(&direct_type_info_off));
U64 off_slot_idx = off_hash%unit_deduped_tag_maps[direct_type_unit_idx].slots_count;
for(D2R_UnitDedupedTagNode *n = unit_deduped_tag_maps[direct_type_unit_idx].slots[off_slot_idx]; n != 0; n = n->next)
{
if(n->src_info_off == direct_type_info_off)
{
direct_type_hash = n->dst_hash;
break;
}
}
}
// rjf: spawn task
if(direct_type_hash != 0)
{
TypeChainTask *new_task = free_task;
if(new_task != 0)
{
SLLStackPop(free_task);
}
else
{
new_task = push_array(scratch.arena, TypeChainTask, 1);
}
new_task->next = t->next;
new_task->hash = direct_type_hash;
t->next = new_task;
}
}
scratch_end(scratch2);
}
}
lane_sync();
}
////////////////////////////
//- rjf: build all types - build types w/ 1 dependency (leaves) first, then 2, 3, etc.,
// to ensure dependencies always travel backwards
//
// NOTE: * DWARF vs RDI Array Type Graph *
//
// For example lets take following decl:
//
// int (*foo[2])[3];
//
// This compiles to in DWARF:
//
// foo -> DW_TAG_ArrayType -> (A0) DW_TAG_Subrange [2]
// \
// -> (B0) DW_TAG_PointerType -> (A1) DW_TAG_ArrayType -> DW_TAG_Subrange [3]
// \
// -> (B1) DW_TAG_BaseType (int)
//
// RDI expects:
//
// foo -> Array[2] -> Pointer -> Array[3] -> int
//
// Note that DWARF forks the graph on DW_TAG_ArrayType to describe array ranges in branch A and
// in branch B describes array type which might be a struct, pointer, base type, or any other type tag.
// However, in RDI we have a simple list of type nodes and to convert we need to append type nodes from
// B to A.
//
RDIM_TypeChunkList *all_types = 0;
RDIM_Type **type_from_idx_map = 0;
ProfScope("build all types")
{
if(lane_idx() == 0)
{
all_types = push_array(scratch.arena, RDIM_TypeChunkList, 1);
type_from_idx_map = push_array(scratch.arena, RDIM_Type *, type_count);
rdim_type_chunk_list_concat_in_place(all_types, builtin_types);
}
lane_sync_u64(&all_types, 0);
lane_sync_u64(&type_from_idx_map, 0);
Rng1U64 range = lane_range(type_count);
U64 max_chain_count = 1;
for(;max_chain_count < max_U64;)
{
Temp scratch2 = scratch_begin(&scratch.arena, 1);
ProfBegin("types build (chain count <= %I64u)", max_chain_count);
//- rjf: gather all types in this lane that fit the dependency restriction;
// find this lane's next dependency restriction
U64 next_max_chain_count = max_U64;
RDIM_TypeChunkList lane_types = {0};
U64 lane_types_chunk_count = 256;
for EachInRange(type_idx, range)
{
// rjf: if this type has a higher chain count than the current,
// but it is lower than our next maximum chain count, collect it,
// so we will hit
//
// if this type has a lower chain count than the current,
// we should've already built it from a previous pass.
//
if(type_dep_chains_counts[type_idx] > max_chain_count)
{
next_max_chain_count = Min(type_dep_chains_counts[type_idx], next_max_chain_count);
continue;
}
else if(type_dep_chains_counts[type_idx] < max_chain_count)
{
continue;
}
Temp temp = temp_begin(scratch2.arena);
// rjf: idx -> type tag node
D2R_UniqueTagNode *type_tag_node = type_tag_nodes[type_idx];
U64 info_off = type_tag_node->info_off;
U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off);
U64 unit_idx = unit_num > 0 ? unit_num-1 : 0;
// rjf: unpack unit
Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx];
DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx];
// rjf: parse root-level tag
DW2_Tag tag = {0};
U64 tag_info_size = dw2_read_tag(temp.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, info_off, &tag);
// rjf: extract common attributes
DW2_Attrib *name_attrib = &dw2_attrib_nil;
DW2_Attrib *direct_type_attrib = &dw2_attrib_nil;
DW2_Attrib *bitsize_attrib = &dw2_attrib_nil;
DW2_Attrib *bytesize_attrib = &dw2_attrib_nil;
DW2_Attrib *encoding_attrib = &dw2_attrib_nil;
DW2_Attrib *decl_attrib = &dw2_attrib_nil;
DW2_Attrib *prototyped_attrib = &dw2_attrib_nil;
//
// TODO(rjf): if a DW_AttribKing_GNU_Vector is found on an DW_TagKind_ArrayType,
// then: @native_vector_support extract byte size from the base type tag
// and convert to U256, U512, S256 and S512
//
for EachNode(n, DW2_AttribNode, tag.attribs.first)
{
switch(n->v.attrib_kind)
{
default:{}break;
#define Case(dst_name, src_name) case DW_AttribKind_##src_name:{dst_name##_attrib = &n->v;}break
Case(name, Name);
Case(direct_type, Type);
Case(bitsize, BitSize);
Case(bytesize, ByteSize);
Case(encoding, Encoding);
Case(decl, Declaration);
#undef Case
}
}
// rjf: unpack common attributes
String8 name = name_attrib->val.string;
DW_ATE encoding = encoding_attrib->val.u128.u64[0];
RDIM_Type *direct_type = d2r_type_from_builtin_kind(RDI_TypeKind_Void);
U64 bitsize = 0;
B32 is_decl = (decl_attrib != &dw2_attrib_nil);
{
// rjf: unpack direct type
if(direct_type_attrib != &dw2_attrib_nil)
{
U64 direct_type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &direct_type_attrib->val);
U64 direct_type_unit_idx = unit_idx;
if(!contains_1u64(unit_info_tag_range, direct_type_info_off))
{
U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, direct_type_info_off);
direct_type_unit_idx = (new_unit_num > 0 ? new_unit_num-1 : unit_idx);
}
D2R_UnitDedupedTagMap *direct_type_unit_type_map = &unit_deduped_tag_maps[direct_type_unit_idx];
U64 info_off_hash = u64_hash_from_str8(str8_struct(&direct_type_info_off));
U64 info_off_slot_idx = info_off_hash%direct_type_unit_type_map->slots_count;
U64 direct_type_hash = 0;
for(D2R_UnitDedupedTagNode *n = direct_type_unit_type_map->slots[info_off_slot_idx]; n != 0; n = n->next)
{
if(n->src_info_off == direct_type_info_off)
{
direct_type_hash = n->dst_hash;
break;
}
}
U64 unique_type_tag_slot_idx = direct_type_hash%unique_tag_slots_count;
for(D2R_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next)
{
if(n->hash == direct_type_hash)
{
direct_type = type_from_idx_map[n->order_idx];
break;
}
}
}
// rjf: unpack bit/byte sizes
if(bitsize_attrib != &dw2_attrib_nil)
{
bitsize = bitsize_attrib->val.u128.u64[0];
}
else if(bytesize_attrib != &dw2_attrib_nil)
{
bitsize = bytesize_attrib->val.u128.u64[0]*8;
}
}
// rjf: convert type
RDIM_Type *dst_type = 0;
RDI_TypeKind rdi_type_kind = RDI_TypeKind_NULL;
RDI_TypeModifierFlags rdi_type_modifier_flags = 0;
switch(tag.kind)
{
default:{}break;
case DW_TagKind_ClassType: rdi_type_kind = is_decl ? RDI_TypeKind_IncompleteClass : RDI_TypeKind_Class; goto struct_type;
case DW_TagKind_StructureType: rdi_type_kind = is_decl ? RDI_TypeKind_IncompleteStruct : RDI_TypeKind_Struct; goto struct_type;
case DW_TagKind_UnionType: rdi_type_kind = is_decl ? RDI_TypeKind_IncompleteUnion : RDI_TypeKind_Union; goto struct_type;
struct_type:;
{
dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count);
dst_type->kind = rdi_type_kind;
dst_type->name = name;
dst_type->byte_size = bitsize/8;
}break;
case DW_TagKind_EnumerationType:
{
dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count);
dst_type->kind = is_decl ? RDI_TypeKind_IncompleteEnum : RDI_TypeKind_Enum;
dst_type->name = name;
dst_type->direct_type = direct_type;
dst_type->byte_size = (direct_type != 0 ? direct_type->byte_size : bitsize/8);
}break;
case DW_TagKind_SubProgram:
case DW_TagKind_SubroutineType:
{
// rjf: build type
dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count);
dst_type->kind = RDI_TypeKind_Function;
dst_type->direct_type = direct_type;
dst_type->byte_size = arch_addr_size;
// rjf: gather all parameter types
typedef struct ParamNode ParamNode;
struct ParamNode
{
ParamNode *next;
U64 type_unit_idx;
U64 type_info_off;
};
ParamNode *first_param = 0;
ParamNode *last_param = 0;
U64 param_count = 0;
if(tag.has_children)
{
U64 tag_children_off = info_off + tag_info_size;
S64 depth = 1;
for(U64 off = tag_children_off; depth > 0 && contains_1u64(unit_info_tag_range, off);)
{
Temp scratch3 = scratch_begin(&scratch2.arena, 1);
U64 start_off = off;
// rjf: read child tag
DW2_Tag child_tag = {0};
off += dw2_read_tag(scratch3.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &child_tag);
// rjf: gather parameters
if(depth == 1 && child_tag.kind == DW_TagKind_FormalParameter)
{
DW2_Attrib *type_attrib = dw2_attrib_from_kind(&child_tag, DW_AttribKind_Type);
ParamNode *n = push_array(scratch2.arena, ParamNode, 1);
SLLQueuePush(first_param, last_param, n);
n->type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &type_attrib->val);
n->type_unit_idx = unit_idx;
if(!contains_1u64(unit_info_tag_range, n->type_info_off))
{
U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, n->type_info_off);
n->type_unit_idx = (new_unit_num > 0 ? new_unit_num-1 : unit_idx);
}
param_count += 1;
}
// rjf: navigate tree
if(child_tag.kind == DW_TagKind_Null)
{
depth -= 1;
}
if(child_tag.has_children)
{
depth += 1;
}
scratch_end(scratch3);
if(off == start_off)
{
break;
}
}
}
// rjf: tighten parameter types
dst_type->count = (RDI_U32)param_count; // TODO(rjf): @u64_to_u32
dst_type->param_types = push_array(arena, RDIM_Type *, dst_type->count);
{
U64 param_idx = 0;
for(ParamNode *n = first_param; n != 0; n = n->next, param_idx += 1)
{
U64 param_type_info_off = n->type_info_off;
U64 param_type_unit_idx = n->type_unit_idx;
// rjf: map info_off/unit_idx -> hash
U64 param_type_hash = 0;
{
D2R_UnitDedupedTagMap *direct_type_unit_type_map = &unit_deduped_tag_maps[param_type_unit_idx];
U64 info_off_hash = u64_hash_from_str8(str8_struct(&param_type_info_off));
U64 info_off_slot_idx = info_off_hash%direct_type_unit_type_map->slots_count;
for(D2R_UnitDedupedTagNode *n = direct_type_unit_type_map->slots[info_off_slot_idx]; n != 0; n = n->next)
{
if(n->src_info_off == param_type_info_off)
{
param_type_hash = n->dst_hash;
break;
}
}
}
// rjf: map hash -> type
RDIM_Type *param_type = 0;
{
U64 unique_type_tag_slot_idx = param_type_hash%unique_tag_slots_count;
for(D2R_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next)
{
if(n->hash == param_type_hash)
{
param_type = type_from_idx_map[n->order_idx];
break;
}
}
}
// rjf: store
dst_type->param_types[param_idx] = param_type;
}
}
}break;
case DW_TagKind_Typedef:
{
dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count);
dst_type->kind = RDI_TypeKind_Alias;
dst_type->name = name;
dst_type->direct_type = direct_type;
dst_type->byte_size = (direct_type ? direct_type->byte_size : 0);
}break;
case DW_TagKind_BaseType:
{
if(0){}
#define CaseA(rdi_type_kind_, encoding_) else if(encoding == (encoding_)) { rdi_type_kind = (rdi_type_kind_); }
#define CaseB(rdi_type_kind_, encoding_, bit_size_) else if(encoding == (encoding_) && bitsize == (bit_size_)) { rdi_type_kind = (rdi_type_kind_); }
#define CaseC(rdi_type_kind_, encoding_, name_) /* assumes x64/arm64 */ else if(encoding == (encoding_) && str8_match(name, str8_lit(name_), 0)) { rdi_type_kind = (rdi_type_kind_); }
#define CaseD(rdi_type_kind_, encoding_, name_, bit_size_) else if(encoding == (encoding_) && bitsize == (bit_size_) && str8_match(name, str8_lit(name_), 0)) { rdi_type_kind = (rdi_type_kind_); }
CaseA(RDI_TypeKind_NULL, DW_ATE_Null)
CaseA(RDI_TypeKind_Void, DW_ATE_Address)
CaseA(RDI_TypeKind_Bool, DW_ATE_Boolean)
CaseB(RDI_TypeKind_ComplexF32, DW_ATE_ComplexFloat, 64)
CaseB(RDI_TypeKind_ComplexF64, DW_ATE_ComplexFloat, 128)
CaseB(RDI_TypeKind_ComplexF80, DW_ATE_ComplexFloat, 160)
CaseB(RDI_TypeKind_ComplexF128, DW_ATE_ComplexFloat, 256)
CaseC(RDI_TypeKind_F80, DW_ATE_Float, "__float80")
CaseC(RDI_TypeKind_F128, DW_ATE_Float, "__float128")
CaseC(RDI_TypeKind_F16, DW_ATE_Float, "_Float16")
CaseC(RDI_TypeKind_BF16, DW_ATE_Float, "__bf16")
CaseB(RDI_TypeKind_F16, DW_ATE_Float, 16)
CaseB(RDI_TypeKind_F32, DW_ATE_Float, 32)
CaseB(RDI_TypeKind_F48, DW_ATE_Float, 48)
CaseB(RDI_TypeKind_F64, DW_ATE_Float, 64)
CaseB(RDI_TypeKind_F80, DW_ATE_Float, 80)
CaseB(RDI_TypeKind_F96, DW_ATE_Float, 96)
CaseB(RDI_TypeKind_F128, DW_ATE_Float, 128)
CaseB(RDI_TypeKind_Char8, DW_ATE_SignedChar, 8)
CaseB(RDI_TypeKind_Char16, DW_ATE_SignedChar, 16)
CaseB(RDI_TypeKind_Char32, DW_ATE_SignedChar, 32)
CaseD(RDI_TypeKind_Char8, DW_ATE_Signed, "wchar_t", 8)
CaseD(RDI_TypeKind_Char16, DW_ATE_Signed, "wchar_t", 16)
CaseD(RDI_TypeKind_Char32, DW_ATE_Signed, "wchar_t", 32)
CaseB(RDI_TypeKind_S8, DW_ATE_Signed, 8)
CaseB(RDI_TypeKind_S16, DW_ATE_Signed, 16)
CaseB(RDI_TypeKind_S32, DW_ATE_Signed, 32)
CaseB(RDI_TypeKind_S64, DW_ATE_Signed, 64)
CaseB(RDI_TypeKind_S128, DW_ATE_Signed, 128)
CaseB(RDI_TypeKind_S256, DW_ATE_Signed, 256)
CaseB(RDI_TypeKind_S512, DW_ATE_Signed, 512)
CaseB(RDI_TypeKind_U8, DW_ATE_Unsigned, 8)
CaseB(RDI_TypeKind_U16, DW_ATE_Unsigned, 16)
CaseB(RDI_TypeKind_U32, DW_ATE_Unsigned, 32)
CaseB(RDI_TypeKind_U64, DW_ATE_Unsigned, 64)
CaseB(RDI_TypeKind_U128, DW_ATE_Unsigned, 128)
CaseB(RDI_TypeKind_U256, DW_ATE_Unsigned, 256)
CaseB(RDI_TypeKind_U512, DW_ATE_Unsigned, 512)
CaseB(RDI_TypeKind_UChar8, DW_ATE_UnsignedChar, 8)
CaseB(RDI_TypeKind_UChar8, DW_ATE_Utf, 8)
CaseB(RDI_TypeKind_UChar16, DW_ATE_UnsignedChar, 16)
CaseB(RDI_TypeKind_UChar16, DW_ATE_Utf, 16)
CaseB(RDI_TypeKind_UChar32, DW_ATE_UnsignedChar, 32)
CaseB(RDI_TypeKind_UChar32, DW_ATE_Utf, 32)
CaseB(RDI_TypeKind_Decimal32, DW_ATE_DecimalFloat, 32)
CaseB(RDI_TypeKind_Decimal64, DW_ATE_DecimalFloat, 64)
CaseB(RDI_TypeKind_Decimal128, DW_ATE_DecimalFloat, 128)
#undef CaseA
#undef CaseB
#undef CaseC
#undef CaseD
dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count);
dst_type->kind = RDI_TypeKind_Alias;
dst_type->name = name;
dst_type->direct_type = d2r_type_from_builtin_kind(rdi_type_kind);
dst_type->byte_size = bitsize/8; // TODO: byte size on __float80 alias mismatches byte size on direct type now
}break;
case DW_TagKind_PointerType: rdi_type_kind = RDI_TypeKind_Ptr; goto ptr_or_ref_type;
case DW_TagKind_ReferenceType: rdi_type_kind = RDI_TypeKind_LRef; goto ptr_or_ref_type;
case DW_TagKind_RValueReferenceType: rdi_type_kind = RDI_TypeKind_RRef; goto ptr_or_ref_type;
ptr_or_ref_type:;
{
dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count);
dst_type->kind = rdi_type_kind;
dst_type->direct_type = direct_type;
dst_type->byte_size = arch_addr_size;
}break;
case DW_TagKind_RestrictType:
{
rdi_type_modifier_flags = RDI_TypeModifierFlag_Restrict;
}goto basic_type_operators;
case DW_TagKind_VolatileType:
{
rdi_type_modifier_flags = RDI_TypeModifierFlag_Volatile;
}goto basic_type_operators;
case DW_TagKind_ConstType:
{
rdi_type_modifier_flags = RDI_TypeModifierFlag_Const;
}goto basic_type_operators;
basic_type_operators:;
{
dst_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count);
dst_type->kind = RDI_TypeKind_Modifier;
dst_type->flags = rdi_type_modifier_flags;
dst_type->direct_type = direct_type;
dst_type->byte_size = (direct_type ? direct_type->byte_size : 0);
}break;
case DW_TagKind_ArrayType:
if(tag.has_children)
{
// rjf: parse array type children; extract dimensions (each one gets a SubrangeType)
typedef struct ArrayDimensionNode ArrayDimensionNode;
struct ArrayDimensionNode
{
ArrayDimensionNode *next;
U64 count;
};
ArrayDimensionNode *top_dimension = 0;
{
U64 tag_children_off = info_off + tag_info_size;
S64 depth = 1;
for(U64 off = tag_children_off; contains_1u64(unit_info_tag_range, off) && depth > 0;)
{
U64 start_off = off;
DW2_Tag child_tag = {0};
off += dw2_read_tag(temp.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &child_tag);
if(child_tag.kind == DW_TagKind_SubrangeType)
{
// rjf: extract bounds attribs
DW2_Attrib *lower_bound_attrib = &dw2_attrib_nil;
DW2_Attrib *upper_bound_attrib = &dw2_attrib_nil;
DW2_Attrib *count_attrib = &dw2_attrib_nil;
for(DW2_AttribNode *n = child_tag.attribs.first;
n != 0 && (lower_bound_attrib == &dw2_attrib_nil || (upper_bound_attrib == &dw2_attrib_nil && count_attrib == &dw2_attrib_nil));
n = n->next)
{
if(n->v.attrib_kind == DW_AttribKind_LowerBound)
{
lower_bound_attrib = &n->v;
}
else if(n->v.attrib_kind == DW_AttribKind_UpperBound)
{
upper_bound_attrib = &n->v;
}
else if(n->v.attrib_kind == DW_AttribKind_Count)
{
count_attrib = &n->v;
}
}
// rjf: resolve lower bound
U64 lower_bound = 0;
if(lower_bound_attrib != &dw2_attrib_nil)
{
if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, lower_bound_attrib->val.kind) & DW_AttribClass_Reference)
{
log_infof("[.debug_info@0x%I64x] Array type lower bound is a variable; this is not currently supported.\n", start_off);
}
else
{
lower_bound = lower_bound_attrib->val.u128.u64[0];
}
}
else switch(unit_parse_ctx->language)
{
default:{}break;
#define X(name, code, default_array_lower_bound) case DW_Language_##name:{lower_bound = (default_array_lower_bound);}break;
DW_Language_XList
#undef X
}
// rjf: resolve upper bound
U64 upper_bound = 0;
{
if(count_attrib != &dw2_attrib_nil)
{
upper_bound = lower_bound + count_attrib->val.u128.u64[0];
}
else if(upper_bound_attrib != &dw2_attrib_nil)
{
if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, upper_bound_attrib->val.kind) & DW_AttribClass_Reference)
{
log_infof("[.debug_info@0x%I64x] Array type upper bound is a variable; this is not currently supported.\n", start_off);
}
else
{
upper_bound = upper_bound_attrib->val.u128.u64[0];
upper_bound += 1; // NOTE(rjf): turn to exclusive range
}
}
}
// rjf: push node
ArrayDimensionNode *dim_n = push_array(temp.arena, ArrayDimensionNode, 1);
SLLStackPush(top_dimension, dim_n);
dim_n->count = (upper_bound - lower_bound);
}
if(child_tag.has_children)
{
depth += 1;
}
else if(child_tag.kind == DW_TagKind_Null)
{
depth -= 1;
}
if(off == start_off)
{
break;
}
}
}
// rjf: create array type operators for each dimension
RDIM_Type *array_direct_type = direct_type;
for EachNode(dim_n, ArrayDimensionNode, top_dimension)
{
RDIM_Type *array_type = rdim_type_chunk_list_push(arena, &lane_types, lane_types_chunk_count);
array_type->kind = RDI_TypeKind_Array;
array_type->byte_size = array_direct_type ? array_direct_type->byte_size*dim_n->count : 0;
array_type->direct_type = array_direct_type;
array_direct_type = array_type;
}
// rjf: final destination type -> the final array dimension type
dst_type = array_direct_type;
}break;
}
// rjf: store type in type-from-idx table
type_from_idx_map[type_idx] = dst_type;
temp_end(temp);
}
//- rjf: combine all types from all lanes
RDIM_TypeChunkList *lanes_types = 0;
if(lane_idx() == 0)
{
lanes_types = push_array(scratch2.arena, RDIM_TypeChunkList, lane_count());
}
lane_sync_u64(&lanes_types, 0);
lanes_types[lane_idx()] = lane_types;
lane_sync();
if(lane_idx() == 0)
{
RDIM_TypeChunkList pass_lane_combined_types = {0};
for EachIndex(l_idx, lane_count())
{
rdim_type_chunk_list_concat_in_place(&pass_lane_combined_types, &lanes_types[l_idx]);
}
rdim_type_chunk_list_concat_in_place(all_types, &pass_lane_combined_types);
}
lane_sync();
//- rjf: find minimum next max chain count across all lanes, for next iteration
U64 *lane_next_max_chain_counts = 0;
if(lane_idx() == 0)
{
lane_next_max_chain_counts = push_array(scratch2.arena, U64, lane_count());
}
lane_sync_u64(&lane_next_max_chain_counts, 0);
lane_next_max_chain_counts[lane_idx()] = next_max_chain_count;
lane_sync();
if(lane_idx() == 0)
{
for EachIndex(l_idx, lane_count())
{
next_max_chain_count = Min(next_max_chain_count, lane_next_max_chain_counts[l_idx]);
}
}
lane_sync_u64(&next_max_chain_count, 0);
//- rjf: update max chain count
max_chain_count = next_max_chain_count;
ProfEnd();
scratch_end(scratch2);
}
}
lane_sync();
////////////////////////////
//- rjf: convert all namespaces
//
RDIM_NamespaceChunkList *all_namespaces = 0;
RDIM_Namespace **namespace_from_idx_map = 0;
{
// rjf: set up
if(lane_idx() == 0)
{
all_namespaces = push_array(scratch.arena, RDIM_NamespaceChunkList, 1);
namespace_from_idx_map = push_array(scratch.arena, RDIM_Namespace *, namespace_count);
}
lane_sync_u64(&all_namespaces, 0);
lane_sync_u64(&namespace_from_idx_map, 0);
// rjf: build all namespaces on this lane
RDIM_NamespaceChunkList lane_namespaces = {0};
Rng1U64 range = lane_range(namespace_count);
for EachInRange(namespace_idx, range)
{
Temp scratch2 = scratch_begin(&scratch.arena, 1);
// rjf: unpack tag node
D2R_UniqueTagNode *tag_node = namespace_tag_nodes[namespace_idx];
U64 info_off = tag_node->info_off;
U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off);
U64 unit_idx = unit_num > 0 ? unit_num-1 : 0;
DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx];
// rjf: read namespace tag
DW2_Tag tag = {0};
dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, info_off, &tag);
DW2_Attrib *name_attrib = dw2_attrib_from_kind(&tag, DW_AttribKind_Name);
String8 name = name_attrib->val.string;
// rjf: build namespace
RDIM_Namespace *ns = rdim_namespace_chunk_list_push(arena, &lane_namespaces, 64);
ns->name = name;
// rjf: store in (deduped tag idx -> namespace) table
namespace_from_idx_map[tag_node->order_idx] = ns;
scratch_end(scratch2);
}
lane_sync();
// rjf: join all namespaces
{
Temp scratch2 = scratch_begin(&scratch.arena, 1);
RDIM_NamespaceChunkList *lanes_namespaces = 0;
if(lane_idx() == 0)
{
lanes_namespaces = push_array(scratch2.arena, RDIM_NamespaceChunkList, lane_count());
}
lane_sync_u64(&lanes_namespaces, 0);
lanes_namespaces[lane_idx()] = lane_namespaces;
lane_sync();
if(lane_idx() == 0)
{
for EachIndex(l_idx, lane_count())
{
rdim_namespace_chunk_list_concat_in_place(all_namespaces, &lanes_namespaces[l_idx]);
}
}
lane_sync();
scratch_end(scratch2);
}
}
lane_sync();
////////////////////////////
//- rjf: convert all UDTs
//
RDIM_UDTChunkList *all_udts = 0;
ProfScope("convert all UDTs")
{
//- rjf: produce UDTs across all lanes
U64 chunk_count = 512;
RDIM_UDTChunkList lane_udts = {0};
Rng1U64 range = lane_range(type_count);
for EachInRange(type_idx, range)
{
RDIM_Type *type = type_from_idx_map[type_idx];
if(type != 0 &&
(type->kind == RDI_TypeKind_Struct ||
type->kind == RDI_TypeKind_Union ||
type->kind == RDI_TypeKind_Class ||
type->kind == RDI_TypeKind_Enum ||
type->kind == RDI_TypeKind_Alias))
{
// rjf: produce UDT
RDIM_UDT *udt = rdim_udt_chunk_list_push(arena, &lane_udts, chunk_count);
type->udt = udt;
udt->self_type = type;
// rjf: idx -> type tag node
D2R_UniqueTagNode *type_tag_node = type_tag_nodes[type_idx];
U64 info_off = type_tag_node->info_off;
U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off);
U64 unit_idx = unit_num > 0 ? unit_num-1 : 0;
// rjf: unpack unit
Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx];
DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx];
// rjf: parse all tags
S64 depth = 0;
for(U64 off = info_off; contains_1u64(unit_info_tag_range, off) && (depth > 0 || off == info_off);)
{
Temp scratch2 = scratch_begin(&scratch.arena, 1);
U64 start_off = off;
// rjf: parse tag
DW2_Tag tag = {0};
U64 tag_parse_off = off;
off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag);
// rjf: unpack tag attributes
DW2_Attrib *name_attrib = &dw2_attrib_nil;
DW2_Attrib *type_attrib = &dw2_attrib_nil;
DW2_Attrib *off_attrib = &dw2_attrib_nil;
DW2_Attrib *val_attrib = &dw2_attrib_nil;
DW2_Attrib *declfile_attrib = &dw2_attrib_nil;
DW2_Attrib *declline_attrib = &dw2_attrib_nil;
DW2_Attrib *declcol_attrib = &dw2_attrib_nil;
for EachNode(n, DW2_AttribNode, tag.attribs.first)
{
switch(n->v.attrib_kind)
{
default:{}break;
#define Case(dst, src) case DW_AttribKind_##src:{dst##_attrib = &n->v;}break
Case(name, Name);
Case(type, Type);
Case(off, DataMemberLocation);
Case(val, ConstValue);
Case(declfile, DeclFile);
Case(declline, DeclLine);
Case(declcol, DeclColumn);
#undef Case
}
}
// rjf: unpack basic attributes
String8 name = name_attrib->val.string;
U64 off = off_attrib->val.u128.u64[0];
U64 val = val_attrib->val.u128.u64[0];
// rjf: unpack type
RDIM_Type *type = 0;
if(type_attrib != &dw2_attrib_nil)
{
// rjf: attrib -> info off / unit idx
U64 type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &type_attrib->val);
U64 type_unit_idx = unit_idx;
if(!contains_1u64(unit_info_tag_range, type_info_off))
{
U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, type_info_off);
type_unit_idx = (new_unit_num > 0 ? new_unit_num-1 : unit_idx);
}
// rjf: (info_off, unit_idx) -> hash
U64 type_hash = 0;
{
D2R_UnitDedupedTagMap *unit_deduped_tag_map = &unit_deduped_tag_maps[type_unit_idx];
U64 info_off_hash = u64_hash_from_str8(str8_struct(&type_info_off));
U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_map->slots_count;
for(D2R_UnitDedupedTagNode *n = unit_deduped_tag_map->slots[info_off_slot_idx]; n != 0; n = n->next)
{
if(n->src_info_off == type_info_off)
{
type_hash = n->dst_hash;
break;
}
}
}
// rjf: hash -> type
{
U64 unique_type_tag_slot_idx = type_hash%unique_tag_slots_count;
for(D2R_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next)
{
if(n->hash == type_hash)
{
type = type_from_idx_map[n->order_idx];
break;
}
}
}
}
// rjf: root-level tag -> collect decl file/line info
if(tag_parse_off == start_off)
{
// TODO(rjf): we need to have gathered the source files before this!!!!!!
// currently, they only come from line info - we need to gather/dedup the
// ones that also come from tags.
}
// rjf: gather children
switch(tag.kind)
{
default:{}break;
case DW_TagKind_Member:
{
RDIM_UDTMember *member = rdim_udt_push_member(arena, &lane_udts, udt);
member->kind = RDI_MemberKind_DataField;
member->name = name;
member->type = type;
member->off = (RDI_U32)off; // TODO(rjf): @u64_to_u32
}break;
case DW_TagKind_Enumerator:
{
RDIM_UDTEnumVal *enum_val = rdim_udt_push_enum_val(arena, &lane_udts, udt);
enum_val->name = name;
enum_val->val = val;
}break;
}
// rjf: tree nav
if(tag.kind == DW_TagKind_Null)
{
depth -= 1;
}
if(tag.has_children)
{
depth += 1;
}
scratch_end(scratch2);
if(off == start_off)
{
break;
}
}
}
}
lane_sync();
//- rjf: combine all lanes
RDIM_UDTChunkList *lanes_udts = 0;
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;
lane_sync();
if(lane_idx() == 0)
{
all_udts = push_array(scratch.arena, RDIM_UDTChunkList, 1);
for EachIndex(l_idx, lane_count())
{
rdim_udt_chunk_list_concat_in_place(all_udts, &lanes_udts[l_idx]);
}
}
lane_sync_u64(&all_udts, 0);
}
////////////////////////////
//- rjf: convert all units
//
RDIM_UnitChunkList *all_units = 0;
RDIM_Unit **unit_from_idx_map = 0;
ProfScope("convert all units")
{
if(lane_idx() == 0)
{
all_units = push_array(scratch.arena, RDIM_UnitChunkList, 1);
unit_from_idx_map = push_array(scratch.arena, RDIM_Unit *, unit_count);
for EachIndex(unit_idx, unit_count)
{
unit_from_idx_map[unit_idx] = rdim_unit_chunk_list_push(arena, all_units, unit_count);
}
}
lane_sync_u64(&all_units, 0);
lane_sync_u64(&unit_from_idx_map, 0);
U64 unit_take_idx_ = 0;
U64 *unit_take_idx_ptr = &unit_take_idx_;
lane_sync_u64(&unit_take_idx_ptr, 0);
for(;;)
{
U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr) - 1;
if(unit_idx >= unit_count)
{
break;
}
//- rjf: unpack unit info
DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx];
Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx];
RDIM_Unit *dst_unit = unit_from_idx_map[unit_idx];
//- rjf: unpack unit's root tag
DW2_Tag *unit_root_tag = &unit_root_tags[unit_idx];
DW2_Attrib *name_attrib = &dw2_attrib_nil;
DW2_Attrib *comp_dir_attrib = &dw2_attrib_nil;
DW2_Attrib *producer_attrib = &dw2_attrib_nil;
DW2_Attrib *lang_attrib = &dw2_attrib_nil;
DW2_Attrib *ranges_attrib = &dw2_attrib_nil;
DW2_Attrib *lopc_attrib = &dw2_attrib_nil;
DW2_Attrib *hipc_attrib = &dw2_attrib_nil;
for EachNode(n, DW2_AttribNode, unit_root_tag->attribs.first)
{
switch(n->v.attrib_kind)
{
default:{}break;
#define Case(dst, src) case DW_AttribKind_##src:{dst##_attrib = &n->v;}break
Case(name, Name);
Case(comp_dir, CompDir);
Case(producer, Producer);
Case(lang, Language);
Case(ranges, Ranges);
Case(lopc, LowPc);
Case(hipc, HighPc);
#undef Case
}
}
//- rjf: unpack attributes
String8 unit_name = name_attrib->val.string;
String8 unit_comp_dir = comp_dir_attrib->val.string;
String8 unit_producer = producer_attrib->val.string;
RDI_Language unit_lang = RDI_Language_NULL;
{
DW_Language dw_lang = lang_attrib->val.u128.u64[0];
switch(dw_lang)
{
default:{}break;
case DW_Language_C89:
case DW_Language_C99:
case DW_Language_C11:
case DW_Language_C:
{
unit_lang = RDI_Language_C;
}break;
case DW_Language_CPlusPlus03:
case DW_Language_CPlusPlus11:
case DW_Language_CPlusPlus14:
case DW_Language_CPlusPlus:
{
unit_lang = RDI_Language_CPlusPlus;
}break;
}
}
//- rjf: get unit's ranges from .debug_aranges parse artifacts, if we have them
RDIM_Rng1U64ChunkList unit_voff_ranges = {0};
if(arange_unit_from_info_off_map_slots_count != 0)
{
U64 unit_info_off = unit_info_ranges->v[unit_idx].min;
U64 hash = u64_hash_from_str8(str8_struct(&unit_info_off));
U64 slot_idx = hash%arange_unit_from_info_off_map_slots_count;
for(D2R_ARangeUnitNode *n = arange_unit_from_info_off_map_slots[slot_idx]; n != 0; n = n->next)
{
if(n->info_off == unit_info_off)
{
unit_voff_ranges = n->ranges[0];
break;
}
}
}
//- rjf: if we have no voff ranges from .debug_aranges, then we need to extract
// this info from the unit root rag instead (via ranges & low-pc/high-pc tags)
if(unit_voff_ranges.total_count == 0)
{
// rjf: gather ranges from a ranges attribute
if(ranges_attrib != &dw2_attrib_nil)
{
Rng1U64List ranges = dw2_rnglist_from_form_val(scratch.arena, unit_parse_ctx, raw, ranges_attrib->val);
for EachNode(n, Rng1U64Node, ranges.first)
{
rdim_rng1u64_chunk_list_push(arena, &unit_voff_ranges, 256, (RDIM_Rng1U64){n->v.min - base_vaddr, n->v.max - base_vaddr});
}
}
// rjf: gather contiguous range from low-pc / high-pc attribute
if(lopc_attrib != &dw2_attrib_nil && hipc_attrib != &dw2_attrib_nil)
{
U64 voff_base = lopc_attrib->val.addr - base_vaddr;
U64 voff_opl = 0;
if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, hipc_attrib->val.kind) & (1<<DW_AttribClass_Address))
{
voff_opl = voff_base + hipc_attrib->val.u128.u64[0];
}
else
{
voff_opl = hipc_attrib->val.addr;
}
rdim_rng1u64_chunk_list_push(arena, &unit_voff_ranges, 256, (RDIM_Rng1U64){voff_base, voff_opl});
}
}
//- rjf: fill top-level unit info
{
dst_unit->unit_name = unit_name;
dst_unit->compiler_name = unit_producer;
// TODO(rjf): dst_unit->source_file = ???;
// TODO(rjf): dst_unit->object_file = ???;
// TODO(rjf): dst_unit->archive_file = ???;
dst_unit->build_path = unit_comp_dir;
dst_unit->language = unit_lang;
dst_unit->line_table = unit_line_tables[unit_idx];
dst_unit->voff_ranges = unit_voff_ranges;
}
}
lane_sync();
}
////////////////////////////
//- rjf: convert all symbols
//
typedef struct D2R_ScopeContainerNode D2R_ScopeContainerNode;
struct D2R_ScopeContainerNode
{
D2R_ScopeContainerNode *next;
U64 info_off;
RDIM_Scope *scope;
};
typedef struct D2R_ScopeContainerMap D2R_ScopeContainerMap;
struct D2R_ScopeContainerMap
{
D2R_ScopeContainerNode **slots;
U64 slots_count;
};
typedef struct D2R_SubUnitWorkArtifacts D2R_SubUnitWorkArtifacts;
struct D2R_SubUnitWorkArtifacts
{
RDIM_SymbolChunkList global_variables;
RDIM_SymbolChunkList thread_variables;
RDIM_SymbolChunkList constants;
RDIM_SymbolChunkList procedures;
RDIM_ScopeChunkList scopes;
RDIM_InlineSiteChunkList inline_sites;
};
D2R_ScopeContainerMap *scope_container_map = 0;
D2R_SubUnitWorkArtifacts *sub_unit_work_artifacts = 0;
ProfScope("convert all symbols")
{
if(lane_idx() == 0)
{
scope_container_map = push_array(scratch.arena, D2R_ScopeContainerMap, 1);
scope_container_map->slots_count = total_tag_count_estimate/8 + 1;
scope_container_map->slots = push_array(scratch.arena, D2R_ScopeContainerNode *, scope_container_map->slots_count);
sub_unit_work_artifacts = push_array(scratch.arena, D2R_SubUnitWorkArtifacts, sub_unit_works_count);
}
lane_sync_u64(&scope_container_map, 0);
lane_sync_u64(&sub_unit_work_artifacts, 0);
U64 work_take_idx_ = 0;
U64 *work_take_idx_ptr = &work_take_idx_;
lane_sync_u64(&work_take_idx_ptr, 0);
for(;;)
{
U64 work_idx = ins_atomic_u64_inc_eval(work_take_idx_ptr) - 1;
if(work_idx >= sub_unit_works_count)
{
break;
}
//- rjf: unpack work
U64 unit_idx = sub_unit_works[work_idx].unit_idx;
Rng1U64 root_tag_idx_range = sub_unit_works[work_idx].root_tag_idx_range;
D2R_SubUnitWorkArtifacts *dst_artifacts = &sub_unit_work_artifacts[work_idx];
//- rjf: unpack unit info
DW2_ParseCtx *unit_parse_ctx = &unit_parse_ctxs[unit_idx];
Rng1U64 unit_info_tag_range = unit_info_tag_ranges[unit_idx];
//- rjf: produce all unit symbols in this work
U64 chunk_count = 256;
for(U64 root_tag_idx = root_tag_idx_range.min; root_tag_idx < root_tag_idx_range.max; root_tag_idx += 1)
{
typedef struct D2R_ParentNode D2R_ParentNode;
struct D2R_ParentNode
{
D2R_ParentNode *next;
DW_TagKind tag_kind;
U64 info_off;
RDIM_Scope *scope;
RDIM_Type *container_type;
RDIM_Namespace *container_namespace;
RDIM_LocationCaseList framebase_location_cases;
};
D2R_ParentNode *top_parent = 0;
D2R_ParentNode *free_parent = 0;
U64 root_tag_start_off = unit_info_root_tag_offs[unit_idx].v[root_tag_idx];
S64 depth = 1;
for(U64 off = root_tag_start_off; off < unit_info_tag_range.max && (depth > 1 || off == root_tag_start_off);)
{
Temp scratch2 = scratch_begin(&scratch.arena, 1);
U64 start_off = off;
////////////////////////
//- rjf: parse tag
//
DW2_Tag tag = {0};
off += dw2_read_tag(scratch2.arena, raw, unit_parse_ctx, raw->sec[DW_SectionKind_Info].data, off, &tag);
////////////////////////
//- rjf: gather attributes from tag
//
DW2_Attrib *name_attrib = &dw2_attrib_nil;
DW2_Attrib *linkname_attrib = &dw2_attrib_nil;
DW2_Attrib *type_attrib = &dw2_attrib_nil;
DW2_Attrib *inline_attrib = &dw2_attrib_nil;
DW2_Attrib *ranges_attrib = &dw2_attrib_nil;
DW2_Attrib *lopc_attrib = &dw2_attrib_nil;
DW2_Attrib *hipc_attrib = &dw2_attrib_nil;
DW2_Attrib *constval_attrib = &dw2_attrib_nil;
DW2_Attrib *location_attrib = &dw2_attrib_nil;
DW2_Attrib *framebase_attrib = &dw2_attrib_nil;
DW2_Attrib *external_attrib = &dw2_attrib_nil;
DW2_Attrib *decl_attrib = &dw2_attrib_nil;
for EachNode(n, DW2_AttribNode, tag.attribs.first)
{
switch(n->v.attrib_kind)
{
default:{}break;
#define Case(dst, src) case DW_AttribKind_##src:{dst##_attrib = &n->v;}break
Case(name, Name);
Case(linkname, LinkageName);
Case(type, Type);
Case(inline, Inline);
Case(ranges, Ranges);
Case(lopc, LowPc);
Case(hipc, HighPc);
Case(constval, ConstValue);
Case(location, Location);
Case(framebase, FrameBase);
Case(external, External);
Case(decl, Declaration);
#undef Case
}
}
////////////////////////
//- rjf: unpack basic attributes
//
String8 name = name_attrib->val.string;
String8 link_name = linkname_attrib->val.string;
DW_InlKind inl_kind = (DW_InlKind)inline_attrib->val.u128.u64[0];
B32 is_external = (external_attrib != &dw2_attrib_nil);
B32 is_decl = (decl_attrib != &dw2_attrib_nil);
////////////////////////
//- rjf: unpack ranges
//
RDIM_Rng1U64List ranges = {0};
{
if(ranges_attrib != &dw2_attrib_nil)
{
Temp temp = temp_begin(scratch2.arena);
Rng1U64List tag_ranges = dw2_rnglist_from_form_val(temp.arena, unit_parse_ctx, raw, ranges_attrib->val);
for EachNode(n, Rng1U64Node, tag_ranges.first)
{
rdim_rng1u64_list_push(arena, &ranges, (RDIM_Rng1U64){.min = n->v.min, .max = n->v.max});
}
temp_end(temp);
}
if(lopc_attrib != &dw2_attrib_nil && hipc_attrib != &dw2_attrib_nil)
{
U64 voff_base = lopc_attrib->val.addr - base_vaddr;
U64 voff_opl = 0;
if(dw_attrib_class_from_form_kind(unit_parse_ctx->version, unit_parse_ctx->exts, hipc_attrib->val.kind) & (1<<DW_AttribClass_Address))
{
voff_opl = voff_base + hipc_attrib->val.u128.u64[0];
}
else
{
voff_opl = hipc_attrib->val.addr;
}
rdim_rng1u64_list_push(arena, &ranges, (RDIM_Rng1U64){voff_base, voff_opl});
}
}
////////////////////////
//- rjf: unpack type
//
RDIM_Type *type = 0;
if(type_attrib != &dw2_attrib_nil || tag.kind == DW_TagKind_SubProgram)
{
// rjf: functions need to get their type info from themselves; the type attrib
// only references the return type. so, in that case, we'll use the procedure's
// own info offset / unit index, rather than the type attribute's.
U64 type_info_off = start_off;
U64 type_unit_idx = unit_idx;
// rjf: attrib -> (info_off, unit_idx)
if(tag.kind != DW_TagKind_SubProgram)
{
type_info_off = dw2_reference_info_off_from_form_val(unit_parse_ctx, &type_attrib->val);
type_unit_idx = unit_idx;
if(!contains_1u64(unit_info_tag_range, type_info_off))
{
U64 new_unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, type_info_off);
type_unit_idx = (new_unit_num > 0 ? new_unit_num-1 : unit_idx);
}
}
// rjf: (info_off, unit_idx) -> hash
U64 type_hash = 0;
{
D2R_UnitDedupedTagMap *unit_deduped_tag_map = &unit_deduped_tag_maps[type_unit_idx];
U64 info_off_hash = u64_hash_from_str8(str8_struct(&type_info_off));
U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_map->slots_count;
for(D2R_UnitDedupedTagNode *n = unit_deduped_tag_map->slots[info_off_slot_idx]; n != 0; n = n->next)
{
if(n->src_info_off == type_info_off)
{
type_hash = n->dst_hash;
break;
}
}
}
// rjf: hash -> type
{
U64 unique_type_tag_slot_idx = type_hash%unique_tag_slots_count;
for(D2R_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next)
{
if(n->hash == type_hash)
{
type = type_from_idx_map[n->order_idx];
break;
}
}
}
}
////////////////////////
//- rjf: unpack location info
//
RDIM_LocationCaseList location_cases = {0};
RDIM_LocationCaseList framebase_location_cases = {0};
B32 location_is_tls_dependent = 0;
B32 framebase_location_is_tls_dependent = 0;
{
struct
{
DW2_Attrib *attrib;
RDIM_LocationCaseList *dst_locations;
B32 *dst_is_tls_dependent;
}
tasks[] =
{
{location_attrib, &location_cases, &location_is_tls_dependent},
{framebase_attrib, &framebase_location_cases, &framebase_location_is_tls_dependent},
};
for EachElement(task_idx, tasks)
{
if(tasks[task_idx].attrib == &dw2_attrib_nil)
{
continue;
}
DW2_Attrib *attrib = tasks[task_idx].attrib;
RDIM_LocationCaseList *dst_locations = tasks[task_idx].dst_locations;
B32 *dst_is_tls_dependent = tasks[task_idx].dst_is_tls_dependent;
//////////////////////
//- rjf: gather DWARF location exprs
//
DW2_LocList locs = {0};
switch(attrib->val.kind)
{
default:{}break;
case DW_FormKind_ExprLoc:
{
U64 expr_info_off = attrib->val.u128.u64[0];
U64 expr_info_size = attrib->val.u128.u64[1];
String8 expr = str8_substr(raw->sec[DW_SectionKind_Info].data, r1u64(expr_info_off, expr_info_off+expr_info_size));
DW2_LocNode *n = push_array(scratch2.arena, DW2_LocNode, 1);
n->v.expr = expr;
n->v.range = r1u64(0, max_U64);
SLLQueuePush(locs.first, locs.last, n);
locs.count += 1;
}break;
case DW_FormKind_LocListx: // NOTE(rjf): dwarf5+, loclistx -> location list offset table
case DW_FormKind_SecOffset: // NOTE(rjf): pre-dwarf5, location section offset -> a location list
{
locs = dw2_loclist_from_form_val(scratch2.arena, unit_parse_ctx, raw, attrib->val);
}break;
}
//////////////////////
//- rjf: convert DWARF location exprs -> RDIM locations
//
// we do this for each possible frame base location case - locations in DWARF
// refer to the frame base via a special op. in our case, we want to bake that
// into the location directly to reduce extra context for evaluation.
//
// if we find that a location doesn't rely on the frame base at all, we just
// skip the rest.
//
B32 is_tls_dependent = 0;
for EachNode(n, DW2_LocNode, locs.first)
{
//- rjf: unpack location
Rng1U64 range = n->v.range;
String8 expr = n->v.expr;
//- rjf: iterate each frame base location case, or once if there are none,
// and convert the location in that context
RDIM_LocationCase nil_framebase_loc_case = {0, {0}, {0, max_U64}};
for(RDIM_LocationCase *framebase_loc_n = top_parent ? top_parent->framebase_location_cases.first : &nil_framebase_loc_case;
framebase_loc_n != 0;
framebase_loc_n = framebase_loc_n->next)
{
//- rjf: unpack framebase location
RDIM_Location framebase_loc = framebase_loc_n->location;
RDIM_Rng1U64 framebase_voff_range = framebase_loc_n->voff_range;
//- rjf: set up type stack for type-evaluating bytecode
typedef struct D2R_ExprVal D2R_ExprVal;
struct D2R_ExprVal
{
RDI_TypeKind type_kind;
B32 is_addr;
};
typedef struct D2R_ExprValNode D2R_ExprValNode;
struct D2R_ExprValNode
{
D2R_ExprValNode *next;
D2R_ExprVal v;
};
D2R_ExprValNode *val_stack_top = 0;
D2R_ExprValNode *free_val = 0;
//- rjf: process DWARF bytecode; produce RDI bytecode + mini-type-info for the expression's result
typedef struct JumpOpNode JumpOpNode;
struct JumpOpNode
{
JumpOpNode *next;
RDIM_EvalBytecodeOp *op;
S64 inst_delta;
};
JumpOpNode *first_jump_op = 0;
JumpOpNode *last_jump_op = 0;
RDIM_EvalBytecode dst_bytecode = {0};
B32 dst_bytecode_is_good = 1;
B32 bytecode_is_framebase_dependent = 0;
for(U64 expr_off = 0; expr_off < expr.size;)
{
U64 start_expr_off = expr_off;
//- rjf: read next opcode
DW_ExprOp opcode = 0;
expr_off += str8_deserial_read_struct(expr, expr_off, &opcode);
//- rjf: unpack opcode
DW_ExprOpInfo *opcode_info = dw_info_from_expr_op(unit_parse_ctx->version, unit_parse_ctx->exts, opcode);
U64 operands_count = opcode_info->operand_count;
DW_ExprOperandKind operands_kinds[2] = {opcode_info->operand_kinds[0], opcode_info->operand_kinds[1]};
U64 push_count = opcode_info->push_count;
U64 pop_count = opcode_info->pop_count;
//- rjf: read op operands
U64 operand_u64s[2] = {0};
S64 operand_s64s[2] = {0};
String8 operand_str8s[2] = {0};
for EachIndex(operand_idx, operands_count)
{
switch(operands_kinds[operand_idx])
{
case DW_ExprOperandKind_Null:
default:{}break;
case DW_ExprOperandKind_U8: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 1, 1);}break;
case DW_ExprOperandKind_U16: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 2, 2);}break;
case DW_ExprOperandKind_U32: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 4, 4);}break;
case DW_ExprOperandKind_U64: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], 8, 8);}break;
case DW_ExprOperandKind_S8: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 1, 1);}break;
case DW_ExprOperandKind_S16: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 2, 2);}break;
case DW_ExprOperandKind_S32: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 4, 4);}break;
case DW_ExprOperandKind_S64: {expr_off += str8_deserial_read(expr, expr_off, &operand_s64s[operand_idx], 8, 8);}break;
case DW_ExprOperandKind_ULEB128: {expr_off += str8_deserial_read_uleb128(expr, expr_off, &operand_u64s[operand_idx]);} break;
case DW_ExprOperandKind_SLEB128: {expr_off += str8_deserial_read_sleb128(expr, expr_off, &operand_s64s[operand_idx]);} break;
case DW_ExprOperandKind_Addr: {expr_off += str8_deserial_read(expr, expr_off, &operand_u64s[operand_idx], unit_parse_ctx->addr_size, unit_parse_ctx->addr_size);}break;
case DW_ExprOperandKind_DwarfUInt: {expr_off += dw2_read_fmt_u64(expr, expr_off, unit_parse_ctx->format, &operand_u64s[operand_idx]);}break;
case DW_ExprOperandKind_Block:
{
U8 block_size = 0;
expr_off += str8_deserial_read_struct(expr, expr_off, &block_size);
operand_str8s[operand_idx] = str8_substr(expr, r1u64(expr_off, expr_off+block_size));
expr_off += block_size;
}break;
}
}
//- rjf: pop stack values
D2R_ExprVal popped_vals[2] = {0};
{
pop_count = Min(pop_count, ArrayCount(popped_vals));
for EachIndex(pop_idx, pop_count)
{
if(val_stack_top != 0)
{
D2R_ExprValNode *popped = val_stack_top;
popped_vals[pop_idx] = popped->v;
SLLStackPop(val_stack_top);
SLLStackPush(free_val, popped);
}
}
}
//- rjf: exec op (produce RDI bytecode, + manipulate value stack)
D2R_ExprVal push_vals[2] = {0};
U64 regcode_dw = 0;
S64 regval_off = 0;
B32 regread_is_addr = 0;
U64 target_pick_val_idx = 0;
RDI_EvalOp rdi_eval_op = 0;
U64 memread_size = 0;
switch(opcode)
{
default:{}break;
//- rjf: small opcode-embedded unsigned literals
case DW_ExprOp_Lit0: case DW_ExprOp_Lit1: case DW_ExprOp_Lit2:
case DW_ExprOp_Lit3: case DW_ExprOp_Lit4: case DW_ExprOp_Lit5:
case DW_ExprOp_Lit6: case DW_ExprOp_Lit7: case DW_ExprOp_Lit8:
case DW_ExprOp_Lit9: case DW_ExprOp_Lit10: case DW_ExprOp_Lit11:
case DW_ExprOp_Lit12: case DW_ExprOp_Lit13: case DW_ExprOp_Lit14:
case DW_ExprOp_Lit15: case DW_ExprOp_Lit16: case DW_ExprOp_Lit17:
case DW_ExprOp_Lit18: case DW_ExprOp_Lit19: case DW_ExprOp_Lit20:
case DW_ExprOp_Lit21: case DW_ExprOp_Lit22: case DW_ExprOp_Lit23:
case DW_ExprOp_Lit24: case DW_ExprOp_Lit25: case DW_ExprOp_Lit26:
case DW_ExprOp_Lit27: case DW_ExprOp_Lit28: case DW_ExprOp_Lit29:
case DW_ExprOp_Lit30: case DW_ExprOp_Lit31:
{
U64 lit_val = (U64)(opcode - DW_ExprOp_Lit0);
rdim_bytecode_push_uconst(arena, &dst_bytecode, lit_val);
}break;
//- rjf: small unsigned literals
case DW_ExprOp_Const1U: push_vals[0].type_kind = RDI_TypeKind_U8; goto const_u;
case DW_ExprOp_Const2U: push_vals[0].type_kind = RDI_TypeKind_U16; goto const_u;
case DW_ExprOp_Const4U: push_vals[0].type_kind = RDI_TypeKind_U32; goto const_u;
case DW_ExprOp_Const8U: push_vals[0].type_kind = RDI_TypeKind_U64; goto const_u;
case DW_ExprOp_ConstU: push_vals[0].type_kind = RDI_TypeKind_U64; goto const_u;
const_u:;
{
rdim_bytecode_push_uconst(arena, &dst_bytecode, operand_u64s[0]);
}break;
//- rjf: small signed literals
case DW_ExprOp_Const1S: push_vals[0].type_kind = RDI_TypeKind_S8; goto const_s;
case DW_ExprOp_Const2S: push_vals[0].type_kind = RDI_TypeKind_S16; goto const_s;
case DW_ExprOp_Const4S: push_vals[0].type_kind = RDI_TypeKind_S32; goto const_s;
case DW_ExprOp_Const8S: push_vals[0].type_kind = RDI_TypeKind_S64; goto const_s;
case DW_ExprOp_ConstS : push_vals[0].type_kind = RDI_TypeKind_S64; goto const_s;
const_s:;
{
rdim_bytecode_push_sconst(arena, &dst_bytecode, operand_s64s[0]);
}break;
//- rjf: address (module offsets)
case DW_ExprOp_Addr:
{
U64 voff = operand_u64s[0] - base_vaddr;
rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_ModuleOff, voff);
push_vals[0].type_kind = RDI_TypeKind_U64;
push_vals[0].is_addr = 1;
}break;
case DW_ExprOp_Addrx:
if(unit_parse_ctx->addr_table != 0)
{
U64 addr_idx = operand_u64s[0];
U64 addr = 0;
if(dw2_try_offset_from_table_idx(unit_parse_ctx->addr_table, addr_idx, &addr))
{
U64 voff = (addr > base_vaddr) ? (addr - base_vaddr) : 0;
rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_ModuleOff, voff);
push_vals[0].type_kind = RDI_TypeKind_U64;
push_vals[0].is_addr = 1;
}
}break;
//- rjf: register reads
case DW_ExprOp_Reg0: case DW_ExprOp_Reg1: case DW_ExprOp_Reg2:
case DW_ExprOp_Reg3: case DW_ExprOp_Reg4: case DW_ExprOp_Reg5:
case DW_ExprOp_Reg6: case DW_ExprOp_Reg7: case DW_ExprOp_Reg8:
case DW_ExprOp_Reg9: case DW_ExprOp_Reg10: case DW_ExprOp_Reg11:
case DW_ExprOp_Reg12: case DW_ExprOp_Reg13: case DW_ExprOp_Reg14:
case DW_ExprOp_Reg15: case DW_ExprOp_Reg16: case DW_ExprOp_Reg17:
case DW_ExprOp_Reg18: case DW_ExprOp_Reg19: case DW_ExprOp_Reg20:
case DW_ExprOp_Reg21: case DW_ExprOp_Reg22: case DW_ExprOp_Reg23:
case DW_ExprOp_Reg24: case DW_ExprOp_Reg25: case DW_ExprOp_Reg26:
case DW_ExprOp_Reg27: case DW_ExprOp_Reg28: case DW_ExprOp_Reg29:
case DW_ExprOp_Reg30: case DW_ExprOp_Reg31:
{
regcode_dw = (U64)(opcode - DW_ExprOp_Reg0);
}goto reg_read;
case DW_ExprOp_RegX:
{
regcode_dw = operand_u64s[0];
}goto reg_read;
case DW_ExprOp_BReg0: case DW_ExprOp_BReg1: case DW_ExprOp_BReg2:
case DW_ExprOp_BReg3: case DW_ExprOp_BReg4: case DW_ExprOp_BReg5:
case DW_ExprOp_BReg6: case DW_ExprOp_BReg7: case DW_ExprOp_BReg8:
case DW_ExprOp_BReg9: case DW_ExprOp_BReg10: case DW_ExprOp_BReg11:
case DW_ExprOp_BReg12: case DW_ExprOp_BReg13: case DW_ExprOp_BReg14:
case DW_ExprOp_BReg15: case DW_ExprOp_BReg16: case DW_ExprOp_BReg17:
case DW_ExprOp_BReg18: case DW_ExprOp_BReg19: case DW_ExprOp_BReg20:
case DW_ExprOp_BReg21: case DW_ExprOp_BReg22: case DW_ExprOp_BReg23:
case DW_ExprOp_BReg24: case DW_ExprOp_BReg25: case DW_ExprOp_BReg26:
case DW_ExprOp_BReg27: case DW_ExprOp_BReg28: case DW_ExprOp_BReg29:
case DW_ExprOp_BReg30: case DW_ExprOp_BReg31:
{
regcode_dw = (U64)(opcode - DW_ExprOp_BReg0);
regval_off = operand_s64s[1];
regread_is_addr = 1;
}goto reg_read;
case DW_ExprOp_BRegX:
{
regcode_dw = operand_u64s[0];
regval_off = operand_s64s[1];
regread_is_addr = 1;
}goto reg_read;
reg_read:;
{
// rjf: DWARF regcode -> internal regcode
ARCH_RegCode regcode = arch_reg_code_from_dw(arch, regcode_dw);
// rjf: internal regcode -> RDI, offset, size
RDI_RegCode regcode_rdi = 0;
U64 reg_off = 0;
U64 reg_size = 0;
{
Rng1U16 reg_rng = arch_info->reg_code_rng_table[regcode];
reg_size = dim_1u16(reg_rng);
regcode_rdi = arch_rdi_from_reg_code_table_from_arch(arch)[regcode];
}
// rjf: push op
rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_RegRead, RDI_EncodeRegReadParam(regcode_rdi, reg_size, reg_off));
// rjf: push add to value offset, if needed
if(regval_off != 0)
{
rdim_bytecode_push_sconst(arena, &dst_bytecode, regval_off);
rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Add, RDI_EvalTypeGroup_S);
}
// rjf: choose if we're doing a float op
B32 is_float_op = 0;
if(arch == Arch_x64 &&
(regcode_rdi == RDI_RegCodeX64_st0 ||
regcode_rdi == RDI_RegCodeX64_st1 ||
regcode_rdi == RDI_RegCodeX64_st2 ||
regcode_rdi == RDI_RegCodeX64_st3 ||
regcode_rdi == RDI_RegCodeX64_st4 ||
regcode_rdi == RDI_RegCodeX64_st5 ||
regcode_rdi == RDI_RegCodeX64_st6 ||
regcode_rdi == RDI_RegCodeX64_st7))
{
is_float_op = 1;
}
// rjf: set up push value
push_vals[0].is_addr = regread_is_addr;
if(is_float_op)
{
switch(reg_size)
{
default:{}break;
case 2: {push_vals[0].type_kind = RDI_TypeKind_F16;}break;
case 4: {push_vals[0].type_kind = RDI_TypeKind_F32;}break;
case 6: {push_vals[0].type_kind = RDI_TypeKind_F48;}break;
case 8: {push_vals[0].type_kind = RDI_TypeKind_F64;}break;
case 10:{push_vals[0].type_kind = RDI_TypeKind_F80;}break;
case 12:{push_vals[0].type_kind = RDI_TypeKind_F96;}break;
case 16:{push_vals[0].type_kind = RDI_TypeKind_F128;}break;
}
}
else
{
switch(reg_size)
{
default:{}break;
case 1:{push_vals[0].type_kind = RDI_TypeKind_U8;}break;
case 2:{push_vals[0].type_kind = RDI_TypeKind_U16;}break;
case 4:{push_vals[0].type_kind = RDI_TypeKind_U32;}break;
case 8:{push_vals[0].type_kind = RDI_TypeKind_U64;}break;
}
}
}break;
//- rjf: implicit values
case DW_ExprOp_ImplicitValue:
{
if(operand_str8s[0].size <= sizeof(U64))
{
U64 implicit_value = 0;
MemoryCopy(&implicit_value, operand_str8s[0].str, Min(sizeof(U64), operand_str8s[0].size));
rdim_bytecode_push_uconst(arena, &dst_bytecode, implicit_value);
switch(operand_str8s[0].size)
{
default:{}break;
case 1:{push_vals[0].type_kind = RDI_TypeKind_U8;}break;
case 2:{push_vals[0].type_kind = RDI_TypeKind_U16;}break;
case 4:{push_vals[0].type_kind = RDI_TypeKind_U32;}break;
case 8:{push_vals[0].type_kind = RDI_TypeKind_U64;}break;
}
}
else
{
log_infof("[.debug_info@0x%I64x] Implicit value DWARF expression operation (DW_ExprOp_ImplicitValue) with block size >8 (%I64u) found. This is not currently supported.\n", start_off, operand_str8s[0].size);
}
}break;
//- rjf: pieces
case DW_ExprOp_Piece:
{
rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_PartialValue, operand_u64s[0]);
}break;
case DW_ExprOp_BitPiece:
{
U64 size = operand_u64s[0];
U64 off = operand_u64s[1];
U64 partial_value = ((size<<32)|(off));
rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_PartialValueBit, partial_value);
}break;
//- rjf: stack ops
case DW_ExprOp_Over: {target_pick_val_idx = 1;}goto pick;
case DW_ExprOp_Pick: {target_pick_val_idx = operand_u64s[0];}goto pick;
case DW_ExprOp_Dup: {target_pick_val_idx = 0;}goto pick;
pick:;
{
// rjf: pick value from stack
D2R_ExprVal picked_val = {0};
{
U64 idx = 0;
for(D2R_ExprValNode *n = val_stack_top; n != 0; n = n->next)
{
if(idx == target_pick_val_idx)
{
picked_val = n->v;
break;
}
}
}
// rjf: push opcode
rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Pick, target_pick_val_idx);
// rjf: push val
push_vals[0] = picked_val;
}break;
case DW_ExprOp_Swap:
{
push_vals[0] = popped_vals[1];
push_vals[1] = popped_vals[0];
rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Swap, 0);
}break;
case DW_ExprOp_Drop:
{
rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Pop, 0);
}break;
//- rjf: jumps
case DW_ExprOp_Skip: rdi_eval_op = RDI_EvalOp_Skip; goto jumps;
case DW_ExprOp_Bra: rdi_eval_op = RDI_EvalOp_Cond; goto jumps;
jumps:;
{
// rjf: jump delta in bytes (how the instruction is encoded) -> jump delta in instructions
S64 jump_delta_bytes = operand_s64s[0];
S64 jump_delta_insts = 0;
{
// TODO(rjf): expr op jumps bytes -> inst counts
}
// rjf: push incomplete op (we need to resolve inst delta -> byte delta later, once we have the full bytecode stream)
RDIM_EvalBytecodeOp *op = rdim_bytecode_push_op(arena, &dst_bytecode, rdi_eval_op, 0);
// rjf: gather op
JumpOpNode *n = push_array(scratch2.arena, JumpOpNode, 1);
SLLQueuePush(first_jump_op, last_jump_op, n);
n->op = op;
n->inst_delta = jump_delta_insts;
}break;
//- rjf: containing procedure frame offsets
case DW_ExprOp_FBReg:
{
bytecode_is_framebase_dependent = 1;
for EachNode(n, RDIM_EvalBytecodeOp, framebase_loc.bytecode.first_op)
{
rdim_bytecode_push_op(arena, &dst_bytecode, n->op, n->p);
}
rdim_bytecode_push_sconst(arena, &dst_bytecode, operand_s64s[0]);
rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Add, RDI_EvalTypeGroup_S);
push_vals[0].type_kind = RDI_TypeKind_U64;
push_vals[0].is_addr = 1;
}break;
//- rjf: memory reads
case DW_ExprOp_Deref: memread_size = unit_parse_ctx->addr_size; goto deref;
case DW_ExprOp_DerefSize: memread_size = operand_u64s[0]; goto deref;
deref:;
{
rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_MemRead, memread_size);
push_vals[0].is_addr = 1;
push_vals[0].type_kind = RDI_TypeKind_U64;
}break;
//- rjf: TLS offsets
case DW_ExprOp_FormTlsAddress:
case DW_GNU_ExprOp_PushTlsAddress:
{
rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_TLSOff, 0);
rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Add, RDI_EvalTypeGroup_U);
push_vals[0].is_addr = 1;
push_vals[0].type_kind = RDI_TypeKind_U64;
is_tls_dependent = 1;
}break;
//- rjf: call site value
case DW_ExprOp_EntryValue:
case DW_GNU_ExprOp_EntryValue:
{
// TODO(rjf): expr op entry value ops
}break;
//- rjf: fixed adds
case DW_ExprOp_PlusUConst:
{
rdim_bytecode_push_uconst(arena, &dst_bytecode, operand_u64s[0]);
rdim_bytecode_push_op(arena, &dst_bytecode, RDI_EvalOp_Add, RDI_EvalTypeGroup_U);
push_vals[0].type_kind = RDI_TypeKind_U64; // TODO(rjf): do we need to adjust this based on popped value at all, even?
}break;
//- rjf: arithmetic ops
case DW_ExprOp_Eq: {rdi_eval_op = RDI_EvalOp_EqEq;}goto arithmetic_op;
case DW_ExprOp_Ge: {rdi_eval_op = RDI_EvalOp_GrEq;}goto arithmetic_op;
case DW_ExprOp_Gt: {rdi_eval_op = RDI_EvalOp_Grtr;}goto arithmetic_op;
case DW_ExprOp_Le: {rdi_eval_op = RDI_EvalOp_LsEq;}goto arithmetic_op;
case DW_ExprOp_Lt: {rdi_eval_op = RDI_EvalOp_Less;}goto arithmetic_op;
case DW_ExprOp_Ne: {rdi_eval_op = RDI_EvalOp_Neg;}goto arithmetic_op;
case DW_ExprOp_Div: {rdi_eval_op = RDI_EvalOp_Div;}goto arithmetic_op;
case DW_ExprOp_Minus: {rdi_eval_op = RDI_EvalOp_Sub;}goto arithmetic_op;
case DW_ExprOp_Mul: {rdi_eval_op = RDI_EvalOp_Mul;}goto arithmetic_op;
case DW_ExprOp_Plus: {rdi_eval_op = RDI_EvalOp_Add;}goto arithmetic_op;
case DW_ExprOp_Xor: {rdi_eval_op = RDI_EvalOp_BitXor;}goto arithmetic_op;
case DW_ExprOp_And: {rdi_eval_op = RDI_EvalOp_BitAnd;}goto arithmetic_op;
case DW_ExprOp_Or: {rdi_eval_op = RDI_EvalOp_BitOr;}goto arithmetic_op;
case DW_ExprOp_Shl: {rdi_eval_op = RDI_EvalOp_LShift;}goto arithmetic_op;
case DW_ExprOp_Shr: {rdi_eval_op = RDI_EvalOp_RShift;}goto arithmetic_op;
case DW_ExprOp_Shra: {rdi_eval_op = RDI_EvalOp_RShift;}goto arithmetic_op;
case DW_ExprOp_Mod: {rdi_eval_op = RDI_EvalOp_Mod;}goto arithmetic_op;
case DW_ExprOp_Abs: {rdi_eval_op = RDI_EvalOp_Abs;}goto arithmetic_op;
case DW_ExprOp_Neg: {rdi_eval_op = RDI_EvalOp_Neg;}goto arithmetic_op;
case DW_ExprOp_Not: {rdi_eval_op = RDI_EvalOp_LogNot;}goto arithmetic_op;
arithmetic_op:;
{
// TODO(rjf): eval arithmetic conversions etc.
rdim_bytecode_push_op(arena, &dst_bytecode, rdi_eval_op, RDI_EvalTypeGroup_U);
}break;
//- rjf: currently unsupported
case DW_ExprOp_XDeref:
{
dst_bytecode_is_good = 0;
log_infof("[.debug_info@0x%I64x] DWARF expression XDeref operation encountered, implying multiple address spaces; this is not supported for location info.\n", start_off);
}break;
case DW_ExprOp_XDerefSize:
{
dst_bytecode_is_good = 0;
log_infof("[.debug_info@0x%I64x] DWARF expression XDerefSize operation encountered, implying multiple address spaces; this is not supported for location info.\n", start_off);
}break;
case DW_ExprOp_Call2:
case DW_ExprOp_Call4:
case DW_ExprOp_CallRef:
{
dst_bytecode_is_good = 0;
log_infof("[.debug_info@0x%I64x] DWARF expression call operation encountered. This is not supported for location info.\n", start_off);
}break;
case DW_ExprOp_ImplicitPointer:
case DW_GNU_ExprOp_ImplicitPointer:
{
dst_bytecode_is_good = 0;
log_infof("[.debug_info@0x%I64x] DWARF expression implicit pointer operation encountered. This is not supported for location info.\n", start_off);
}break;
case DW_GNU_ExprOp_ParameterRef:
{
dst_bytecode_is_good = 0;
log_infof("[.debug_info@0x%I64x] DWARF expression GNU_ParameterRef operation encountered. This is not supported for location info.\n", start_off);
}break;
case DW_ExprOp_DerefType:
case DW_GNU_ExprOp_DerefType:
{
dst_bytecode_is_good = 0;
log_infof("[.debug_info@0x%I64x] DWARF expression DerefType operation encountered. This is not supported for location info.\n", start_off);
}break;
case DW_ExprOp_ConstType:
case DW_GNU_ExprOp_ConstType:
{
dst_bytecode_is_good = 0;
log_infof("[.debug_info@0x%I64x] DWARF expression ConstType operation encountered. This is not supported for location info.\n", start_off);
}break;
case DW_ExprOp_RegvalType:
{
dst_bytecode_is_good = 0;
log_infof("[.debug_info@0x%I64x] DWARF expression RegvalType operation encountered. This is not supported for location info.\n", start_off);
}break;
case DW_ExprOp_PushObjectAddress:
{
dst_bytecode_is_good = 0;
log_infof("[.debug_info@0x%I64x] DWARF expression PushObjectAddress operation encountered. This is not supported for location info.\n", start_off);
}break;
case DW_ExprOp_Rot:
{
dst_bytecode_is_good = 0;
log_infof("[.debug_info@0x%I64x] DWARF expression Rot (rotate) operation encountered. This is not supported for location info.\n", start_off);
}break;
case DW_GNU_ExprOp_UnInit:
{
dst_bytecode_is_good = 0;
log_infof("[.debug_info@0x%I64x] DWARF expression GNU_UnInit operation encountered. This is not supported for location info.\n", start_off);
// TODO: flag value as unitialized; this must be last opcode; possible to use with DW_ExprOp_Piece;
} break;
}
//- rjf: push values to stack
{
push_count = Min(push_count, ArrayCount(push_vals));
for EachIndex(push_idx, push_count)
{
D2R_ExprValNode *n = free_val;
if(n != 0)
{
SLLStackPop(free_val);
}
else
{
n = push_array(scratch2.arena, D2R_ExprValNode, 1);
}
n->v = push_vals[push_idx];
SLLStackPush(val_stack_top, n);
}
}
if(expr_off == start_expr_off)
{
break;
}
}
//- rjf: apply RDI byte offsets to jump ops
{
// TODO(rjf): apply RDI byte offsets to jump ops
}
//- rjf: map bytecode -> RDIM_Location - we may want to simplify
RDIM_Location loc = {0};
{
loc.kind = val_stack_top && val_stack_top->v.is_addr ? RDI_LocationKind_AddrBytecodeStream : RDI_LocationKind_ValBytecodeStream;
loc.bytecode = dst_bytecode;
// rjf: gather first few bytecodes for pattern matching
RDI_EvalOp beginning_ops[5] = {0};
RDIM_EvalBytecodeOp *beginning_nodes[5] = {0};
{
U64 idx = 0;
for EachNode(n, RDIM_EvalBytecodeOp, dst_bytecode.first_op)
{
if(idx >= ArrayCount(beginning_ops))
{
break;
}
beginning_ops[idx] = n->op;
beginning_nodes[idx] = n;
idx += 1;
}
}
// rjf: match val registers
if(loc.kind == RDI_LocationKind_ValBytecodeStream &&
beginning_ops[0] == RDI_EvalOp_RegRead &&
beginning_ops[1] == RDI_EvalOp_Stop)
{
U8 rdi_reg_code = (beginning_nodes[0]->p&0x0000FF)>>0;
U8 byte_size = (beginning_nodes[0]->p&0x00FF00)>>8;
U8 byte_off = (beginning_nodes[0]->p&0xFF0000)>>16;
if(byte_size == unit_parse_ctx->addr_size &&
byte_off == 0)
{
loc.kind = RDI_LocationKind_ValReg;
loc.reg_code = rdi_reg_code;
}
}
// rjf: match simple register offsets
if(loc.kind == RDI_LocationKind_AddrBytecodeStream &&
beginning_ops[0] == RDI_EvalOp_RegRead &&
(beginning_ops[1] == RDI_EvalOp_ConstU8 ||
beginning_ops[1] == RDI_EvalOp_ConstU16) &&
beginning_ops[2] == RDI_EvalOp_TruncSigned &&
beginning_ops[3] == RDI_EvalOp_Add &&
beginning_ops[4] == RDI_EvalOp_Stop)
{
U8 rdi_reg_code = (beginning_nodes[0]->p&0x0000FF)>>0;
U8 byte_size = (beginning_nodes[0]->p&0x00FF00)>>8;
U8 byte_off = (beginning_nodes[0]->p&0xFF0000)>>16;
if(byte_off == 0 && byte_size == unit_parse_ctx->addr_size)
{
loc.kind = RDI_LocationKind_AddrRegPlusOff;
loc.reg_code = rdi_reg_code;
loc.offset = beginning_nodes[1]->p;
}
}
// rjf: match simple module offsets
if(beginning_ops[0] == RDI_EvalOp_ModuleOff && beginning_ops[1] == RDI_EvalOp_Stop)
{
U64 voff = beginning_nodes[0]->p;
loc.kind = RDI_LocationKind_ModuleOff;
loc.offset = voff;
}
else if(((beginning_ops[0] == RDI_EvalOp_ModuleOff &&
(beginning_ops[1] == RDI_EvalOp_ConstU8 ||
beginning_ops[1] == RDI_EvalOp_ConstU16 ||
beginning_ops[1] == RDI_EvalOp_ConstU32 ||
beginning_ops[1] == RDI_EvalOp_ConstU64)) ||
(beginning_ops[1] == RDI_EvalOp_ModuleOff &&
(beginning_ops[0] == RDI_EvalOp_ConstU8 ||
beginning_ops[0] == RDI_EvalOp_ConstU16 ||
beginning_ops[0] == RDI_EvalOp_ConstU32 ||
beginning_ops[0] == RDI_EvalOp_ConstU64))) &&
beginning_ops[2] == RDI_EvalOp_Add &&
beginning_ops[3] == RDI_EvalOp_Stop)
{
U64 voff = beginning_nodes[0]->p + beginning_nodes[1]->p;
loc.kind = RDI_LocationKind_ModuleOff;
loc.offset = voff;
}
// rjf: match simple TLS offsets
if(beginning_ops[0] == RDI_EvalOp_TLSOff && beginning_ops[1] == RDI_EvalOp_Stop)
{
U64 toff = beginning_nodes[0]->p;
loc.kind = RDI_LocationKind_TLSOff;
loc.offset = toff;
}
else if(((beginning_ops[0] == RDI_EvalOp_TLSOff &&
(beginning_ops[1] == RDI_EvalOp_ConstU8 ||
beginning_ops[1] == RDI_EvalOp_ConstU16 ||
beginning_ops[1] == RDI_EvalOp_ConstU32 ||
beginning_ops[1] == RDI_EvalOp_ConstU64)) ||
(beginning_ops[1] == RDI_EvalOp_TLSOff &&
(beginning_ops[0] == RDI_EvalOp_ConstU8 ||
beginning_ops[0] == RDI_EvalOp_ConstU16 ||
beginning_ops[0] == RDI_EvalOp_ConstU32 ||
beginning_ops[0] == RDI_EvalOp_ConstU64))) &&
beginning_ops[2] == RDI_EvalOp_Add &&
beginning_ops[3] == RDI_EvalOp_Stop)
{
U64 toff = beginning_nodes[0]->p + beginning_nodes[1]->p;
loc.kind = RDI_LocationKind_TLSOff;
loc.offset = toff;
}
}
//- rjf: collect
{
RDIM_Rng1U64 voff_range = {range.min - base_vaddr, range.max - base_vaddr};
if(bytecode_is_framebase_dependent)
{
voff_range.min = Max(voff_range.min, framebase_voff_range.min);
voff_range.max = Min(voff_range.max, framebase_voff_range.max);
}
rdim_location_case_list_push(arena, dst_locations, loc, voff_range);
dst_is_tls_dependent[0] = is_tls_dependent;
}
//- rjf: not frame-base dependent? -> break - don't do this per-framebase case
if(!bytecode_is_framebase_dependent)
{
break;
}
}
}
}
}
////////////////////////
//- rjf: produce symbols from tag
//
RDIM_Symbol *new_symbol = 0;
RDIM_Scope *new_scope_open = 0;
B32 parent_tag_is_container_to_deduped_tags = 0;
switch(tag.kind)
{
default:{}break;
//- rjf: determine if parent tag is a container to deduplicated tags
case DW_TagKind_Namespace:
case DW_TagKind_StructureType:
case DW_TagKind_UnionType:
case DW_TagKind_ClassType:
case DW_TagKind_EnumerationType:
case DW_TagKind_Typedef:
{
parent_tag_is_container_to_deduped_tags = 1;
}break;
//- rjf: subprograms (procedures)
case DW_TagKind_SubProgram:
if(!is_decl)
{
RDIM_Scope *root_scope = rdim_scope_chunk_list_push(arena, &dst_artifacts->scopes, chunk_count);
RDIM_Symbol *procedure = rdim_symbol_chunk_list_push(arena, &dst_artifacts->procedures, chunk_count);
procedure->name = name;
procedure->link_name = link_name;
procedure->type = type;
procedure->root_scope = root_scope;
procedure->location_cases = framebase_location_cases;
root_scope->symbol = procedure;
root_scope->voff_ranges = ranges;
dst_artifacts->scopes.scope_voff_count += 2*ranges.count;
new_symbol = procedure;
new_scope_open = root_scope;
}break;
//- rjf: inline site
case DW_TagKind_InlinedSubroutine:
{
// TODO(rjf)
}break;
//- rjf: variables
case DW_TagKind_Variable:
case DW_TagKind_FormalParameter:
if(name.size != 0)
{
U64 var_chunk_count = chunk_count;
RDIM_SymbolChunkList *dst_symbols = &dst_artifacts->global_variables;
if(location_is_tls_dependent)
{
dst_symbols = &dst_artifacts->thread_variables;
}
else if(top_parent != 0 && top_parent->scope != 0)
{
dst_symbols = &top_parent->scope->locals;
var_chunk_count = 8;
}
RDIM_Symbol *var = rdim_symbol_chunk_list_push(arena, dst_symbols, var_chunk_count);
var->is_extern = is_external;
var->is_param = (tag.kind == DW_TagKind_FormalParameter);
var->name = name;
var->link_name = link_name;
var->type = type;
var->location_cases = location_cases;
new_symbol = var;
}break;
//- rjf: lexical blocks (scopes)
case DW_TagKind_LexicalBlock:
{
RDIM_Scope *scope = rdim_scope_chunk_list_push(arena, &dst_artifacts->scopes, chunk_count);
scope->voff_ranges = ranges;
dst_artifacts->scopes.scope_voff_count += 2*ranges.count;
new_scope_open = scope;
}break;
}
////////////////////////
//- rjf: if we've determined that the parent tag is a container to
// deduplicated tags (types, namespaces), then we need to record
// the .debug_info offset -> scope mapping for this scope, so it
// can be equipped as a container later.
//
if(parent_tag_is_container_to_deduped_tags)
{
U64 info_off = 0;
RDIM_Scope *scope = 0;
for(D2R_ParentNode *n = top_parent; n != 0; n = n->next)
{
if(n->scope != 0)
{
info_off = n->info_off;
scope = n->scope;
break;
}
}
if(scope != 0)
{
D2R_ScopeContainerNode *n = push_array(scratch.arena, D2R_ScopeContainerNode, 1);
n->info_off = info_off;
n->scope = scope;
U64 hash = u64_hash_from_str8(str8_struct(&info_off));
U64 slot_idx = hash%scope_container_map->slots_count;
for(B32 gathered = 0; !gathered;)
{
U64 expected_head_value = ins_atomic_u64_eval(&scope_container_map->slots[slot_idx]);
n->next = (D2R_ScopeContainerNode *)expected_head_value;
if(expected_head_value == ins_atomic_u64_eval_cond_assign(&scope_container_map->slots[slot_idx], (U64)n, expected_head_value))
{
gathered = 1;
}
}
}
}
////////////////////////
//- rjf: equip new symbols with container info
//
if(new_symbol != 0 && top_parent != 0)
{
if(top_parent->scope != 0)
{
new_symbol->container_scope = top_parent->scope;
}
else if(top_parent->container_type != 0)
{
new_symbol->container_type = top_parent->container_type;
}
else if(top_parent->container_namespace != 0)
{
new_symbol->container_namespace = top_parent->container_namespace;
}
}
////////////////////////
//- rjf: insert new scopes to their parent
//
if(new_scope_open && top_parent != 0 && top_parent->scope != 0)
{
RDIM_Scope *parent = top_parent->scope;
SLLQueuePush_N(parent->first_child, parent->last_child, new_scope_open, next_sibling);
new_scope_open->parent_scope = parent;
if(new_scope_open->symbol == 0)
{
new_scope_open->symbol = parent->symbol;
}
}
////////////////////////
//- rjf: gather container info for new tag parents
//
RDIM_Type *container_type = 0;
RDIM_Namespace *container_namespace = 0;
if(tag.has_children)
{
// rjf: info offset -> hash
U64 hash = 0;
{
D2R_UnitDedupedTagMap *unit_deduped_tag_map = &unit_deduped_tag_maps[unit_idx];
U64 info_off_hash = u64_hash_from_str8(str8_struct(&start_off));
U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_map->slots_count;
for EachNode(n, D2R_UnitDedupedTagNode, unit_deduped_tag_map->slots[info_off_slot_idx])
{
if(n->src_info_off == start_off)
{
hash = n->dst_hash;
break;
}
}
}
// rjf: map hash -> unique tag node
D2R_UniqueTagNode *tag_node = 0;
{
U64 slot_idx = hash%unique_tag_slots_count;
for(D2R_UniqueTagNode *n = unique_tag_slots[slot_idx]; n != 0; n = n->next)
{
if(n->hash == hash)
{
tag_node = n;
break;
}
}
}
// rjf: map tag node -> type/namespace
if(tag_node != 0) switch(tag_node->kind)
{
default:{}break;
case D2R_UniqueTagKind_Type: {container_type = type_from_idx_map[tag_node->order_idx];}break;
case D2R_UniqueTagKind_Namespace:{container_namespace = namespace_from_idx_map[tag_node->order_idx];}break;
}
}
////////////////////////
//- rjf: push tag parents
//
if(tag.has_children)
{
// rjf: push new parent to stack
D2R_ParentNode *n = free_parent;
if(n != 0)
{
SLLStackPop(free_parent);
}
else
{
n = push_array(scratch.arena, D2R_ParentNode, 1);
}
n->tag_kind = tag.kind;
n->info_off = start_off;
n->scope = new_scope_open ? new_scope_open : top_parent ? top_parent->scope : 0;
n->container_type = new_scope_open ? 0 : container_type;
n->container_namespace = new_scope_open ? 0 : container_namespace;
if(framebase_attrib != &dw2_attrib_nil)
{
n->framebase_location_cases = framebase_location_cases;
}
else if(top_parent != 0)
{
n->framebase_location_cases = top_parent->framebase_location_cases;
}
SLLStackPush(top_parent, n);
}
////////////////////////
//- rjf: pop scopes
//
if(tag.kind == DW_TagKind_Null && top_parent != 0)
{
D2R_ParentNode *n = top_parent;
SLLStackPop(top_parent);
SLLStackPush(free_parent, n);
}
////////////////////////
//- rjf: tree nav
//
if(tag.has_children)
{
depth += 1;
}
if(tag.kind == DW_TagKind_Null)
{
depth -= 1;
}
scratch_end(scratch2);
if(off == start_off)
{
break;
}
}
}
}
lane_sync();
}
////////////////////////////
//- rjf: join all symbols from all sub-unit works into their units
//
ProfScope("join all symbols from all sub-unit works into their units")
{
U64 unit_take_idx_ = 0;
U64 *unit_take_idx_ptr = &unit_take_idx_;
lane_sync_u64(&unit_take_idx_ptr, 0);
for(;;)
{
U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr)-1;
if(unit_idx >= unit_count)
{
break;
}
RDIM_Unit *dst_unit = unit_from_idx_map[unit_idx];
for EachIndex(work_idx, sub_unit_works_count)
{
if(sub_unit_works[work_idx].unit_idx == unit_idx)
{
rdim_symbol_chunk_list_concat_in_place(&dst_unit->global_variables, &sub_unit_work_artifacts[work_idx].global_variables);
rdim_symbol_chunk_list_concat_in_place(&dst_unit->thread_variables, &sub_unit_work_artifacts[work_idx].thread_variables);
rdim_symbol_chunk_list_concat_in_place(&dst_unit->constants, &sub_unit_work_artifacts[work_idx].constants);
rdim_symbol_chunk_list_concat_in_place(&dst_unit->procedures, &sub_unit_work_artifacts[work_idx].procedures);
rdim_scope_chunk_list_concat_in_place(&dst_unit->scopes, &sub_unit_work_artifacts[work_idx].scopes);
rdim_inline_site_chunk_list_concat_in_place(&dst_unit->inline_sites, &sub_unit_work_artifacts[work_idx].inline_sites);
}
}
}
lane_sync();
}
////////////////////////////
//- rjf: upgrade all types with namespacing info
//
ProfScope("upgrade all types with namespacing info")
{
Rng1U64 range = lane_range(type_count);
for EachInRange(type_idx, range)
{
D2R_UniqueTagNode *type_tag_node = type_tag_nodes[type_idx];
U64 container_ancestor_info_off = type_tag_node->container_ancestor_info_off;
RDIM_Type *dst_type = type_from_idx_map[type_idx];
if(dst_type == 0)
{
continue;
}
RDIM_UDT *dst_udt = dst_type->udt;
if(dst_udt == 0)
{
continue;
}
// rjf: find container scopes
RDIM_Scope *container_scope = 0;
{
U64 info_off = container_ancestor_info_off;
U64 hash = u64_hash_from_str8(str8_struct(&info_off));
U64 slot_idx = hash%scope_container_map->slots_count;
for(D2R_ScopeContainerNode *n = scope_container_map->slots[slot_idx]; n != 0; n = n->next)
{
if(n->info_off == info_off)
{
container_scope = n->scope;
break;
}
}
}
// rjf: find container types/namespaces
RDIM_Type *container_type = 0;
RDIM_Namespace *container_namespace = 0;
if(!container_scope)
{
U64 info_off = container_ancestor_info_off;
U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off);
U64 unit_idx = unit_num > 0 ? unit_num-1 : 0;
// rjf: info off -> hash
U64 hash = 0;
{
D2R_UnitDedupedTagMap *unit_deduped_tag_map = &unit_deduped_tag_maps[unit_idx];
U64 info_off_hash = u64_hash_from_str8(str8_struct(&info_off));
U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_map->slots_count;
for(D2R_UnitDedupedTagNode *n = unit_deduped_tag_map->slots[info_off_slot_idx]; n != 0; n = n->next)
{
if(n->src_info_off == info_off)
{
hash = n->dst_hash;
break;
}
}
}
// rjf: hash -> type
{
U64 unique_type_tag_slot_idx = hash%unique_tag_slots_count;
for(D2R_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next)
{
if(n->hash == hash)
{
if(n->kind == D2R_UniqueTagKind_Type)
{
container_type = type_from_idx_map[n->order_idx];
}
else if(n->kind == D2R_UniqueTagKind_Namespace)
{
container_namespace = namespace_from_idx_map[n->order_idx];
}
break;
}
}
}
}
// rjf: fill
dst_udt->container_scope = container_scope;
dst_udt->container_type = container_type;
dst_udt->container_namespace = container_namespace;
}
}
lane_sync();
////////////////////////////
//- rjf: upgrade all namespaces with namespacing info
//
ProfScope("upgrade all namespaces with namespacing info")
{
Rng1U64 range = lane_range(namespace_count);
for EachInRange(namespace_idx, range)
{
D2R_UniqueTagNode *unique_tag_node = namespace_tag_nodes[namespace_idx];
U64 container_ancestor_info_off = unique_tag_node->container_ancestor_info_off;
RDIM_Namespace *dst_namespace = namespace_from_idx_map[namespace_idx];
if(dst_namespace == 0)
{
continue;
}
// rjf: find container scopes
RDIM_Scope *container_scope = 0;
{
U64 info_off = container_ancestor_info_off;
U64 hash = u64_hash_from_str8(str8_struct(&info_off));
U64 slot_idx = hash%scope_container_map->slots_count;
for(D2R_ScopeContainerNode *n = scope_container_map->slots[slot_idx]; n != 0; n = n->next)
{
if(n->info_off == info_off)
{
container_scope = n->scope;
break;
}
}
}
// rjf: find container types/namespaces
RDIM_Type *container_type = 0;
RDIM_Namespace *container_namespace = 0;
{
U64 info_off = container_ancestor_info_off;
U64 unit_num = rng1u64_array_num_from_value__binary_search(&unit_info_tag_ranges_array, info_off);
U64 unit_idx = unit_num > 0 ? unit_num-1 : 0;
// rjf: info off -> hash
U64 hash = 0;
{
D2R_UnitDedupedTagMap *unit_deduped_tag_map = &unit_deduped_tag_maps[unit_idx];
U64 info_off_hash = u64_hash_from_str8(str8_struct(&info_off));
U64 info_off_slot_idx = info_off_hash%unit_deduped_tag_map->slots_count;
for(D2R_UnitDedupedTagNode *n = unit_deduped_tag_map->slots[info_off_slot_idx]; n != 0; n = n->next)
{
if(n->src_info_off == info_off)
{
hash = n->dst_hash;
break;
}
}
}
// rjf: hash -> type
{
U64 unique_type_tag_slot_idx = hash%unique_tag_slots_count;
for(D2R_UniqueTagNode *n = unique_tag_slots[unique_type_tag_slot_idx]; n != 0; n = n->next)
{
if(n->hash == hash)
{
if(n->kind == D2R_UniqueTagKind_Type)
{
container_type = type_from_idx_map[n->order_idx];
}
else if(n->kind == D2R_UniqueTagKind_Namespace)
{
container_namespace = namespace_from_idx_map[n->order_idx];
}
break;
}
}
}
}
// rjf: fill
dst_namespace->parent_scope = container_scope;
dst_namespace->parent_type = container_type;
dst_namespace->parent_namespace = container_namespace;
}
}
lane_sync();
////////////////////////////
//- rjf: fill result
//
RDIM_BakeParams result = {0};
{
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;
}
#undef d2r_type_from_builtin_kind
lane_sync();
scratch_end(scratch);
return result;
}