mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-10-02 05:58:41 +00:00
4393 lines
187 KiB
C
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 = ¶ms->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(¶m_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;
|
|
}
|