test all LNCT encoding in the file table,

test line instruction encoder,
test special opcode encoding
This commit is contained in:
Nikita Smith
2026-02-22 20:17:00 -08:00
parent a7e64c3928
commit 2a8a1a5741
6 changed files with 549 additions and 166 deletions
+34 -32
View File
@@ -2276,7 +2276,7 @@ dw_read_line_vm_header(Arena *arena,
// compile unit name is always first in the file table
{
DW_LineFileNode *node = push_array(scratch.arena, DW_LineFileNode, 1);
node->file.file_name = cu_name;
node->file.path = cu_name;
SLLQueuePush(file_list.first, file_list.last, node);
file_list.node_count += 1;
}
@@ -2288,10 +2288,10 @@ dw_read_line_vm_header(Arena *arena,
if (first_byte == 0) { break; }
DW_LineFile file = {0};
TryRead(str8_deserial_read_cstr (data, cursor, &file.file_name), cursor, exit);
TryRead(str8_deserial_read_uleb128(data, cursor, &file.dir_idx), cursor, exit);
TryRead(str8_deserial_read_uleb128(data, cursor, &file.modify_time), cursor, exit);
TryRead(str8_deserial_read_uleb128(data, cursor, &file.file_size), cursor, exit);
TryRead(str8_deserial_read_cstr (data, cursor, &file.path), cursor, exit);
TryRead(str8_deserial_read_uleb128(data, cursor, &file.dir_idx), cursor, exit);
TryRead(str8_deserial_read_uleb128(data, cursor, &file.time_stamp), cursor, exit);
TryRead(str8_deserial_read_uleb128(data, cursor, &file.size), cursor, exit);
DW_LineFileNode *node = push_array(scratch.arena, DW_LineFileNode, 1);
node->file = file;
@@ -2309,10 +2309,10 @@ dw_read_line_vm_header(Arena *arena,
file_table.v = push_array(arena, DW_LineFile, file_list.node_count);
for EachNode(n, DW_LineFileNode, file_list.first) {
DW_LineFile *dst = &file_table.v[file_table.count++];
dst->file_name = push_str8_copy(arena, n->file.file_name);
dst->path = push_str8_copy(arena, n->file.path);
dst->dir_idx = n->file.dir_idx;
dst->modify_time = n->file.modify_time;
dst->file_size = n->file.file_size;
dst->time_stamp = n->file.time_stamp;
dst->size = n->file.size;
}
}
// DWARF5
@@ -2379,12 +2379,12 @@ dw_read_line_vm_header(Arena *arena,
cursor += form_size;
switch (lnct) {
case DW_LNCT_Path: { file->file_name = dw_interp_string(input, format, cu_str_offsets, form); } break;
case DW_LNCT_DirectoryIndex: { file->dir_idx = dw_interp_const_u64(form); } break;
case DW_LNCT_TimeStamp: { file->modify_time = dw_interp_const_u64(form); } break;
case DW_LNCT_Size: { file->file_size = dw_interp_const_u64(form); } break;
case DW_LNCT_MD5: { file->md5_digest = dw_interp_const_u128(form); } break;
case DW_LNCT_LLVM_Source: { file->source = dw_interp_string(input, format, cu_str_offsets, form); } break;
case DW_LNCT_Path: { file->path = dw_interp_string(input, format, cu_str_offsets, form); } break;
case DW_LNCT_DirectoryIndex: { file->dir_idx = dw_interp_const_u64(form); } break;
case DW_LNCT_TimeStamp: { file->time_stamp = dw_interp_const_u64(form); } break;
case DW_LNCT_Size: { file->size = dw_interp_const_u64(form); } break;
case DW_LNCT_MD5: { file->md5 = dw_interp_const_u128(form); } break;
case DW_LNCT_LLVM_Source: { file->source = dw_interp_string(input, format, cu_str_offsets, form); } break;
default: {
Assert(!"unexpected LNTC encoding");
} break;
@@ -2565,7 +2565,9 @@ dw_line_vm_step(DW_LineVM *vm)
} break;
case DW_StdOpcode_FixedAdvancePc: {
TryRead(str8_deserial_read_struct(vm->program, vm->cursor, &vm->operands[0].u64), vm->cursor, exit);
U16 fixed_advance = 0;
TryRead(str8_deserial_read_struct(vm->program, vm->cursor, &fixed_advance), vm->cursor, exit);
vm->operands[0].u64 = fixed_advance;
vm->state.address += vm->operands[0].u64;
vm->state.op_index = 0;
} break;
@@ -2590,7 +2592,7 @@ dw_line_vm_step(DW_LineVM *vm)
vm->cursor += vm->ext_length;
U64 ext_cursor = 0;
TryRead(str8_deserial_read_struct(ext_data, ext_cursor, &vm->ext_opcode), vm->cursor, exit);
TryRead(str8_deserial_read_struct(ext_data, ext_cursor, &vm->ext_opcode), ext_cursor, exit);
switch (vm->ext_opcode) {
case DW_ExtOpcode_EndSequence: {
@@ -2600,26 +2602,26 @@ dw_line_vm_step(DW_LineVM *vm)
} break;
case DW_ExtOpcode_SetAddress: {
TryRead(str8_deserial_read(ext_data, ext_cursor, &vm->operands[0].u64, vm->header.address_size, vm->header.address_size), vm->cursor, exit);
TryRead(str8_deserial_read(ext_data, ext_cursor, &vm->operands[0].u64, vm->header.address_size, vm->header.address_size), ext_cursor, exit);
vm->state.address = vm->operands[0].u64;
vm->state.op_index = 0;
} break;
case DW_ExtOpcode_DefineFile: {
TryRead(str8_deserial_read_cstr (ext_data, ext_cursor, &vm->operands[0].string), vm->cursor, exit); // file name
TryRead(str8_deserial_read_uleb128(ext_data, ext_cursor, &vm->operands[1].u64), vm->cursor, exit); // dir index
TryRead(str8_deserial_read_uleb128(ext_data, ext_cursor, &vm->operands[2].u64), vm->cursor, exit); // modify time
TryRead(str8_deserial_read_uleb128(ext_data, ext_cursor, &vm->operands[3].u64), vm->cursor, exit); // file size
TryRead(str8_deserial_read_cstr (ext_data, ext_cursor, &vm->operands[0].string), ext_cursor, exit); // file name
TryRead(str8_deserial_read_uleb128(ext_data, ext_cursor, &vm->operands[1].u64), ext_cursor, exit); // dir index
TryRead(str8_deserial_read_uleb128(ext_data, ext_cursor, &vm->operands[2].u64), ext_cursor, exit); // modify time
TryRead(str8_deserial_read_uleb128(ext_data, ext_cursor, &vm->operands[3].u64), ext_cursor, exit); // file size
if (vm->ext_file_ht == 0) {
vm->ext_file_ht = hash_table_init(vm->arena, 512);
}
DW_LineFile *file = push_array(vm->arena, DW_LineFile, 1);
file->file_name = vm->operands[0].string;
file->dir_idx = vm->operands[1].u64;
file->modify_time = vm->operands[2].u64;
file->file_size = vm->operands[3].u64;
file->path = vm->operands[0].string;
file->dir_idx = vm->operands[1].u64;
file->time_stamp = vm->operands[2].u64;
file->size = vm->operands[3].u64;
if (hash_table_search_u64_raw(vm->ext_file_ht, vm->state.file_index) == 0) {
vm->error = push_str8f(vm->arena, "file with index %I64d was already defined", vm->state.file_index);
@@ -2630,7 +2632,7 @@ dw_line_vm_step(DW_LineVM *vm)
} break;
case DW_ExtOpcode_SetDiscriminator: {
TryRead(str8_deserial_read_uleb128(ext_data, ext_cursor, &vm->operands[0].u64), vm->cursor, exit);
TryRead(str8_deserial_read_uleb128(ext_data, ext_cursor, &vm->operands[0].u64), ext_cursor, exit);
vm->state.discriminator = vm->operands[0].u64;
} break;
@@ -2675,13 +2677,13 @@ dw_path_from_file(Arena *arena, String8Array dir_table, DW_LineFile *file)
Assert(dir_table.count > 0);
// find directory and file name associated with the file index
String8 dir = dir_table.v[file->dir_idx];
String8 file_name = file->file_name;
String8 dir = dir_table.v[file->dir_idx];
String8 path = file->path;
// infer path style if directory is empty use file name
PathStyle style = path_style_from_str8(dir);
if (style == PathStyle_Null || style == PathStyle_Relative) {
style = path_style_from_str8(file->file_name);
style = path_style_from_str8(file->path);
}
String8List path_list = {0};
@@ -2697,17 +2699,17 @@ dw_path_from_file(Arena *arena, String8Array dir_table, DW_LineFile *file)
str8_list_concat_in_place(&path_list, &dir_list);
// push file name
str8_list_push(scratch.arena, &path_list, file->file_name);
str8_list_push(scratch.arena, &path_list, file->path);
// resolve dots in the path
str8_path_list_resolve_dots_in_place(&path_list, style);
}
// join path
String8 path = str8_path_list_join_by_style(arena, &path_list, style);
String8 result = str8_path_list_join_by_style(arena, &path_list, style);
scratch_end(scratch);
return path;
return result;
}
internal DW_PubStringsTable
+4 -4
View File
@@ -173,11 +173,11 @@ typedef struct DW_TagTree
typedef struct DW_LineFile
{
String8 file_name;
String8 path;
U64 dir_idx;
U64 modify_time;
U64 file_size;
U128 md5_digest;
U64 time_stamp;
U64 size;
U128 md5;
String8 source;
} DW_LineFile;
+71 -32
View File
@@ -117,6 +117,7 @@ dw_writer_begin(DW_Format format, DW_Version version, DW_CompUnitKind cu_kind, A
writer->line.line_range = 14;
writer->line.opcode_base = DW_StdOpcode_Count;
writer->line.ln = 1;
Assert(writer->line.line_base < writer->line.line_range);
for EachElement(i, writer->sections) {
str8_serial_begin(arena, &writer->sections[i].srl);
}
@@ -179,6 +180,10 @@ dw_serial_push_form(Arena *arena, String8List *srl, DW_Version version, DW_Forma
case DW_Form_String: {
str8_serial_push_cstr(arena, srl, form.reader.string);
} break;
case DW_Form_Block: {
dw_serial_push_uleb128(arena, srl, form.reader.block.size);
str8_serial_push_string(arena, srl, form.reader.block);
} break;
case DW_Form_Flag: {
str8_serial_push_u8(arena, srl, form.reader.flag);
} break;
@@ -537,7 +542,9 @@ dw_data_from_line_insts(Arena *arena, U8 address_size, DW_LineInstList insts)
} break;
case DW_StdOpcode_SetPrologueEnd: {} break;
case DW_StdOpcode_SetEpilogueBegin: {} break;
case DW_StdOpcode_SetIsa: {} break;
case DW_StdOpcode_SetIsa: {
dw_serial_push_uleb128(arena, &srl, inst->set_isa);
} break;
case DW_StdOpcode_ExtendedOpcode: {
// get ext size
U64 ext_size = 0;
@@ -562,7 +569,7 @@ dw_data_from_line_insts(Arena *arena, U8 address_size, DW_LineInstList insts)
switch (inst->ext) {
case DW_ExtOpcode_EndSequence: {} break;
case DW_ExtOpcode_SetAddress: {
dw_serial_push_uleb128(arena, &srl, inst->set_address);
str8_serial_push_string(arena, &srl, str8_copy(arena, str8((U8 *)&inst->set_address, address_size)));
} break;
case DW_ExtOpcode_DefineFile: {
NotImplemented;
@@ -590,23 +597,29 @@ dw_writer_line_emit(DW_Writer *writer, DW_WriterFile *file, U64 ln, U64 col, U64
dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_set_file(file));
}
S64 col_delta = (S64)col - (S64)writer->line.col;
if (col_delta != 0) { dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_set_column(col_delta)); }
Assert(addr <= max_S64);
S64 addr_delta = (S64)addr - (S64)writer->line.addr;
S64 ln_delta = (S64)ln - (S64)writer->line.ln;
S64 col_delta = (S64)col - (S64)writer->line.col;
if (col_delta == 0 && (ln_delta > 0 || addr_delta > 0) &&
writer->line.line_base <= ln_delta && ln_delta < writer->line.line_range+writer->line.line_base &&
addr_delta < max_U8 - writer->line.opcode_base) {
U8 opcode = (ln_delta - writer->line.line_base) + (writer->line.line_range * addr_delta) + writer->line.opcode_base;
S64 max_ln_delta = writer->line.line_range + writer->line.line_base;
S64 max_addr_delta = (max_U8 - writer->line.opcode_base - (ln_delta - writer->line.line_base)) / writer->line.line_range;
if ((ln_delta != 0 || addr_delta != 0) &&
writer->line.line_base <= ln_delta && ln_delta < max_ln_delta && // check line number window
0 <= addr_delta && addr_delta <= max_addr_delta) { // check address window
U8 opcode = (ln_delta - writer->line.line_base) + (addr_delta * writer->line.line_range) + writer->line.opcode_base;
dw_line_inst_list_push(writer->arena, &writer->line.line_insts, (DW_LineInst){ .opcode = opcode });
} else {
if (addr_delta != 0) { dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_advance_pc(addr_delta)); }
if (ln_delta != 0) { dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_advance_line(ln_delta)); }
if (col_delta != 0) { dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_set_column(col_delta)); }
dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_copy());
if (addr_delta != 0 || ln_delta != 0 || col_delta != 0) {
dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_copy());
}
}
// update registers
writer->line.file = file;
writer->line.ln = ln;
writer->line.col = col;
@@ -619,6 +632,22 @@ dw_writer_line_set_address(DW_Writer *writer, U64 address)
dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNE_set_address(address));
}
internal void
dw_writer_line_set_discriminator(DW_Writer *writer, U64 d)
{
dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNE_set_discriminator(d));
}
internal void
dw_writer_line_end_sequence(DW_Writer *writer)
{
writer->line.file = 0;
writer->line.ln = 1;
writer->line.addr = 0;
writer->line.col = 0;
dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNE_end_sequence());
}
internal void
dw_writer_line_set_prologue_end(DW_Writer *writer)
{
@@ -641,8 +670,7 @@ internal DW_WriterFile *
dw_writer_new_file(DW_Writer *writer, String8 path)
{
DW_WriterFile *file = push_array(writer->arena, DW_WriterFile, 1);
file->path = path;
file->file_idx = writer->line.file_count;
file->path = path;
SLLQueuePush(writer->line.first_file, writer->line.last_file, file);
writer->line.file_count += 1;
@@ -936,7 +964,7 @@ dw_writer_emit(DW_Writer *writer)
// line
{
// push line table terminator
dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNE_end_sequence());
dw_writer_line_end_sequence(writer);
// find comp dir in the compile unit tag
String8 comp_dir = {0};
@@ -945,7 +973,7 @@ dw_writer_emit(DW_Writer *writer)
for EachNode(attrib, DW_WriterAttrib, writer->root->first_attrib) {
if (attrib->kind == DW_AttribKind_CompDir) {
AssertAlways(attrib->form.writer.kind == DW_WriterFormKind_String);
comp_dir = attrib->form.writer.string;
comp_dir = str8_chop_last_slash(attrib->form.writer.string);
} else if (attrib->kind == DW_AttribKind_Name) {
AssertAlways(attrib->form.writer.kind == DW_WriterFormKind_String);
comp_name = attrib->form.writer.string;
@@ -964,31 +992,38 @@ dw_writer_emit(DW_Writer *writer)
dw_serial_push_uleb128(writer->arena, srl, DW_LNCT_Path);
dw_serial_push_uleb128(writer->arena, srl, DW_Form_String);
// dedup directories
String8List dirs = {0};
HashTable *dir_ht = hash_table_init(scratch.arena, writer->line.file_count + 1);
// first entry must be compile unit directory
hash_table_push_string_u64(scratch.arena, dir_ht, comp_dir, dir_ht->count);
str8_list_push(scratch.arena, &dirs, comp_dir);
if (comp_dir.size == 2 && char_is_alpha(comp_dir.str[0]) && comp_dir.str[1] == ':') {
str8_list_pushf(scratch.arena, &dirs, "%S/", comp_dir);
} else {
str8_list_push(scratch.arena, &dirs, comp_dir);
}
for EachNode(file, DW_WriterFile, writer->line.first_file) {
String8 path = str8_chop_last_slash(file->path);
if (path.size == 0) {
continue;
}
if ( ! hash_table_search_string_u64(dir_ht, path, &file->dir_idx)) {
// @dir_idx
file->dir_idx = hash_table_push_string_u64(scratch.arena, dir_ht, path, dir_ht->count)->v.value_u64;
str8_list_push(scratch.arena, &dirs, path);
if (path.size == 2 && char_is_alpha(path.str[0]) && path.str[1] == ':') {
str8_list_pushf(scratch.arena, &dirs, "%S/", path);
} else {
str8_list_push(scratch.arena, &dirs, path);
}
}
}
KeyValuePair *dirs_kv = key_value_pairs_from_hash_table(scratch.arena, dir_ht);
//sort_key_value_pairs_as_string_sensitive(dirs_kv, dir_ht->count);
// (15) directories_count
dw_serial_push_uleb128(writer->arena, srl, dir_ht->count);
dw_serial_push_uleb128(writer->arena, srl, dirs.node_count);
// (16) directories
for (U64 i = 0; i < dir_ht->count; i += 1) {
String8 dir = dirs_kv[i].key_string;
dw_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, (DW_WriterXForm) { .reader = { .kind = DW_Form_String, .string = dir } });
for EachNode(dir_n, String8Node, dirs.first) {
dw_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, (DW_WriterXForm) { .reader = { .kind = DW_Form_String, .string = dir_n->string } });
}
scratch_end(scratch);
@@ -1014,10 +1049,10 @@ dw_writer_emit(DW_Writer *writer)
// enable encodings in use
for EachNode(file, DW_WriterFile, writer->line.first_file) {
if (file->time_stamp != 0) { *(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_TimeStamp ) = DW_Form_UData; }
if (file->size != 0) { *(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_Size ) = DW_Form_UData; }
if (file->md5.size != 0) { *(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_MD5 ) = DW_Form_Block; }
if (file->source.size != 0) { *(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_LLVM_Source) = DW_Form_String; }
if (file->time_stamp != 0) { *(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_TimeStamp ) = DW_Form_UData; }
if (file->size != 0) { *(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_Size ) = DW_Form_UData; }
if ( ! u128_match(file->md5, u128_zero())) { *(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_MD5 ) = DW_Form_Block; }
if (file->source.size != 0) { *(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_LLVM_Source) = DW_Form_String; }
}
// count needed encodings
@@ -1057,11 +1092,15 @@ dw_writer_emit(DW_Writer *writer)
}
}
}
U64 file_idx = 0;
for EachNode(file, DW_WriterFile, writer->line.first_file) {
static DW_WriterXForm null_form_udata = { .reader = { .kind = DW_Form_UData } };
static DW_WriterXForm null_form_string = { .reader = { .kind = DW_Form_String } };
static DW_WriterXForm null_form_block = { .reader = { .kind = DW_Form_Block } };
// @file_idx
file->file_idx = file_idx++;
// path
Assert(*(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_Path) == DW_Form_String);
String8 file_name = str8_skip_last_slash(file->path);
@@ -1095,8 +1134,8 @@ dw_writer_emit(DW_Writer *writer)
// MD5
if (*(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_MD5) == DW_Form_Block) {
if (file->md5.size) {
DW_WriterXForm md5_form = { .reader = { .kind = DW_Form_Block, .block = file->md5 } };
if ( ! u128_match(file->md5, u128_zero())) {
DW_WriterXForm md5_form = { .reader = { .kind = DW_Form_Block, .block = str8_struct(&file->md5) } };
dw_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, md5_form);
} else {
dw_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, null_form_block);
@@ -1143,9 +1182,9 @@ dw_writer_emit(DW_Writer *writer)
// (11) opcode_base
str8_serial_push_struct(writer->arena, srl, &writer->line.opcode_base);
// (12) standard_opcode_lengths
U8 *std_op_lens = push_array(writer->arena, U8, DW_StdOpcode_Count - 1);
for (U64 i = 1; i < DW_StdOpcode_Count; i += 1) { std_op_lens[i-1] = dw_length_from_std_opcode(i); }
str8_serial_push_string(writer->arena, srl, str8(std_op_lens, DW_StdOpcode_Count - 1));
U8 *std_op_lens = push_array(writer->arena, U8, writer->line.opcode_base - 1);
for (U64 i = 1; i < writer->line.opcode_base; i += 1) { std_op_lens[i-1] = dw_length_from_std_opcode(i); }
str8_serial_push_string(writer->arena, srl, str8(std_op_lens, writer->line.opcode_base - 1));
// directory table
str8_list_concat_in_place(srl, &dir_table_srl);
// file table
+9 -7
View File
@@ -135,7 +135,7 @@ typedef struct DW_WriterFile
String8 path;
U64 time_stamp;
U64 size;
String8 md5;
U128 md5;
String8 source;
// computed during line table emit step
U64 file_idx;
@@ -306,12 +306,14 @@ internal DW_LineInstNode * dw_line_inst_list_push (Arena *arena, DW_LineInst
#define DW_LNE_set_address(a) (DW_LineInst){ .opcode = DW_StdOpcode_ExtendedOpcode, .ext = DW_ExtOpcode_SetAddress, .set_address = a }
#define DW_LNE_set_discriminator(d) (DW_LineInst){ .opcode = DW_StdOpcode_ExtendedOpcode, .ext = DW_ExtOpcode_SetDiscriminator, .set_discriminator = d }
internal void dw_writer_line_emit (DW_Writer *writer, DW_WriterFile *file, U64 ln, U64 col, U64 addr);
internal void dw_writer_line_set_address (DW_Writer *writer, U64 address);
internal void dw_writer_line_set_prologue_end(DW_Writer *writer);
internal void dw_writer_line_epilogue_begin (DW_Writer *writer);
internal void dw_writer_line_set_isa (DW_Writer *writer, U64 isa);
internal DW_WriterFile * dw_writer_new_file (DW_Writer *writer, String8 path);
internal void dw_writer_line_emit (DW_Writer *writer, DW_WriterFile *file, U64 ln, U64 col, U64 addr);
internal void dw_writer_line_set_address (DW_Writer *writer, U64 address);
internal void dw_writer_line_set_discriminator(DW_Writer *writer, U64 d);
internal void dw_writer_line_end_sequence (DW_Writer *writer);
internal void dw_writer_line_set_prologue_end (DW_Writer *writer);
internal void dw_writer_line_epilogue_begin (DW_Writer *writer);
internal void dw_writer_line_set_isa (DW_Writer *writer, U64 isa);
internal DW_WriterFile * dw_writer_new_file (DW_Writer *writer, String8 path);
////////////////////////////////
// Emit