mirror of
https://github.com/Ed94/pikuma_ps1.git
synced 2026-08-25 02:20:33 +00:00
more metaprogram review
This commit is contained in:
@@ -275,6 +275,10 @@ local function _project_emission_inner(root_body_entry, ctx_table)
|
|||||||
local gp0_shape = type(encoder) == "string"
|
local gp0_shape = type(encoder) == "string"
|
||||||
and encoder:match("^mac_format_([%w_]+)_color$")
|
and encoder:match("^mac_format_([%w_]+)_color$")
|
||||||
or nil
|
or nil
|
||||||
|
local is_load = (isa_kind == "load")
|
||||||
|
local is_branch = (isa_kind == "branch")
|
||||||
|
local is_unconditional_jump = (encoder == "jump" or encoder == "call_addr")
|
||||||
|
local is_terminal_jump = (encoder == "jump_reg" or encoder == "call_reg" or encoder == "jump_link")
|
||||||
items[#items + 1] = {
|
items[#items + 1] = {
|
||||||
kind = "word",
|
kind = "word",
|
||||||
encoder = encoder,
|
encoder = encoder,
|
||||||
@@ -291,6 +295,10 @@ local function _project_emission_inner(root_body_entry, ctx_table)
|
|||||||
isa_kind = isa_kind,
|
isa_kind = isa_kind,
|
||||||
nop_words = nop_words,
|
nop_words = nop_words,
|
||||||
is_yield = is_yield,
|
is_yield = is_yield,
|
||||||
|
is_load = is_load,
|
||||||
|
is_branch = is_branch,
|
||||||
|
is_unconditional_jump = is_unconditional_jump,
|
||||||
|
is_terminal_jump = is_terminal_jump,
|
||||||
gp0_shape = gp0_shape,
|
gp0_shape = gp0_shape,
|
||||||
}
|
}
|
||||||
word_events[#word_events + 1] = {
|
word_events[#word_events + 1] = {
|
||||||
@@ -309,6 +317,10 @@ local function _project_emission_inner(root_body_entry, ctx_table)
|
|||||||
kind = isa_kind,
|
kind = isa_kind,
|
||||||
nop_words = nop_words,
|
nop_words = nop_words,
|
||||||
is_yield = is_yield,
|
is_yield = is_yield,
|
||||||
|
is_load = is_load,
|
||||||
|
is_branch = is_branch,
|
||||||
|
is_unconditional_jump = is_unconditional_jump,
|
||||||
|
is_terminal_jump = is_terminal_jump,
|
||||||
gp0_shape = gp0_shape,
|
gp0_shape = gp0_shape,
|
||||||
}
|
}
|
||||||
word_idx = word_idx + 1
|
word_idx = word_idx + 1
|
||||||
|
|||||||
+3
-43
@@ -18,7 +18,7 @@ M.DELAY_MARKERS = {
|
|||||||
["DmaSlot_"] = true,
|
["DmaSlot_"] = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- One row per encoder. Old table names are load-time views (build_isa_views).
|
-- One row per encoder. Read through duffle.instr.
|
||||||
M.INSTRUCTION = {
|
M.INSTRUCTION = {
|
||||||
["BdSlot_"] = { cycles = 0, kind = "marker", },
|
["BdSlot_"] = { cycles = 0, kind = "marker", },
|
||||||
["LdSlot_"] = { cycles = 0, kind = "marker", },
|
["LdSlot_"] = { cycles = 0, kind = "marker", },
|
||||||
@@ -300,7 +300,7 @@ function M.instr (ident) return M.INSTRUCTION [ident] end
|
|||||||
function M.gte_canon(ident) return M.ALIAS_TO_CANONICAL [ident] or ident end
|
function M.gte_canon(ident) return M.ALIAS_TO_CANONICAL [ident] or ident end
|
||||||
function M.gte (ident) return M.GTE_COMMAND[M.gte_canon(ident)] end
|
function M.gte (ident) return M.GTE_COMMAND[M.gte_canon(ident)] end
|
||||||
|
|
||||||
local function build_isa_views()
|
local function build_alias_map()
|
||||||
M.ALIAS_TO_CANONICAL = {}
|
M.ALIAS_TO_CANONICAL = {}
|
||||||
for canon, row in pairs(M.GTE_COMMAND) do
|
for canon, row in pairs(M.GTE_COMMAND) do
|
||||||
M.ALIAS_TO_CANONICAL[canon] = canon
|
M.ALIAS_TO_CANONICAL[canon] = canon
|
||||||
@@ -308,48 +308,8 @@ local function build_isa_views()
|
|||||||
M.ALIAS_TO_CANONICAL[alias] = canon
|
M.ALIAS_TO_CANONICAL[alias] = canon
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
M.INSTRUCTION_LATENCY = {}
|
|
||||||
M.INSTRUCTION_GPR_EFFECTS = {}
|
|
||||||
M.IMMEDIATE_FIELD_WIDTHS = {}
|
|
||||||
M.GPR_VALUE_RULES = {}
|
|
||||||
M.CONTROL_TRANSFER_DELAY_SLOT_POLICIES = {}
|
|
||||||
for name, row in pairs(M.INSTRUCTION) do
|
|
||||||
M.INSTRUCTION_LATENCY[name] = row.cycles
|
|
||||||
if row.reads or row.writes then
|
|
||||||
M.INSTRUCTION_GPR_EFFECTS[name] = {
|
|
||||||
reads = row.reads or {},
|
|
||||||
writes = row.writes or {},
|
|
||||||
}
|
|
||||||
end
|
|
||||||
if row.imm then M.IMMEDIATE_FIELD_WIDTHS[name] = row.imm end
|
|
||||||
if row.value then M.GPR_VALUE_RULES [name] = row.value end
|
|
||||||
if (row.kind == "branch" or row.kind == "jump" or row.kind == "call")
|
|
||||||
and row.delay_slot ~= false then
|
|
||||||
M.CONTROL_TRANSFER_DELAY_SLOT_POLICIES[name] = {
|
|
||||||
family = row.kind,
|
|
||||||
suppress_arg1 = row.suppress_arg1,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
M.GTE_COMMAND_ALIASES = {}
|
|
||||||
M.GTE_COMMAND_INPUTS = {}
|
|
||||||
M.GTE_COMMAND_OUTPUTS = {}
|
|
||||||
M.GTE_COMMAND_LATCH_WINDOWS = {}
|
|
||||||
for canon, row in pairs(M.GTE_COMMAND) do
|
|
||||||
M.GTE_COMMAND_ALIASES [canon] = canon
|
|
||||||
M.INSTRUCTION_LATENCY [canon] = row.cycles
|
|
||||||
M.INSTRUCTION_GPR_EFFECTS[canon] = { reads = {}, writes = {} }
|
|
||||||
for _, alias in ipairs(row.aliases or {}) do
|
|
||||||
M.GTE_COMMAND_ALIASES [alias] = canon
|
|
||||||
M.INSTRUCTION_LATENCY [alias] = row.cycles
|
|
||||||
M.INSTRUCTION_GPR_EFFECTS[alias] = { reads = {}, writes = {} }
|
|
||||||
end
|
|
||||||
M.GTE_COMMAND_INPUTS [canon] = row.inputs
|
|
||||||
M.GTE_COMMAND_OUTPUTS [canon] = row.outputs
|
|
||||||
M.GTE_COMMAND_LATCH_WINDOWS[canon] = row.latch
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
build_isa_views()
|
build_alias_map()
|
||||||
|
|
||||||
|
|
||||||
--- GTE control-register alias groups.
|
--- GTE control-register alias groups.
|
||||||
|
|||||||
@@ -137,23 +137,24 @@ local DIE_SCHEMA = {
|
|||||||
variable = {
|
variable = {
|
||||||
abbrev = ABBREV_VARIABLE,
|
abbrev = ABBREV_VARIABLE,
|
||||||
attrs = {
|
attrs = {
|
||||||
{ form = "string", key = "name" },
|
{ form = "string", key = "name" },
|
||||||
{ form = "ref4", key = "type" },
|
{ form = "exprloc", key = "location" },
|
||||||
{ form = "exprloc", key = "location" },
|
{ form = "ref4", key = "type" },
|
||||||
|
{ form = "data1", key = "external" },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
structure_type = {
|
structure_type = {
|
||||||
abbrev = ABBREV_STRUCT_TYPE,
|
abbrev = ABBREV_STRUCT_TYPE,
|
||||||
attrs = {
|
attrs = {
|
||||||
{ form = "string", key = "name" },
|
{ form = "string", key = "name" },
|
||||||
{ form = "data1", key = "byte_size" },
|
{ form = "udata", key = "byte_size" },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
member = {
|
member = {
|
||||||
abbrev = ABBREV_MEMBER,
|
abbrev = ABBREV_MEMBER,
|
||||||
attrs = {
|
attrs = {
|
||||||
{ form = "string", key = "name" },
|
{ form = "string", key = "name" },
|
||||||
{ form = "data2", key = "data_member_location" },
|
{ form = "udata", key = "data_member_location" },
|
||||||
{ form = "ref4", key = "type" },
|
{ form = "ref4", key = "type" },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -163,6 +164,24 @@ local DIE_SCHEMA = {
|
|||||||
{ form = "ref4", key = "type" },
|
{ form = "ref4", key = "type" },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
bind_var_loclist = {
|
||||||
|
abbrev = ABBREV_BIND_VAR_LOCLIST,
|
||||||
|
attrs = {
|
||||||
|
{ form = "string", key = "name" },
|
||||||
|
{ form = "sec_offset", key = "location" },
|
||||||
|
{ form = "ref4", key = "type" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
inlined_subroutine = {
|
||||||
|
abbrev = ABBREV_INLINED_SUBROUTINE,
|
||||||
|
attrs = {
|
||||||
|
{ form = "ref4", key = "abstract_origin" },
|
||||||
|
{ form = "addr", key = "low_pc" },
|
||||||
|
{ form = "addr", key = "high_pc" },
|
||||||
|
{ form = "udata", key = "call_file" },
|
||||||
|
{ form = "udata", key = "call_line" },
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
-- DWARF5 §7.7.3 loclist opcodes.
|
-- DWARF5 §7.7.3 loclist opcodes.
|
||||||
@@ -1653,7 +1672,7 @@ local function build_inserted_children(main_cu_offset, main_cu_end_excl, atom_ta
|
|||||||
emit(string.char(v))
|
emit(string.char(v))
|
||||||
elseif attr.form == "udata" then
|
elseif attr.form == "udata" then
|
||||||
emit(uleb128(v))
|
emit(uleb128(v))
|
||||||
elseif attr.form == "addr" or attr.form == "ref4" then
|
elseif attr.form == "addr" or attr.form == "ref4" or attr.form == "sec_offset" then
|
||||||
emit(elf_dwarf.write_u32_le(v))
|
emit(elf_dwarf.write_u32_le(v))
|
||||||
elseif attr.form == "data2" then
|
elseif attr.form == "data2" then
|
||||||
emit(elf_dwarf.write_u16_le(v))
|
emit(elf_dwarf.write_u16_le(v))
|
||||||
@@ -1775,10 +1794,11 @@ local function build_inserted_children(main_cu_offset, main_cu_end_excl, atom_ta
|
|||||||
local key = tn .. "|" .. byte_size .. "|" .. encoding
|
local key = tn .. "|" .. byte_size .. "|" .. encoding
|
||||||
if member_base_type_offsets[key] then return member_base_type_offsets[key] end
|
if member_base_type_offsets[key] then return member_base_type_offsets[key] end
|
||||||
local off = next_offset()
|
local off = next_offset()
|
||||||
emit(uleb128(ABBREV_BASE_TYPE))
|
emit_die("base_type", {
|
||||||
emit(tn .. "\0")
|
name = tn,
|
||||||
emit(string.char(byte_size))
|
byte_size = byte_size,
|
||||||
emit(string.char(encoding))
|
encoding = encoding,
|
||||||
|
})
|
||||||
member_base_type_offsets[key] = off
|
member_base_type_offsets[key] = off
|
||||||
return off
|
return off
|
||||||
end
|
end
|
||||||
@@ -1796,49 +1816,44 @@ local function build_inserted_children(main_cu_offset, main_cu_end_excl, atom_ta
|
|||||||
-- Unknown typed view: fall back to a generic 4-byte unsigned base_type to keep the wire valid.
|
-- Unknown typed view: fall back to a generic 4-byte unsigned base_type to keep the wire valid.
|
||||||
-- gdb renders as the typename but `print *ptr` only sees the first 4 bytes.
|
-- gdb renders as the typename but `print *ptr` only sees the first 4 bytes.
|
||||||
local innermost_offset = next_offset()
|
local innermost_offset = next_offset()
|
||||||
emit(uleb128(ABBREV_BASE_TYPE))
|
emit_die("base_type", {
|
||||||
emit(tn .. "\0")
|
name = tn,
|
||||||
emit(string.char(U4_BYTE_SIZE)) -- byte_size = 4 (fallback for unknown types)
|
byte_size = U4_BYTE_SIZE,
|
||||||
emit(string.char(DW_ATE_unsigned)) -- encoding = unsigned
|
encoding = DW_ATE_unsigned,
|
||||||
|
})
|
||||||
local outermost_offset = next_offset()
|
local outermost_offset = next_offset()
|
||||||
emit(uleb128(ABBREV_TYPED_VIEW_POINTER)) -- DW_TAG_pointer_type (abbrev 110; NOT 9)
|
emit_die("pointer_type", { type = ref4_of(innermost_offset) })
|
||||||
emit(elf_dwarf.write_u32_le(ref4_of(innermost_offset)))
|
|
||||||
type_chain_offsets[tn .. "|" .. depth] = outermost_offset
|
type_chain_offsets[tn .. "|" .. depth] = outermost_offset
|
||||||
else
|
else
|
||||||
-- Emit a proper structure_type DIE for this typed view.
|
-- Emit a proper structure_type DIE for this typed view.
|
||||||
local struct_offset = next_offset()
|
local struct_offset = next_offset()
|
||||||
emit(uleb128(ABBREV_STRUCT_TYPE))
|
emit_die("structure_type", {
|
||||||
emit(tn .. "\0") -- DW_AT_name (struct_type has children, no name in abbrev 103 [DW_FORM_string only])
|
name = tn,
|
||||||
emit(uleb128(type_info.byte_size)) -- DW_AT_byte_size (DW_FORM_udata)
|
byte_size = type_info.byte_size,
|
||||||
-- For each member, emit ABBREV_MEMBER (name + data_member_location + type ref4).
|
})
|
||||||
for _, m in ipairs(type_info.members) do
|
for _, m in ipairs(type_info.members) do
|
||||||
emit(uleb128(ABBREV_MEMBER))
|
|
||||||
emit(m.name .. "\0")
|
|
||||||
emit(uleb128(m.offset))
|
|
||||||
-- The member's type is S2 (for v*_S2 family) or S4 (for v*_S4 family), based on the member's byte_size.
|
|
||||||
local member_type_name, member_type_encoding
|
local member_type_name, member_type_encoding
|
||||||
if m.byte_size == 2 then
|
if m.byte_size == 2 then
|
||||||
member_type_name = "S2"
|
member_type_name = "S2"
|
||||||
member_type_encoding = 5 -- DW_ATE_signed = 5
|
member_type_encoding = 5
|
||||||
elseif m.byte_size == 4 then
|
elseif m.byte_size == 4 then
|
||||||
member_type_name = "S4"
|
member_type_name = "S4"
|
||||||
member_type_encoding = 5 -- DW_ATE_signed = 5
|
member_type_encoding = 5
|
||||||
else
|
else
|
||||||
-- Unexpected byte_size; fall back to U4 (unsigned int) since U4 base type is already emitted
|
|
||||||
member_type_name = "U4"
|
member_type_name = "U4"
|
||||||
member_type_encoding = 7 -- DW_ATE_unsigned = 7
|
member_type_encoding = 7
|
||||||
end
|
end
|
||||||
local member_base_off = ensure_member_base_type(member_type_name, m.byte_size, member_type_encoding)
|
local member_base_off = ensure_member_base_type(member_type_name, m.byte_size, member_type_encoding)
|
||||||
emit(elf_dwarf.write_u32_le(ref4_of(member_base_off))) -- DW_AT_type ref4 → base_type
|
emit_die("member", {
|
||||||
|
name = m.name,
|
||||||
|
data_member_location = m.offset,
|
||||||
|
type = ref4_of(member_base_off),
|
||||||
|
})
|
||||||
end
|
end
|
||||||
emit(string.char(DIE_CHILDREN_TERMINATOR)) -- end of structure_type's children (DWARF5 §7.5.3)
|
emit(string.char(DIE_CHILDREN_TERMINATOR))
|
||||||
-- Emit a single DW_TAG_pointer_type (abbrev 110) pointing at the structure_type. We picked 110 over 9 (the duplicated gcc pointer_type) because
|
|
||||||
-- abbrev 9 carries DW_AT_byte_size + DW_AT_type and gdb would misparse our ref4 as those attributes. Abbrev 110 has only DW_AT_type, so a 4-byte ref4 lands cleanly on the target type.
|
|
||||||
-- For depth > 1, we'd chain pointer_type → pointer_type → ... → structure_type; that path isn't exercised today.
|
|
||||||
if depth == 1 then
|
if depth == 1 then
|
||||||
local outermost_offset = next_offset()
|
local outermost_offset = next_offset()
|
||||||
emit(uleb128(ABBREV_TYPED_VIEW_POINTER)) -- DW_TAG_pointer_type (abbrev 110; NOT 9)
|
emit_die("pointer_type", { type = ref4_of(struct_offset) })
|
||||||
emit(elf_dwarf.write_u32_le(ref4_of(struct_offset))) -- DW_AT_type → structure_type
|
|
||||||
type_chain_offsets[tn .. "|" .. depth] = outermost_offset
|
type_chain_offsets[tn .. "|" .. depth] = outermost_offset
|
||||||
else
|
else
|
||||||
error("typed-view: pointer_depth > 1 is not yet supported in this emission path")
|
error("typed-view: pointer_depth > 1 is not yet supported in this emission path")
|
||||||
@@ -1855,12 +1870,12 @@ local function build_inserted_children(main_cu_offset, main_cu_end_excl, atom_ta
|
|||||||
-- Follow the SAME pattern as the typed-views chain above: capture the offset BEFORE the uleb tag
|
-- Follow the SAME pattern as the typed-views chain above: capture the offset BEFORE the uleb tag
|
||||||
-- (this is the ref4 target), emit the DIE bytes, then emit the pointer_type pointing at the offset.
|
-- (this is the ref4 target), emit the DIE bytes, then emit the pointer_type pointing at the offset.
|
||||||
local void_chain_offset = next_offset()
|
local void_chain_offset = next_offset()
|
||||||
emit(uleb128(ABBREV_BASE_TYPE))
|
emit_die("base_type", {
|
||||||
emit("void\0") -- DW_FORM_string (DW_AT_name)
|
name = "void",
|
||||||
emit(string.char(1)) -- DW_FORM_data1 (DW_AT_byte_size = 1; DWARF's "void" base_type)
|
byte_size = 1,
|
||||||
emit(string.char(DW_ATE_unsigned)) -- DW_FORM_data1 (DW_AT_encoding = unsigned; DWARF doesn't define a void encoding but gdb reads the name "void" off the DIE and renders it correctly as `(void *)` when wrapped in a pointer_type)
|
encoding = DW_ATE_unsigned,
|
||||||
emit(uleb128(ABBREV_TYPED_VIEW_POINTER)) -- DW_TAG_pointer_type (abbrev 110; NOT 9; void chain target)
|
})
|
||||||
emit(elf_dwarf.write_u32_le(ref4_of(void_chain_offset))) -- 4-byte ref4: points at the void base_type's tag byte
|
emit_die("pointer_type", { type = ref4_of(void_chain_offset) })
|
||||||
-- type_chain_offsets["void|1"] is what step (f) of the per-RR_<R_Name> chain looks up.
|
-- type_chain_offsets["void|1"] is what step (f) of the per-RR_<R_Name> chain looks up.
|
||||||
type_chain_offsets["void|1"] = void_chain_offset -- both the base_type offset and the pointer_type are emitted consecutively; the OUTERMOST is the pointer_type.
|
type_chain_offsets["void|1"] = void_chain_offset -- both the base_type offset and the pointer_type are emitted consecutively; the OUTERMOST is the pointer_type.
|
||||||
-- The variable's DW_AT_type must reference the pointer_type, not the base_type. Patch below.
|
-- The variable's DW_AT_type must reference the pointer_type, not the base_type. Patch below.
|
||||||
@@ -1876,8 +1891,7 @@ local function build_inserted_children(main_cu_offset, main_cu_end_excl, atom_ta
|
|||||||
-- reusing the pre-emitted base type keeps the wire consistent.
|
-- reusing the pre-emitted base type keeps the wire consistent.
|
||||||
-- Once this chain is registered as `type_chain_offsets["U4|1"]`, step (e) of the per-RR_<R_Name> precedence chain will resolve `atom_type(U4 *)`
|
-- Once this chain is registered as `type_chain_offsets["U4|1"]`, step (e) of the per-RR_<R_Name> precedence chain will resolve `atom_type(U4 *)`
|
||||||
-- declarations on aliases like `R_PrimCursor` and `R_OtBase` to `U4 *` (gdb renders as `(unsigned int *)` with the value displayed in hex).
|
-- declarations on aliases like `R_PrimCursor` and `R_OtBase` to `U4 *` (gdb renders as `(unsigned int *)` with the value displayed in hex).
|
||||||
emit(uleb128(ABBREV_TYPED_VIEW_POINTER)) -- DW_TAG_pointer_type (abbrev 110; NOT 9; U4 chain target)
|
emit_die("pointer_type", { type = ref4_of(base_type_section_offset) })
|
||||||
emit(elf_dwarf.write_u32_le(ref4_of(base_type_section_offset))) -- 4-byte ref4 → "unsigned int" base_type
|
|
||||||
local u4_chain_offset = next_offset() - 5 -- 1 (uleb tag) + 4 (ref4) = 5 bytes; capture the pointer_type's start offset
|
local u4_chain_offset = next_offset() - 5 -- 1 (uleb tag) + 4 (ref4) = 5 bytes; capture the pointer_type's start offset
|
||||||
type_chain_offsets["U4|1"] = u4_chain_offset
|
type_chain_offsets["U4|1"] = u4_chain_offset
|
||||||
|
|
||||||
@@ -1890,18 +1904,12 @@ local function build_inserted_children(main_cu_offset, main_cu_end_excl, atom_ta
|
|||||||
local struct = rbind_structs[binds_name]
|
local struct = rbind_structs[binds_name]
|
||||||
struct_section_offsets[binds_name] = next_offset()
|
struct_section_offsets[binds_name] = next_offset()
|
||||||
|
|
||||||
emit(uleb128(ABBREV_STRUCT_TYPE))
|
emit_die("structure_type", {
|
||||||
emit(binds_name .. "\0") -- DW_FORM_string (DW_AT_name)
|
name = binds_name,
|
||||||
emit(uleb128(struct.bytes)) -- DW_FORM_udata (DW_AT_byte_size)
|
byte_size = struct.bytes,
|
||||||
|
})
|
||||||
|
|
||||||
-- Emit DW_TAG_member children (one per field).
|
|
||||||
for _, field in ipairs(struct.fields) do
|
for _, field in ipairs(struct.fields) do
|
||||||
emit(uleb128(ABBREV_MEMBER))
|
|
||||||
emit(field.name .. "\0") -- DW_FORM_string (DW_AT_name)
|
|
||||||
emit(uleb128(field.offset)) -- DW_FORM_udata (DW_AT_data_member_location)
|
|
||||||
-- typed field:
|
|
||||||
-- For a pointer-typed field, the member's DW_AT_type points at the deepest pointer_type in its chain.
|
|
||||||
-- For U4 (no pointer), it points at the base_type.
|
|
||||||
local field_type_offset
|
local field_type_offset
|
||||||
if field.pointer_depth and field.pointer_depth > 0 then
|
if field.pointer_depth and field.pointer_depth > 0 then
|
||||||
field_type_offset = type_chain_offsets[field.type_name .. "|" .. field.pointer_depth]
|
field_type_offset = type_chain_offsets[field.type_name .. "|" .. field.pointer_depth]
|
||||||
@@ -1909,7 +1917,11 @@ local function build_inserted_children(main_cu_offset, main_cu_end_excl, atom_ta
|
|||||||
if not field_type_offset then
|
if not field_type_offset then
|
||||||
field_type_offset = base_type_section_offset
|
field_type_offset = base_type_section_offset
|
||||||
end
|
end
|
||||||
emit(elf_dwarf.write_u32_le(ref4_of(field_type_offset))) -- DW_FORM_ref4 → type
|
emit_die("member", {
|
||||||
|
name = field.name,
|
||||||
|
data_member_location = field.offset,
|
||||||
|
type = ref4_of(field_type_offset),
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
emit(string.char(DIE_CHILDREN_TERMINATOR)) -- end of structure_type's children (DWARF5 §7.5.3)
|
emit(string.char(DIE_CHILDREN_TERMINATOR)) -- end of structure_type's children (DWARF5 §7.5.3)
|
||||||
@@ -2073,19 +2085,17 @@ local function build_inserted_children(main_cu_offset, main_cu_end_excl, atom_ta
|
|||||||
local alias = by_alias[r_name]
|
local alias = by_alias[r_name]
|
||||||
local rr_name = "RR_" .. strip_r_prefix(r_name)
|
local rr_name = "RR_" .. strip_r_prefix(r_name)
|
||||||
local alias_code = alias.code
|
local alias_code = alias.code
|
||||||
emit(uleb128(ABBREV_VARIABLE))
|
|
||||||
emit(rr_name .. "\0") -- DW_FORM_string (DW_AT_name)
|
|
||||||
-- DW_FORM_exprloc: ULEB byte count + DW_OP_regN byte. DW_OP_reg0..reg31 occupy opcodes 0x50..0x6f; DW_OP_reg15 is 0x5f.
|
|
||||||
-- `alias_code` is the MIPS GPR index (0..31) from the merged register_alias_registry.
|
|
||||||
emit(uleb128(1) .. string.char(DW_OP_reg0 + alias_code)) -- DW_FORM_exprloc (DW_OP_regN from registry code)
|
|
||||||
-- Precedence chain (a..e). Step (f) is the void* fallback initialized below; the chain overrides it when any step yields a non-nil offset.
|
|
||||||
local type_offset = type_chain_offsets["void|1"]
|
local type_offset = type_chain_offsets["void|1"]
|
||||||
for _, step in ipairs(PRECEDENCE_STEPS) do
|
for _, step in ipairs(PRECEDENCE_STEPS) do
|
||||||
local candidate = step(r_name, alias_code)
|
local candidate = step(r_name, alias_code)
|
||||||
if candidate then type_offset = candidate; break end
|
if candidate then type_offset = candidate; break end
|
||||||
end
|
end
|
||||||
emit(elf_dwarf.write_u32_le(ref4_of(type_offset))) -- DW_FORM_ref4 → type
|
emit_die("variable", {
|
||||||
emit(string.char(0x01)) -- DW_AT_external=1 (visible at CU scope)
|
name = rr_name,
|
||||||
|
location = uleb128(1) .. string.char(DW_OP_reg0 + alias_code),
|
||||||
|
type = ref4_of(type_offset),
|
||||||
|
external = 0x01,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If rbind, emit bind_args variable with PC-ranged location list.
|
-- If rbind, emit bind_args variable with PC-ranged location list.
|
||||||
@@ -2095,10 +2105,11 @@ local function build_inserted_children(main_cu_offset, main_cu_end_excl, atom_ta
|
|||||||
if atom.rbind then
|
if atom.rbind then
|
||||||
local binds_name = atom.rbind.binds
|
local binds_name = atom.rbind.binds
|
||||||
local loclists_offset = loclists_offsets[atom.name] or 0
|
local loclists_offset = loclists_offsets[atom.name] or 0
|
||||||
emit(uleb128(ABBREV_BIND_VAR_LOCLIST))
|
emit_die("bind_var_loclist", {
|
||||||
emit("bind_args\0") -- DW_FORM_string (DW_AT_name)
|
name = "bind_args",
|
||||||
emit(elf_dwarf.write_u32_le(loclists_offset)) -- DW_FORM_sec_offset → .debug_loclists
|
location = loclists_offset,
|
||||||
emit(elf_dwarf.write_u32_le(ref4_of(struct_section_offsets[binds_name]))) -- DW_FORM_ref4 → struct_type
|
type = ref4_of(struct_section_offsets[binds_name]),
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Per-component invocation inlined_subroutine instances.
|
-- Per-component invocation inlined_subroutine instances.
|
||||||
@@ -2113,12 +2124,13 @@ local function build_inserted_children(main_cu_offset, main_cu_end_excl, atom_ta
|
|||||||
else
|
else
|
||||||
local inv_low = atom.addr + inv.start_pos * MIPS_BYTES_PER_WORD
|
local inv_low = atom.addr + inv.start_pos * MIPS_BYTES_PER_WORD
|
||||||
local inv_high = atom.addr + (inv.end_pos + 1) * MIPS_BYTES_PER_WORD
|
local inv_high = atom.addr + (inv.end_pos + 1) * MIPS_BYTES_PER_WORD
|
||||||
emit(uleb128(ABBREV_INLINED_SUBROUTINE))
|
emit_die("inlined_subroutine", {
|
||||||
emit(elf_dwarf.write_u32_le(ref4_of(abstract_offsets[inv.component_name]))) -- DW_FORM_ref4 → abstract_origin
|
abstract_origin = ref4_of(abstract_offsets[inv.component_name]),
|
||||||
emit(elf_dwarf.write_u32_le(inv_low)) -- DW_FORM_addr (DW_AT_low_pc)
|
low_pc = inv_low,
|
||||||
emit(elf_dwarf.write_u32_le(inv_high)) -- DW_FORM_addr (DW_AT_high_pc)
|
high_pc = inv_high,
|
||||||
emit(uleb128(resolve_provenance_file_index(inv.call_path))) -- DW_FORM_udata (DW_AT_call_file)
|
call_file = resolve_provenance_file_index(inv.call_path),
|
||||||
emit(uleb128(inv.call_line)) -- DW_FORM_udata (DW_AT_call_line)
|
call_line = inv.call_line,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2357,11 +2357,64 @@ local function parse_enum(source, pos, ident_end, line_of, out)
|
|||||||
return after_brace
|
return after_brace
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function parse_addrs_assign(source, pos, ident_end, line_of, out)
|
||||||
|
local after = duffle.skip_ws_and_cmt(source, ident_end)
|
||||||
|
if source:sub(after, after) ~= "[" then return ident_end end
|
||||||
|
local inner, after_br = duffle.read_brackets(source, after)
|
||||||
|
local idx = inner and tonumber(duffle.trim(inner))
|
||||||
|
after_br = duffle.skip_ws_and_cmt(source, after_br or after)
|
||||||
|
if not (idx and source:sub(after_br, after_br) == "=") then
|
||||||
|
return after_br or (after + 1)
|
||||||
|
end
|
||||||
|
local rhs = duffle.skip_ws_and_cmt(source, after_br + 1)
|
||||||
|
local rhs_ident = duffle.read_ident(source, rhs)
|
||||||
|
if rhs_ident then out._addrs[idx] = rhs_ident end
|
||||||
|
return rhs
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_tb_emit_(source, pos, ident_end, line_of, out)
|
||||||
|
local after = duffle.skip_ws_and_cmt(source, ident_end)
|
||||||
|
if source:sub(after, after) ~= "(" then return ident_end end
|
||||||
|
local inner, after_p = duffle.read_parens(source, after)
|
||||||
|
local name = duffle.trim(inner or ""):match("^([%w_]+)")
|
||||||
|
if name then
|
||||||
|
out._chain = out._chain or {}
|
||||||
|
out._chain[#out._chain + 1] = name
|
||||||
|
end
|
||||||
|
return after_p or (after + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_tb_emit(source, pos, ident_end, line_of, out)
|
||||||
|
local after = duffle.skip_ws_and_cmt(source, ident_end)
|
||||||
|
if source:sub(after, after) ~= "(" then return ident_end end
|
||||||
|
local inner, after_p = duffle.read_parens(source, after)
|
||||||
|
local args = duffle.split_top_level_commas(inner or "")
|
||||||
|
local last = duffle.trim(args[#args] or "")
|
||||||
|
local idx = last:match("^addrs%s*%[%s*(%d+)%s*%]$")
|
||||||
|
local name
|
||||||
|
if idx then
|
||||||
|
name = out._addrs[tonumber(idx)]
|
||||||
|
else
|
||||||
|
name = last:match("([%w_]+)$")
|
||||||
|
end
|
||||||
|
if name then
|
||||||
|
out._chain = out._chain or {}
|
||||||
|
out._chain[#out._chain + 1] = name
|
||||||
|
end
|
||||||
|
return after_p or (after + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local C_STMT_PARSERS = {
|
||||||
|
tb_emit_ = parse_tb_emit_,
|
||||||
|
tb_emit = parse_tb_emit,
|
||||||
|
addrs = parse_addrs_assign,
|
||||||
|
}
|
||||||
|
|
||||||
-- ════════════════════════════════════════════════════════════════════════════
|
-- ════════════════════════════════════════════════════════════════════════════
|
||||||
-- DECL_PARSERS — data-driven construct dispatch (the plex pattern)
|
-- DECL_PARSERS — data-driven construct dispatch (the plex pattern)
|
||||||
-- ════════════════════════════════════════════════════════════════════════════
|
-- ════════════════════════════════════════════════════════════════════════════
|
||||||
-- Each entry maps a leading ident to its parser function. The main scan_source() loop is one line of dispatch:
|
-- Each entry maps a leading ident to its parser function. The main scan_source() loop is one line of dispatch:
|
||||||
-- local parser = DECL_PARSERS[ident]; if parser then pos = parser(...) end
|
-- local parser = DECL_PARSERS[ident] or C_STMT_PARSERS[ident]; if parser then pos = parser(...) end
|
||||||
--
|
--
|
||||||
-- Adding a new construct = 1 row here + 1 parser function above.
|
-- Adding a new construct = 1 row here + 1 parser function above.
|
||||||
|
|
||||||
@@ -2469,51 +2522,9 @@ local function scan_source(source, source_file, code_macros, code_macro_bodies)
|
|||||||
if pos <= src_len then
|
if pos <= src_len then
|
||||||
local ident, ident_end = duffle.read_ident(source, pos)
|
local ident, ident_end = duffle.read_ident(source, pos)
|
||||||
if ident then
|
if ident then
|
||||||
local parser = DECL_PARSERS[ident]
|
local parser = DECL_PARSERS[ident] or C_STMT_PARSERS[ident]
|
||||||
if parser then
|
if parser then
|
||||||
pos = parser(source, pos, ident_end, line_of, out)
|
pos = parser(source, pos, ident_end, line_of, out)
|
||||||
elseif ident == "addrs" then
|
|
||||||
local after = duffle.skip_ws_and_cmt(source, ident_end)
|
|
||||||
if source:sub(after, after) == "[" then
|
|
||||||
local inner, after_br = duffle.read_brackets(source, after)
|
|
||||||
local idx = inner and tonumber(duffle.trim(inner))
|
|
||||||
after_br = duffle.skip_ws_and_cmt(source, after_br or after)
|
|
||||||
if idx and source:sub(after_br, after_br) == "=" then
|
|
||||||
local rhs = duffle.skip_ws_and_cmt(source, after_br + 1)
|
|
||||||
local rhs_ident = duffle.read_ident(source, rhs)
|
|
||||||
if rhs_ident then out._addrs[idx] = rhs_ident end
|
|
||||||
pos = rhs
|
|
||||||
else
|
|
||||||
pos = after_br or (after + 1)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
pos = ident_end
|
|
||||||
end
|
|
||||||
elseif ident == "tb_emit_" or ident == "tb_emit" then
|
|
||||||
local after = duffle.skip_ws_and_cmt(source, ident_end)
|
|
||||||
if source:sub(after, after) == "(" then
|
|
||||||
local inner, after_p = duffle.read_parens(source, after)
|
|
||||||
local name
|
|
||||||
if ident == "tb_emit_" then
|
|
||||||
name = duffle.trim(inner or ""):match("^([%w_]+)")
|
|
||||||
else
|
|
||||||
local args = duffle.split_top_level_commas(inner or "")
|
|
||||||
local last = duffle.trim(args[#args] or "")
|
|
||||||
local idx = last:match("^addrs%s*%[%s*(%d+)%s*%]$")
|
|
||||||
if idx then
|
|
||||||
name = out._addrs[tonumber(idx)]
|
|
||||||
else
|
|
||||||
name = last:match("([%w_]+)$")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if name then
|
|
||||||
out._chain = out._chain or {}
|
|
||||||
out._chain[#out._chain + 1] = name
|
|
||||||
end
|
|
||||||
pos = after_p or (after + 1)
|
|
||||||
else
|
|
||||||
pos = ident_end
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
-- Unsupported identifiers follow the unrelated-token path. If a
|
-- Unsupported identifiers follow the unrelated-token path. If a
|
||||||
-- pending marker is still open, consume it so it cannot drift to a
|
-- pending marker is still open, consume it so it cannot drift to a
|
||||||
|
|||||||
@@ -186,8 +186,8 @@ end
|
|||||||
-- ONE forward pass over the token list produces a flat table of per-token classifications.
|
-- ONE forward pass over the token list produces a flat table of per-token classifications.
|
||||||
-- Every check + analyze_atom_paths reads from this table instead of re-scanning the token strings.
|
-- Every check + analyze_atom_paths reads from this table instead of re-scanning the token strings.
|
||||||
--
|
--
|
||||||
-- The classification is stored on `atom.paths.tok_class` as an array indexed by token index (1..#tokens).
|
-- Classification fields are stamped onto each `atom.paths.tokens[i]` record.
|
||||||
-- Each entry has:
|
-- Each token has:
|
||||||
-- ident — the leading identifier (e.g. "load_word", "gte_cmdw_rtpt", "nop", "mac_yield")
|
-- ident — the leading identifier (e.g. "load_word", "gte_cmdw_rtpt", "nop", "mac_yield")
|
||||||
-- nop_words — 0 / 1 / 2 (for "nop" / "nop2" / anything else)
|
-- nop_words — 0 / 1 / 2 (for "nop" / "nop2" / anything else)
|
||||||
-- nop_prefix — consecutive nop words ending just BEFORE this token (forward-pass pre-compute; makes preceding-nop lookup O(N))
|
-- nop_prefix — consecutive nop words ending just BEFORE this token (forward-pass pre-compute; makes preceding-nop lookup O(N))
|
||||||
@@ -202,8 +202,8 @@ end
|
|||||||
-- load_byte_u, gte_lw, gte_lwc2. These all have MIPS load-delay semantics (the destination register is volatile for 1 word after the load).
|
-- load_byte_u, gte_lw, gte_lwc2. These all have MIPS load-delay semantics (the destination register is volatile for 1 word after the load).
|
||||||
-- is_store_word — true if this token starts with `store_word(`
|
-- is_store_word — true if this token starts with `store_word(`
|
||||||
--
|
--
|
||||||
-- Checks that need the leading ident use `tok_class.ident` instead of re-matching the token string.
|
-- Checks that need the leading ident use `token.ident` instead of re-matching the token string.
|
||||||
-- Checks that need "how many nops before token i" use `tok_class.nop_prefix` instead of walking backwards.
|
-- Checks that need "how many nops before token i" use `token.nop_prefix` instead of walking backwards.
|
||||||
|
|
||||||
--- @class TokClass
|
--- @class TokClass
|
||||||
--- @field ident string -- lLading identifier
|
--- @field ident string -- lLading identifier
|
||||||
@@ -245,12 +245,10 @@ local BRANCH_PATTERN = "^branch_[%w_]+%s*%("
|
|||||||
-- This keeps `consuming_encoder` canonical for any downstream tooling that consults the metadata field.
|
-- This keeps `consuming_encoder` canonical for any downstream tooling that consults the metadata field.
|
||||||
local JUMP_REL_PATTERN = "^jump_rel%s*%("
|
local JUMP_REL_PATTERN = "^jump_rel%s*%("
|
||||||
|
|
||||||
local function tok_class_view(tokens)
|
local function stamp_token_fields(tokens)
|
||||||
local n = #tokens
|
|
||||||
local tc = {}
|
|
||||||
local nop_run = 0 -- running count of consecutive nop words (forward pass)
|
local nop_run = 0 -- running count of consecutive nop words (forward pass)
|
||||||
for tok_idx, t in ipairs(tokens) do
|
for _, t in ipairs(tokens) do
|
||||||
local tok = t.tok
|
local tok = t.tok or ""
|
||||||
local ident = tok:match("^([%w_]+)") or "?"
|
local ident = tok:match("^([%w_]+)") or "?"
|
||||||
local is_delay_marker = false
|
local is_delay_marker = false
|
||||||
local delay_marker = nil
|
local delay_marker = nil
|
||||||
@@ -327,36 +325,33 @@ local function tok_class_view(tokens)
|
|||||||
if tok:find("R_TapePtr", 1, true) then reads_r_tape_ptr = true end
|
if tok:find("R_TapePtr", 1, true) then reads_r_tape_ptr = true end
|
||||||
if is_store_word and tok:find("R_PrimCursor", 1, true) then writes_r_prim_cursor = true end
|
if is_store_word and tok:find("R_PrimCursor", 1, true) then writes_r_prim_cursor = true end
|
||||||
|
|
||||||
tc[tok_idx] = {
|
t.ident = ident
|
||||||
ident = ident,
|
t.is_delay_marker = is_delay_marker
|
||||||
is_delay_marker = is_delay_marker,
|
t.delay_marker = delay_marker
|
||||||
delay_marker = delay_marker,
|
t.nop_words = nop_words
|
||||||
nop_words = nop_words,
|
t.nop_prefix = nop_run
|
||||||
nop_prefix = nop_run,
|
t.is_yield = is_yield
|
||||||
is_yield = is_yield,
|
t.is_atom_label = is_atom_label
|
||||||
is_atom_label = is_atom_label,
|
t.label_name = label_name
|
||||||
label_name = label_name,
|
t.is_branch = is_branch
|
||||||
is_branch = is_branch,
|
t.is_unconditional_jump = is_unconditional_jump
|
||||||
is_unconditional_jump = is_unconditional_jump,
|
t.is_terminal_jump = is_terminal_jump
|
||||||
is_terminal_jump = is_terminal_jump,
|
t.branch_label = branch_label
|
||||||
branch_label = branch_label,
|
t.is_load = is_load
|
||||||
is_load = is_load,
|
t.is_store_word = is_store_word
|
||||||
is_store_word = is_store_word,
|
t.mac_format_shape = mac_format_shape
|
||||||
mac_format_shape = mac_format_shape,
|
t.is_gte_store = is_gte_store
|
||||||
is_gte_store = is_gte_store,
|
t.is_ot_tag = is_ot_tag
|
||||||
is_ot_tag = is_ot_tag,
|
t.writes_r_prim_cursor = writes_r_prim_cursor
|
||||||
writes_r_prim_cursor = writes_r_prim_cursor,
|
t.reads_r_tape_ptr = reads_r_tape_ptr
|
||||||
reads_r_tape_ptr = reads_r_tape_ptr,
|
t.o_arg1 = o_arg1
|
||||||
o_arg1 = o_arg1,
|
t.o_arg2 = o_arg2
|
||||||
o_arg2 = o_arg2,
|
t.s_arg1 = s_arg1
|
||||||
s_arg1 = s_arg1,
|
|
||||||
}
|
|
||||||
-- Advance the nop run for the NEXT token.
|
-- Advance the nop run for the NEXT token.
|
||||||
if nop_words > 0 then nop_run = nop_run + nop_words
|
if nop_words > 0 then nop_run = nop_run + nop_words
|
||||||
else nop_run = 0
|
else nop_run = 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return tc
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ════════════════════════════════════════════════════════════════════════════
|
-- ════════════════════════════════════════════════════════════════════════════
|
||||||
@@ -1622,7 +1617,7 @@ local function check_mac_yield_uniformity(atom, pipe_ctx, findings)
|
|||||||
-- The GTE pipeline-fill check applies to all 3 kinds (see check_gte_pipeline_fill). Only the mac_yield rule branches on kind.
|
-- The GTE pipeline-fill check applies to all 3 kinds (see check_gte_pipeline_fill). Only the mac_yield rule branches on kind.
|
||||||
local tokens = atom.paths.tokens
|
local tokens = atom.paths.tokens
|
||||||
local line_in_body = atom.paths.line_in_body
|
local line_in_body = atom.paths.line_in_body
|
||||||
local tc = atom.paths.tok_class
|
local tc = tokens
|
||||||
local n = #tokens
|
local n = #tokens
|
||||||
|
|
||||||
local count = 0
|
local count = 0
|
||||||
@@ -1720,7 +1715,7 @@ local function check_yield_load_tail_pairing(atom, _pipe_ctx, findings)
|
|||||||
|
|
||||||
local tokens = atom.paths.tokens
|
local tokens = atom.paths.tokens
|
||||||
local line_in_body = atom.paths.line_in_body
|
local line_in_body = atom.paths.line_in_body
|
||||||
local tc = atom.paths.tok_class
|
local tc = tokens
|
||||||
local n = #tokens
|
local n = #tokens
|
||||||
|
|
||||||
local function line_for(idx)
|
local function line_for(idx)
|
||||||
@@ -1913,7 +1908,7 @@ local function check_abi_handoff(atom, pipe_ctx, findings)
|
|||||||
end
|
end
|
||||||
local tokens = atom.paths.tokens
|
local tokens = atom.paths.tokens
|
||||||
local line_in_body = atom.paths.line_in_body
|
local line_in_body = atom.paths.line_in_body
|
||||||
local tc = atom.paths.tok_class
|
local tc = tokens
|
||||||
local found_field_set = {}
|
local found_field_set = {}
|
||||||
local found_advance = false
|
local found_advance = false
|
||||||
|
|
||||||
@@ -1982,7 +1977,7 @@ local function check_gpu_portstore_shape(atom, pipe_ctx, findings)
|
|||||||
if atom.kind ~= "atom" and atom.kind ~= "atom_proc" then return end
|
if atom.kind ~= "atom" and atom.kind ~= "atom_proc" then return end
|
||||||
local tokens = atom.paths.tokens
|
local tokens = atom.paths.tokens
|
||||||
local line_in_body = atom.paths.line_in_body
|
local line_in_body = atom.paths.line_in_body
|
||||||
local tc = atom.paths.tok_class
|
local tc = tokens
|
||||||
local cmd_byte = nil
|
local cmd_byte = nil
|
||||||
local cmd_line = nil
|
local cmd_line = nil
|
||||||
local contrib = 0
|
local contrib = 0
|
||||||
@@ -2119,7 +2114,8 @@ end
|
|||||||
--- plus `mac_*` idents whose bare name is missing from `pipe_ctx.components_by_name` (i.e. no `MipsAtomComp_` for it).
|
--- plus `mac_*` idents whose bare name is missing from `pipe_ctx.components_by_name` (i.e. no `MipsAtomComp_` for it).
|
||||||
local function analyze_atom_paths(atom, pipe_ctx)
|
local function analyze_atom_paths(atom, pipe_ctx)
|
||||||
local tokens = atom.paths.tokens or duffle.tokenize_body(atom.body)
|
local tokens = atom.paths.tokens or duffle.tokenize_body(atom.body)
|
||||||
local tc = atom.paths.tok_class or tok_class_view(tokens)
|
if tokens[1] and tokens[1].ident == nil then stamp_token_fields(tokens) end
|
||||||
|
local tc = tokens
|
||||||
local n = #tokens
|
local n = #tokens
|
||||||
|
|
||||||
-- Build label + branch maps from the pre-computed classification (no re-scan).
|
-- Build label + branch maps from the pre-computed classification (no re-scan).
|
||||||
@@ -2437,8 +2433,8 @@ end
|
|||||||
-- The check also flags fields whose Type has no `fields` table (typedefs and enums don't have fields — any Field reference against them is bogus)
|
-- The check also flags fields whose Type has no `fields` table (typedefs and enums don't have fields — any Field reference against them is bogus)
|
||||||
-- and fields whose name doesn't appear in the resolved Type's fields array.
|
-- and fields whose name doesn't appear in the resolved Type's fields array.
|
||||||
--
|
--
|
||||||
-- Walks every atom's pre-computed `paths.tok_class`
|
-- Walks every atom's stamped `paths.tokens`
|
||||||
-- (set by `classify_tokens` once per atom in validate()) and uses the `o_arg1` / `o_arg2` captures instead of re-matching the token string.
|
-- and uses the `o_arg1` / `o_arg2` captures instead of re-matching the token string.
|
||||||
-- Resolution consults `pipe_ctx.type_name_registry`
|
-- Resolution consults `pipe_ctx.type_name_registry`
|
||||||
-- (Binds_* structs are registered there by scan_source's `register_struct_type`, so a unified lookup works for both Binds_* and non-Binds structs).
|
-- (Binds_* structs are registered there by scan_source's `register_struct_type`, so a unified lookup works for both Binds_* and non-Binds structs).
|
||||||
--
|
--
|
||||||
@@ -2482,8 +2478,8 @@ end
|
|||||||
local function check_binds_no_substruct_deref(_src, pipe_ctx, findings)
|
local function check_binds_no_substruct_deref(_src, pipe_ctx, findings)
|
||||||
local type_registry = pipe_ctx.type_name_registry or {}
|
local type_registry = pipe_ctx.type_name_registry or {}
|
||||||
for _, a in ipairs(pipe_ctx.atoms or {}) do
|
for _, a in ipairs(pipe_ctx.atoms or {}) do
|
||||||
local tc = a.paths and a.paths.tok_class or {}
|
|
||||||
local tokens = a.paths and a.paths.tokens or {}
|
local tokens = a.paths and a.paths.tokens or {}
|
||||||
|
local tc = tokens
|
||||||
local line_in_body = a.paths and a.paths.line_in_body or {}
|
local line_in_body = a.paths and a.paths.line_in_body or {}
|
||||||
for ti = 1, #tokens do
|
for ti = 1, #tokens do
|
||||||
local tc_entry = tc[ti]
|
local tc_entry = tc[ti]
|
||||||
@@ -2609,7 +2605,7 @@ local function check_gte_cr_alias_writes(atom, pipe_ctx, findings)
|
|||||||
if not next(groups) then return end
|
if not next(groups) then return end
|
||||||
|
|
||||||
local tokens = atom.paths and atom.paths.tokens or {}
|
local tokens = atom.paths and atom.paths.tokens or {}
|
||||||
local tc = atom.paths and atom.paths.tok_class or {}
|
local tc = tokens
|
||||||
local line_in_body = atom.paths and atom.paths.line_in_body
|
local line_in_body = atom.paths and atom.paths.line_in_body
|
||||||
if not next(tokens) then return end
|
if not next(tokens) then return end
|
||||||
|
|
||||||
@@ -2669,7 +2665,7 @@ end
|
|||||||
-- The bare macro IS the right call for the canonical libgte outer-product convention, so this is an opt-out hint rather than a hard warning.
|
-- The bare macro IS the right call for the canonical libgte outer-product convention, so this is an opt-out hint rather than a hard warning.
|
||||||
local function check_rtdiagonal_completeness(atom, _pipe_ctx, findings)
|
local function check_rtdiagonal_completeness(atom, _pipe_ctx, findings)
|
||||||
local tokens = atom.paths and atom.paths.tokens or {}
|
local tokens = atom.paths and atom.paths.tokens or {}
|
||||||
local tc = atom.paths and atom.paths.tok_class or {}
|
local tc = tokens
|
||||||
local line_in_body = atom.paths and atom.paths.line_in_body
|
local line_in_body = atom.paths and atom.paths.line_in_body
|
||||||
if not next(tokens) then return end
|
if not next(tokens) then return end
|
||||||
local strict = os.getenv("GTE_RT_DIAGONAL_STRICT") == "1"
|
local strict = os.getenv("GTE_RT_DIAGONAL_STRICT") == "1"
|
||||||
@@ -2702,7 +2698,7 @@ end
|
|||||||
-- Severity: info. The convention is correct; this is a documentation-pointer check.
|
-- Severity: info. The convention is correct; this is a documentation-pointer check.
|
||||||
local function check_gte_cr_TR_naming(atom, _pipe_ctx, findings)
|
local function check_gte_cr_TR_naming(atom, _pipe_ctx, findings)
|
||||||
local tokens = atom.paths and atom.paths.tokens or {}
|
local tokens = atom.paths and atom.paths.tokens or {}
|
||||||
local tc = atom.paths and atom.paths.tok_class or {}
|
local tc = tokens
|
||||||
local line_in_body = atom.paths and atom.paths.line_in_body
|
local line_in_body = atom.paths and atom.paths.line_in_body
|
||||||
if not next(tokens) then return end
|
if not next(tokens) then return end
|
||||||
local touched = false
|
local touched = false
|
||||||
@@ -3275,7 +3271,7 @@ local function validate(ctx, src, corpus_pipe_ctx)
|
|||||||
--- Body, token, and emission projections come from here (`paths.tokens = body_tokens`, `paths.line_in_body = build_body_line_index` `paths.word_events`
|
--- Body, token, and emission projections come from here (`paths.tokens = body_tokens`, `paths.line_in_body = build_body_line_index` `paths.word_events`
|
||||||
--- and related fields are owned by `passes/emission_model.lua` pass (per-atom emission projection).
|
--- and related fields are owned by `passes/emission_model.lua` pass (per-atom emission projection).
|
||||||
--- This pass reads: `paths.tokens`, `paths.line_in_body`, `paths.items`, `paths.word_events` from the emitted projection,
|
--- This pass reads: `paths.tokens`, `paths.line_in_body`, `paths.items`, `paths.word_events` from the emitted projection,
|
||||||
--- then computes `paths.tok_class`, `paths.cycles_min/max`, `paths.branches`, `paths.paths`, `paths.has_loops`, `paths.unknown_macros` via `classify_tokens` + `analyze_atom_paths`.
|
--- then stamps token fields and computes `paths.cycles_min/max`, `paths.branches`, `paths.paths`, `paths.has_loops`, `paths.unknown_macros` via `stamp_token_fields` + `analyze_atom_paths`.
|
||||||
--- No re-walk of body text or body_tokens happens here.
|
--- No re-walk of body text or body_tokens happens here.
|
||||||
---
|
---
|
||||||
--- Canonical contract: `atom.paths` and `atom.paths.word_events` MUST be populated by `passes/emission_model.run(ctx)` before this pass runs.
|
--- Canonical contract: `atom.paths` and `atom.paths.word_events` MUST be populated by `passes/emission_model.run(ctx)` before this pass runs.
|
||||||
@@ -3291,7 +3287,7 @@ local function validate(ctx, src, corpus_pipe_ctx)
|
|||||||
-- `paths.tokens` / `paths.line_in_body` / `paths.items` / `paths.word_events` are populated by `passes/emission_model.lua`.
|
-- `paths.tokens` / `paths.line_in_body` / `paths.items` / `paths.word_events` are populated by `passes/emission_model.lua`.
|
||||||
-- Supply tokens when no emission projection is present.
|
-- Supply tokens when no emission projection is present.
|
||||||
if a.paths.tokens == nil then a.paths.tokens = a.body_tokens end
|
if a.paths.tokens == nil then a.paths.tokens = a.body_tokens end
|
||||||
a.paths.tok_class = tok_class_view(a.paths.tokens)
|
stamp_token_fields(a.paths.tokens)
|
||||||
|
|
||||||
-- analyze_atom_paths fills the *cycles / branches / has_loops / unknown_macros* fields of a.paths.
|
-- analyze_atom_paths fills the *cycles / branches / has_loops / unknown_macros* fields of a.paths.
|
||||||
analyze_atom_paths(a, pipe_ctx)
|
analyze_atom_paths(a, pipe_ctx)
|
||||||
|
|||||||
Reference in New Issue
Block a user