mirror of
https://github.com/Ed94/pikuma_ps1.git
synced 2026-07-25 08:57:51 +00:00
lua metaprogram: Delete dead code, some more lifting to duffle
This commit is contained in:
@@ -22,10 +22,10 @@
|
||||
|
||||
-- Bootstrap: load `scripts/duffle_paths.lua` (sets package.path + package.cpath).
|
||||
-- Uses `debug.getinfo` to find this file's own directory, so it works both standalone and when require'd from the orchestrator.
|
||||
local _src = debug.getinfo(1, "S").source:sub(2)
|
||||
local _dir = _src:match("(.*[/\\])") or "./"
|
||||
dofile(_dir .. "../duffle_paths.lua")
|
||||
local duffle = require("duffle")
|
||||
-- Bootstrap: load `duffle_paths.lua` via `debug.getinfo(1, "S").source` (works both standalone + when require'd).
|
||||
-- duffle_paths.lua sets package.path then returns `require("duffle")` at the bottom, so the dofile value IS the duffle module.
|
||||
local _bootstrap_dir = debug.getinfo(1, "S").source:match("^@?(.*[/\\])") or "./"
|
||||
local duffle = dofile(_bootstrap_dir .. "../duffle_paths.lua")
|
||||
|
||||
-- ════════════════════════════════════════════════════════════════════════════
|
||||
-- Constants
|
||||
@@ -392,197 +392,6 @@ local function check_mac_yield_uniformity(atoms, findings)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ════════════════════════════════════════════════════════════════════════════
|
||||
-- Source walkers: Binds_* structs + per-atom atom_info
|
||||
-- ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
-- Parse the U4 fields from a Binds_X body. Returns (fields, byte_count).
|
||||
local function parse_binds_fields(body)
|
||||
local fields = {}
|
||||
local byte_off = 0
|
||||
local body_pos = 1
|
||||
while body_pos <= #body do
|
||||
body_pos = duffle.skip_ws_and_cmt(body, body_pos)
|
||||
if body_pos > #body then break end
|
||||
local type_ident, type_end = duffle.read_ident(body, body_pos)
|
||||
if not type_ident then
|
||||
body_pos = body_pos + 1
|
||||
elseif type_ident == "U4" then
|
||||
local field_ident, field_end = duffle.read_ident(body, duffle.skip_ws_and_cmt(body, type_end))
|
||||
if field_ident then
|
||||
fields[#fields + 1] = { name = field_ident, offset = byte_off }
|
||||
byte_off = byte_off + 4
|
||||
end
|
||||
body_pos = field_end or (type_end + 1)
|
||||
else
|
||||
body_pos = type_end + 1
|
||||
end
|
||||
end
|
||||
return fields, byte_off
|
||||
end
|
||||
|
||||
--- Walk source-as-written, return a list of `{line, name, fields, bytes}` for every `typedef Struct_(Binds_X) { ... };` declaration.
|
||||
--- Only U4 fields are tracked (Binds_* are always word arrays in this codebase pointers stored as U4, indices as U4, etc.).
|
||||
--- Mirrors annotation.lua :: find_binds_structs but is independent (no shared cross-pass state for static-analysis; each pass re-walks source).
|
||||
local function find_binds_structs(source_text)
|
||||
local line_of = duffle.LineIndex(source_text)
|
||||
local out = {}
|
||||
local src_len = #source_text
|
||||
local pos = 1
|
||||
while pos <= src_len do
|
||||
pos = duffle.skip_ws_and_cmt(source_text, pos)
|
||||
if pos > src_len then break end
|
||||
|
||||
local pp_pos = duffle.skip_preprocessor_line(source_text, pos)
|
||||
if pp_pos then pos = pp_pos; goto continue end
|
||||
|
||||
local ident, ident_end = duffle.read_ident(source_text, pos)
|
||||
-- scan: <ident>
|
||||
if not ident then pos = pos + 1; goto continue end
|
||||
if ident ~= "typedef" then pos = ident_end; goto continue end
|
||||
|
||||
local after_typedef = duffle.skip_ws_and_cmt(source_text, ident_end)
|
||||
local id2, id2_end = duffle.read_ident(source_text, after_typedef)
|
||||
-- scan: typedef <id2>
|
||||
if id2 ~= "Struct_" then pos = id2_end or (after_typedef + 1); goto continue end
|
||||
|
||||
local open_paren = duffle.skip_ws_and_cmt(source_text, id2_end)
|
||||
if source_text:sub(open_paren, open_paren) ~= "(" then pos = open_paren + 1; goto continue end
|
||||
|
||||
local inner, after_paren = duffle.read_parens(source_text, open_paren)
|
||||
-- scan: typedef Struct_(<name>)
|
||||
local name = duffle.trim(inner)
|
||||
local brace = duffle.scan_to_char(source_text, "{", after_paren)
|
||||
-- scan: typedef Struct_(<name>) {
|
||||
if not brace then pos = open_paren + 1; goto continue end
|
||||
|
||||
local body, after_brace = duffle.read_braces(source_text, brace)
|
||||
-- scan: typedef Struct_(<name>) { <fields> }
|
||||
local fields, byte_off = parse_binds_fields(body)
|
||||
if name:sub(1, 6) == "Binds_" then
|
||||
out[#out + 1] = { line = line_of(pos), name = name, fields = fields, bytes = byte_off }
|
||||
end
|
||||
pos = after_brace
|
||||
|
||||
::continue::
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
-- Parse the register list from inside `atom_reads(...)` or `atom_writes(...)`.
|
||||
local function parse_reg_list(sub_inner)
|
||||
local regs = {}
|
||||
local sub_inner_pos = 1
|
||||
while sub_inner_pos <= #sub_inner do
|
||||
sub_inner_pos = duffle.skip_ws_and_cmt(sub_inner, sub_inner_pos)
|
||||
if sub_inner_pos > #sub_inner then break end
|
||||
local reg_ident, reg_end = duffle.read_ident(sub_inner, sub_inner_pos)
|
||||
if reg_ident then
|
||||
regs[#regs + 1] = duffle.trim(reg_ident)
|
||||
sub_inner_pos = reg_end
|
||||
else
|
||||
sub_inner_pos = sub_inner_pos + 1
|
||||
end
|
||||
if sub_inner_pos > #sub_inner then break end
|
||||
if sub_inner:sub(sub_inner_pos, sub_inner_pos) == "," then sub_inner_pos = sub_inner_pos + 1 end
|
||||
end
|
||||
return regs
|
||||
end
|
||||
|
||||
-- Parse the sub-calls inside `atom_info(atom_bind(...), atom_reads(...), atom_writes(...))`.
|
||||
-- Returns (binds, reads, writes).
|
||||
local function parse_atom_info_subcalls(info_inner)
|
||||
local binds, reads, writes = nil, nil, nil
|
||||
local sub_pos = 1
|
||||
while sub_pos <= #info_inner do
|
||||
sub_pos = duffle.skip_ws_and_cmt(info_inner, sub_pos)
|
||||
if sub_pos > #info_inner then break end
|
||||
local sub_ident, sub_end = duffle.read_ident(info_inner, sub_pos)
|
||||
if not sub_ident then
|
||||
sub_pos = sub_pos + 1
|
||||
elseif sub_ident == "atom_bind" then
|
||||
local sub_open = duffle.skip_ws_and_cmt(info_inner, sub_end)
|
||||
if info_inner:sub(sub_open, sub_open) == "(" then
|
||||
local sub_inner, sub_after2 = duffle.read_parens(info_inner, sub_open)
|
||||
-- scan: atom_bind(<Binds_X>)
|
||||
binds = duffle.trim(sub_inner)
|
||||
sub_pos = sub_after2
|
||||
else
|
||||
sub_pos = sub_open + 1
|
||||
end
|
||||
elseif sub_ident == "atom_reads" or sub_ident == "atom_writes" then
|
||||
local kind = sub_ident
|
||||
local sub_open = duffle.skip_ws_and_cmt(info_inner, sub_end)
|
||||
if info_inner:sub(sub_open, sub_open) == "(" then
|
||||
local sub_inner, sub_after2 = duffle.read_parens(info_inner, sub_open)
|
||||
-- scan: atom_reads(<regs>) OR atom_writes(<regs>)
|
||||
local regs = parse_reg_list(sub_inner)
|
||||
if kind == "atom_reads" then reads = regs else writes = regs end
|
||||
sub_pos = sub_after2
|
||||
else
|
||||
sub_pos = sub_open + 1
|
||||
end
|
||||
else
|
||||
sub_pos = sub_end
|
||||
end
|
||||
end
|
||||
return binds, reads, writes
|
||||
end
|
||||
|
||||
--- For every `MipsAtom_(name)` followed by `atom_info(...)`, parse the atom_info's `atom_bind(Binds_X)`, `atom_reads(...)`, and `atom_writes(...)` sub-calls.
|
||||
--- Returns a list of `{atom_name, binds, reads, writes, info_line}` for use by check_abi_handoff.
|
||||
local function find_atom_info(source_text)
|
||||
local line_of = duffle.LineIndex(source_text)
|
||||
local out = {}
|
||||
local src_len = #source_text
|
||||
local pos = 1
|
||||
while pos <= src_len do
|
||||
pos = duffle.skip_ws_and_cmt(source_text, pos)
|
||||
if pos > src_len then break end
|
||||
|
||||
local pp_pos = duffle.skip_preprocessor_line(source_text, pos)
|
||||
if pp_pos then pos = pp_pos; goto continue end
|
||||
|
||||
local ident, ident_end = duffle.read_ident(source_text, pos)
|
||||
-- scan: <ident>
|
||||
if not ident then pos = pos + 1; goto continue end
|
||||
if ident ~= "MipsAtom_" then pos = ident_end; goto continue end
|
||||
|
||||
local open_paren = duffle.skip_ws_and_cmt(source_text, ident_end)
|
||||
if source_text:sub(open_paren, open_paren) ~= "(" then pos = open_paren + 1; goto continue end
|
||||
|
||||
local inner, after_paren = duffle.read_parens(source_text, open_paren)
|
||||
-- scan: MipsAtom_(<name>)
|
||||
local name_start = 1
|
||||
while name_start <= #inner and inner:sub(name_start, name_start):match("[%s]") do name_start = name_start + 1 end
|
||||
local name_end = name_start
|
||||
while name_end <= #inner and inner:sub(name_end, name_end):match("[%w_]") do name_end = name_end + 1 end
|
||||
local atom_name = inner:sub(name_start, name_end - 1)
|
||||
|
||||
local lookahead = duffle.skip_ws_and_cmt(source_text, after_paren)
|
||||
local look_ident, look_end = duffle.read_ident(source_text, lookahead)
|
||||
-- scan: MipsAtom_(<name>) <look_ident>
|
||||
if look_ident ~= "atom_info" then pos = after_paren; goto continue end
|
||||
|
||||
local info_open = duffle.skip_ws_and_cmt(source_text, look_end)
|
||||
if source_text:sub(info_open, info_open) ~= "(" then pos = info_open + 1; goto continue end
|
||||
|
||||
local info_inner, info_after = duffle.read_parens(source_text, info_open)
|
||||
-- scan: MipsAtom_(<name>) atom_info(<binds>, <reads>, <writes>)
|
||||
local binds, reads, writes = parse_atom_info_subcalls(info_inner)
|
||||
out[#out + 1] = {
|
||||
atom_name = atom_name, binds = binds,
|
||||
reads = reads or {}, writes = writes or {},
|
||||
info_line = line_of(lookahead),
|
||||
}
|
||||
pos = info_after
|
||||
|
||||
::continue::
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
-- ════════════════════════════════════════════════════════════════════════════
|
||||
-- Check #3: ABI handoff discipline
|
||||
-- ════════════════════════════════════════════════════════════════════════════
|
||||
@@ -1275,13 +1084,9 @@ function M.run(ctx)
|
||||
-- Aggregate per-DIRECTORY (per-module). One static_analysis.txt per source-directory, emitted only if the directory contains at least one atom.
|
||||
-- Empty-source directories (e.g. duffle headers with no atoms) produce no report.
|
||||
--
|
||||
-- Group sources by `src.dir`. The first component of `dir` is the module name (e.g. "code/duffle" -> "duffle", "code/gte_hello" -> "gte_hello").
|
||||
-- Group sources by `src.dir`. The first component of `dir` is the module name (e.g. "code/duffle" -> "duffle", "code/gte_hello" -> "gte_hello").
|
||||
-- Output path is `<out_root>/<module_basename>.static_analysis.txt`.
|
||||
local by_dir = {}
|
||||
for _, src in ipairs(ctx.sources) do
|
||||
by_dir[src.dir] = by_dir[src.dir] or {}
|
||||
table.insert(by_dir[src.dir], src)
|
||||
end
|
||||
local by_dir = duffle.group_sources_by_dir(ctx.sources)
|
||||
|
||||
for dir, dir_sources in pairs(by_dir) do
|
||||
-- Run validate() against every source in this directory; accumulate atoms / findings / errors / warnings.
|
||||
|
||||
Reference in New Issue
Block a user