almost ready for static analysis additions

This commit is contained in:
2026-07-09 19:48:02 -04:00
parent 59903546d7
commit c9b7f8c08b
4 changed files with 96 additions and 21 deletions
+8 -3
View File
@@ -287,7 +287,7 @@ function build-graphis_hello {
$src_asm_crt = join-path $path_nugget_common 'crt0/crt0.s'
$module_asm_crt = join-path $path_build 'crt0.o'
# assemble-unit $src_asm_crt $module_asm_crt $includes $assemble_args
assemble-unit $src_asm_crt $module_asm_crt $includes $assemble_args
$src_asm = join-path $path_module 'hello_gpu.s'
$module_asm = join-path $path_build 'hello_gpu.o'
@@ -316,6 +316,7 @@ function build-graphis_hello {
make-binary $elf $exe
}
# build-graphis_hello
# ps1-meta orchestrator. Replaces generate-TapeAtomOffsets +
# generate-TapeAtomAnnotations with a single invocation. Dispatches
# the 6 passes (word-counts / components / annotation / offsets /
@@ -375,7 +376,7 @@ function build-gte_hello {
$src_asm_crt = join-path $path_nugget_common 'crt0/crt0.s'
$module_asm_crt = join-path $path_build 'crt0.o'
# assemble-unit $src_asm_crt $module_asm_crt $includes $assemble_args
assemble-unit $src_asm_crt $module_asm_crt $includes $assemble_args
# $src_asm = join-path $path_module 'hello_gte.s'
# $module_asm = join-path $path_build 'hello_gte.o'
@@ -400,7 +401,11 @@ function build-gte_hello {
$link_args = @()
$link_args += $f_debug
# $link_args += $f_optimize_size
link-modules @($module_asm_crt, $module_c) $elf $link_args
$link_modules = @(
$module_asm_crt,
$module_c
)
link-modules $link_modules $elf $link_args
make-binary $elf $exe
}
build-gte_hello
+2 -2
View File
@@ -56,7 +56,7 @@ local function valid_phase(p) return KNOWN_PHASES[p] or false end
local function is_wave_context_reg(n) return WAVE_CONTEXT_REGS[n] ~= nil end
-- ════════════════════════════════════════════════════════════════════════════
-- Hand-rolled split helpers (no :gmatch / no regex)
-- Hand-rolled split helpers (no regex patterns used)
-- ════════════════════════════════════════════════════════════════════════════
--- Split a string at top-level commas. Used inside TAPE_ATOM_* macro
@@ -87,7 +87,7 @@ local function split_csv_top(s)
end
--- Split a string into whitespace-separated tokens.
--- Hand-rolled (no :gmatch / no regex).
--- Hand-rolled (no regex patterns).
local function split_ws(s)
local tokens = {}
local i, n = 1, 1
+75 -15
View File
@@ -408,24 +408,74 @@ end
-- macro expansion. Each comma-separated entry in the body is a
-- "slot" that contributes its own word count. For most entries
-- (regular MIPS instructions) the count is 1. For `mac_Y(...)`
-- calls, the count is the word count of mac_Y (recursive lookup).
-- For encoding macros with a known multi-word count (e.g.
-- `mask_upper` = 2), the count is taken from `word_counts`.
-- calls, the count is the word count of mac_Y (recursive lookup
-- through `components`). For encoding macros with a known multi-word
-- count (e.g. `mask_upper` = 2), the count is taken from `word_counts`.
--
-- The lookup is memoized via `rec()` to avoid infinite recursion
-- (e.g. if two components referenced each other). This is the
-- same algorithm as the original tape_atom_annotation_pass.lua
-- (commit 7d20a4d) — without it, a fresh build with no pre-existing
-- *.macs.h files in gen/ would compute wrong counts: e.g.
-- `mac_format_f3_color` calls `mac_pack_color_word` which isn't
-- in `wc` yet, so the lookup falls through to the "1 word" default
-- and produces `WORD_COUNT(mac_format_f3_color, 1)` instead of 3.
--
-- ADAPTATION: the original used an inline recursive `rec()` that
-- walked the components list + word_counts table. The new code
-- delegates to word_count_eval.count_body_words(body, wc), which
-- is equivalent when `wc` is pre-populated with component macros.
-- The word-counts pass (which runs before components) scans all
-- existing gen/*.macs.h files, so on any build with checked-in
-- gen/ files, wc already contains every mac_X entry.
--- @param c Component
--- @param components Component[] -- (kept for API compatibility, unused)
--- @param components Component[]
--- @param wc table<string, integer>
--- @return integer
local function compute_component_word_count(c, components, wc)
return count_body_words(c.body, wc)
-- Build component lookup table once per call.
local comp_by_name = {}
for _, cc in ipairs(components) do
comp_by_name[cc.name] = cc
end
local cache = {}
local function rec(name)
if cache[name] ~= nil then return cache[name] end
cache[name] = -1 -- mark in-progress (cycle detection)
local cc = comp_by_name[name]
local n
if cc then
local body_tokens = {}
for _, t in ipairs(split_top_level_commas(cc.body)) do
local trimmed = trim(t)
if trimmed ~= "" then body_tokens[#body_tokens + 1] = trimmed end
end
n = 0
for _, t in ipairs(body_tokens) do
-- Read the first identifier from the token.
local ident = read_ident(t, 1)
-- Components are stored without the `mac_` prefix
-- (e.g. "format_f3_color"). The token has `mac_format_f3_color(...)`,
-- so strip the `mac_` prefix to look up the component.
local comp_name = ident
if comp_name and comp_name:sub(1, 4) == "mac_" then
comp_name = comp_name:sub(5)
end
if comp_name and comp_by_name[comp_name] then
-- It's a `mac_X(...)` call. Recurse.
n = n + rec(comp_name)
elseif comp_name and wc and wc[comp_name] then
-- Encoding macro or pseudo-instruction (e.g. mask_upper = 2,
-- nop2 = 2). Trust the metadata — tape_atom.metadata.h is the
-- single source of truth for word counts.
n = n + wc[comp_name]
else
-- Unrecognized token. Fall back to 1 word.
n = n + 1
end
end
else
-- Not a known component: assume 1 word (regular instruction).
n = 1
end
cache[name] = n
return n
end
return rec(c.name)
end
-- ============================================================
@@ -446,8 +496,18 @@ local function build_component_lines(c, components, wc)
-- Emit the signature comment (if any) above the macro.
if c.comment and c.comment ~= "" then
for line in (c.comment .. "\n"):gmatch("([^\n]*)\n") do
lines[#lines + 1] = line
-- Hand-rolled newline splitter (no regex patterns used).
local s = c.comment .. "\n"
local i = 1
local len = #s
while i <= len do
local nl = s:find("\n", i, true)
if not nl then
lines[#lines + 1] = s:sub(i)
break
end
lines[#lines + 1] = s:sub(i, nl - 1)
i = nl + 1
end
end
+11 -1
View File
@@ -385,7 +385,17 @@ local function topo_sort(passes, requested_set)
end
end
if #order ~= 0 then
-- Cycle detection: if order doesn't include all needed passes,
-- some are stuck with in_degree > 0 (the cycle closed on itself
-- before Kahn could process them). Without this check, a fully-
-- closed cycle (e.g. A -> B -> A) would silently return an empty
-- order list, leaving the orchestrator to dispatch nothing.
--
-- Note: `#needed` returns 0 for hash tables (needed is a set,
-- not a sequence), so we count entries explicitly.
local needed_count = 0
for _ in pairs(needed) do needed_count = needed_count + 1 end
if #order ~= needed_count then
for name, deg in pairs(in_degree) do
if deg > 0 then
error("dependency cycle detected involving pass '" .. name .. "'")