reading...

This commit is contained in:
ed
2026-08-19 21:49:36 -04:00
parent bde829bf59
commit 81f37e0098
+53 -72
View File
@@ -283,8 +283,13 @@ local function stamp_event_fields(events)
end end
local shape = ident:match("^mac_format_([%w_]+)_color$") local shape = ident:match("^mac_format_([%w_]+)_color$")
if shape then mac_format_shape = shape end if shape then
if ident:match("^mac_gte_store_[%w_]+$") then is_gte_store = true end mac_format_shape = shape
end
if ident:match("^mac_gte_store_[%w_]+$")
then
is_gte_store = true
end
if ident == "mac_insert_ot_tag" or ident:match("^mac_insert_ot_tag_[%w_]+$") then if ident == "mac_insert_ot_tag" or ident:match("^mac_insert_ot_tag_[%w_]+$") then
is_ot_tag = true is_ot_tag = true
end end
@@ -537,14 +542,9 @@ local function sign_extend_i16(value)
return value return value
end end
local function is_gpr_operand(operand) local function is_gpr_operand(operand) return type(operand) == "string" and operand:sub(1, 2) == "R_" end
return type(operand) == "string" and operand:sub(1, 2) == "R_"
end
local function is_tracked_gpr(operand) local function is_tracked_gpr(operand) return is_gpr_operand(operand) or (type(operand) == "string" and operand:sub(1, 7) == "reguse:") end
return is_gpr_operand(operand)
or (type(operand) == "string" and operand:sub(1, 7) == "reguse:")
end
local function constant_for_operand(gpr_values, operand) local function constant_for_operand(gpr_values, operand)
if operand == "R_0" then return 0 end if operand == "R_0" then return 0 end
@@ -672,8 +672,7 @@ local function is_cop2_use(ident)
return canonical:sub(1, 9) == "gte_cmdw_" or ident:sub(1, 4) == "gte_" return canonical:sub(1, 9) == "gte_cmdw_" or ident:sub(1, 4) == "gte_"
end end
local function append_cu2_finding(atom, event, forward, transition, local function append_cu2_finding(atom, event, forward, transition, gap, kind, confidence, message)
gap, kind, confidence, message)
local event_ident = event.encoder or event.ident or "?" local event_ident = event.encoder or event.ident or "?"
local policy = duffle.CU2_TRANSITION_POLICY or {} local policy = duffle.CU2_TRANSITION_POLICY or {}
local evidence = policy.evidence or {} local evidence = policy.evidence or {}
@@ -706,8 +705,7 @@ end
-- Read `sys_mov_to_cop0(source, 12)` before applying any writes from the current event. -- Read `sys_mov_to_cop0(source, 12)` before applying any writes from the current event.
-- A known source stages a target transition; an unknown source stages an ambiguity that is reported only if a later COP2 use reaches it. -- A known source stages a target transition; an unknown source stages an ambiguity that is reported only if a later COP2 use reaches it.
local function stage_cu2_transition(ev_ident, ev_args, ev_word, ev_line, local function stage_cu2_transition(ev_ident, ev_args, ev_word, ev_line, ev_source, forward)
ev_source, forward)
if ev_ident ~= "sys_mov_to_cop0" then return end if ev_ident ~= "sys_mov_to_cop0" then return end
local policy = duffle.CU2_TRANSITION_POLICY local policy = duffle.CU2_TRANSITION_POLICY
if not policy then return end if not policy then return end
@@ -776,8 +774,7 @@ local function consume_cu2_transition(atom, event, ev_word, forward)
if target == "enabled" then if target == "enabled" then
forward.cu2_state = "enabled" forward.cu2_state = "enabled"
else else
append_cu2_finding(atom, event, forward, transition, gap, append_cu2_finding(atom, event, forward, transition, gap, "error", "exact",
"error", "exact",
string.format("%s at line %d: COP2 unavailable after SR.CU2 was disabled" string.format("%s at line %d: COP2 unavailable after SR.CU2 was disabled"
.. " (gap=%d, required=%d)", .. " (gap=%d, required=%d)",
atom.name, event_line, atom.name, event_line,
@@ -851,8 +848,7 @@ local function analyze_hardware_relations(atom)
forward.c2_ctrl_writes = forward.c2_ctrl_writes or {} forward.c2_ctrl_writes = forward.c2_ctrl_writes or {}
local alias = ev_args[2] local alias = ev_args[2]
if type(alias) ~= "string" or not alias:match("^gte_cr_") then if type(alias) ~= "string" or not alias:match("^gte_cr_") then
alias = tostring(ev.call_text or ""):match("gte_cr_[%w_]+") alias = tostring(ev.call_text or ""):match("gte_cr_[%w_]+") or tostring(ev.root_call_text or ""):match("gte_cr_[%w_]+")
or tostring(ev.root_call_text or ""):match("gte_cr_[%w_]+")
end end
local src = ev_args[1] local src = ev_args[1]
if type(src) == "string" then src = src:match("[%w_]+") end if type(src) == "string" then src = src:match("[%w_]+") end
@@ -902,7 +898,8 @@ local function analyze_hardware_relations(atom)
and relation.consumer == "cop2_input" and relation.consumer == "cop2_input"
and relation.id ~= "mtc2_irgb_visibility" and relation.id ~= "mtc2_irgb_visibility"
and relation.id ~= "lwc2_to_gte_command" and relation.id ~= "lwc2_to_gte_command"
and relation.id ~= "lwc2_to_other_consumer" then and relation.id ~= "lwc2_to_other_consumer"
then
required = 0 required = 0
end end
local unknown_visibility = relation.visibility and relation.visibility.kind == "unknown_consumer" local unknown_visibility = relation.visibility and relation.visibility.kind == "unknown_consumer"
@@ -1628,8 +1625,7 @@ local function check_mac_yield_uniformity(atom, pipe_ctx, findings)
if ev.is_yield or ev.is_raw_yield_tail then return true end if ev.is_yield or ev.is_raw_yield_tail then return true end
local ident = ev.encoder or ev.ident or "" local ident = ev.encoder or ev.ident or ""
if ident == "mac_yield" or ident == "mac_yield_tail" then return true end if ident == "mac_yield" or ident == "mac_yield_tail" then return true end
local lead = tostring(ev.call_text or ""):match("^([%w_]+)") local lead = tostring(ev.call_text or ""):match("^([%w_]+)") or tostring(ev.root_call_text or ""):match("^([%w_]+)")
or tostring(ev.root_call_text or ""):match("^([%w_]+)")
return lead == "mac_yield" or lead == "mac_yield_tail" return lead == "mac_yield" or lead == "mac_yield_tail"
end end
@@ -1874,8 +1870,7 @@ local function check_yield_load_tail_pairing(atom, _pipe_ctx, findings)
line = line_for(ev), line = line_for(ev),
check = "yield_load_tail_pairing", check = "yield_load_tail_pairing",
kind = "error", kind = "error",
msg = string.format( msg = string.format("%s at line %d has `mac_yield_load()` at word %d but the previous event is `%s`, not a branch; and the next label is `%s`. `mac_yield_load()` must fill a branch BD-slot or sit between two `atom_label`s for the natural fall-through load."
"%s at line %d has `mac_yield_load()` at word %d but the previous event is `%s`, not a branch; and the next label is `%s`. `mac_yield_load()` must fill a branch BD-slot or sit between two `atom_label`s for the natural fall-through load."
, atom.name, line_for(ev), event_idx, prev_ident, next_ident), , atom.name, line_for(ev), event_idx, prev_ident, next_ident),
} }
end end
@@ -2858,8 +2853,7 @@ local function check_gte_cr_alias_writes(atom, pipe_ctx, findings)
line = distinct[1].line, line = distinct[1].line,
check = "gte_cr_alias_writes", check = "gte_cr_alias_writes",
kind = "warning", kind = "warning",
msg = string.format( msg = string.format("atom '%s' touches %d aliases that share C2[%d]: %s; verify the intent"
"atom '%s' touches %d aliases that share C2[%d]: %s; verify the intent"
, atom.name or "", #distinct, slot, table.concat(aliases, ", ")), , atom.name or "", #distinct, slot, table.concat(aliases, ", ")),
} }
end end
@@ -2871,7 +2865,6 @@ end
-- The bare macro encodes only the cmd field; the canonical libgte-2-pass shape uses `gte_cmdw_mvmva_c11_pass2_exact = 0x4A49E012` (gte.h:430). -- The bare macro encodes only the cmd field; the canonical libgte-2-pass shape uses `gte_cmdw_mvmva_c11_pass2_exact = 0x4A49E012` (gte.h:430).
-- --
-- Severity: info by default. Escalates to warning when `GTE_RT_DIAGONAL_STRICT=1` env var is set (CI / production builds). -- Severity: info by default. Escalates to warning when `GTE_RT_DIAGONAL_STRICT=1` env var is set (CI / production builds).
--
-- 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 {}
@@ -2886,8 +2879,7 @@ local function check_rtdiagonal_completeness(atom, _pipe_ctx, findings)
line = atom_body_token_source_line(atom, token, line_in_body), line = atom_body_token_source_line(atom, token, line_in_body),
check = "rtdiagonal_completeness", check = "rtdiagonal_completeness",
kind = strict and "warning" or "info", kind = strict and "warning" or "info",
msg = string.format( msg = string.format("atom '%s' uses the bare gte_cmdw_mvmva macro; "
"atom '%s' uses the bare gte_cmdw_mvmva macro; "
.. "the canonical libgte-2-pass shape is gte_cmdw_mvmva_c11_pass2_exact = 0x4A49E012 " .. "the canonical libgte-2-pass shape is gte_cmdw_mvmva_c11_pass2_exact = 0x4A49E012 "
.. "(gte.h:430). The bare macro does not encode RT23/RT31/RT32/RT33; " .. "(gte.h:430). The bare macro does not encode RT23/RT31/RT32/RT33; "
.. "for a full 3x3 matrix, use the dedicated literal or hand-build via enc_gte_*()." .. "for a full 3x3 matrix, use the dedicated literal or hand-build via enc_gte_*()."
@@ -2899,10 +2891,8 @@ end
-- Check #N+2: gte_cr_TR_naming -- Check #N+2: gte_cr_TR_naming
-- Fires one info per atom body when a `gte_cr_TR[XYZ]` alias is used. -- Fires one info per atom body when a `gte_cr_TR[XYZ]` alias is used.
-- Translation-vector registers are the only 3-letter-suffix C2 aliases -- Translation-vector registers are the only 3-letter-suffix C2 aliases (`TRX/TRY/TRZ`); an agent who reads `TRX` might typo it as `RT_X` or
-- (`TRX/TRY/TRZ`); an agent who reads `TRX` might typo it as `RT_X` or -- `RTX0` and either get a compile error (best case) or a build that links but routes the `ctc2` write to the wrong C2 slot.
-- `RTX0` and either get a compile error (best case) or a build that
-- links but routes the `ctc2` write to the wrong C2 slot.
-- --
-- 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)
@@ -2963,8 +2953,7 @@ local function check_gte_cr_alias_writes_xatom(src, pipe_ctx, findings)
line = w.line, line = w.line,
check = "gte_cr_alias_writes_xatom", check = "gte_cr_alias_writes_xatom",
kind = "warning", kind = "warning",
msg = string.format( msg = string.format("atom '%s' writes %s to C2[%d]; atom '%s' already wrote %s"
"atom '%s' writes %s to C2[%d]; atom '%s' already wrote %s"
, atom.name or "", w.alias, slot, prev.atom, prev.alias), , atom.name or "", w.alias, slot, prev.atom, prev.alias),
} }
end end
@@ -3003,8 +2992,7 @@ local function emit_packed_finding(atom, writes, rel, findings)
line = line, line = line,
check = "gte_packed_writes", check = "gte_packed_writes",
kind = "warning", kind = "warning",
msg = string.format( msg = string.format("atom '%s' writes %s before %s on packed C2[%d]"
"atom '%s' writes %s before %s on packed C2[%d]"
, atom.name or "", rel.second, rel.first, rel.slot), , atom.name or "", rel.second, rel.first, rel.slot),
} }
end end
@@ -3075,8 +3063,7 @@ local function emit_ctc2_finding(atom, line, dest, findings)
line = line or atom.line, line = line or atom.line,
check = "ctc2_chain_source_preservation", check = "ctc2_chain_source_preservation",
kind = "warning", kind = "warning",
msg = string.format( msg = string.format("atom '%s' reloads %s before a later ctc2 that still names it"
"atom '%s' reloads %s before a later ctc2 that still names it"
, atom.name or "", dest), , atom.name or "", dest),
} }
end end
@@ -3249,10 +3236,8 @@ local function check_immediate_field_width(atom, pipe_ctx, findings)
kind = "error", kind = "error",
atom = atom.name, atom = atom.name,
line = ev_line, line = ev_line,
msg = string.format( msg = string.format("%s: immediate %d at arg %d overflows %d-bit %s field (valid %d..%d)"
"%s: immediate %d at arg %d overflows %d-bit %s field (valid %d..%d)", , ev_ident, signed_value, rule.arg, width, "signed", lo, hi),
ev_ident, signed_value, rule.arg, width,
"signed", lo, hi),
} }
end end
else else
@@ -3267,9 +3252,8 @@ local function check_immediate_field_width(atom, pipe_ctx, findings)
kind = "warning", kind = "warning",
atom = atom.name, atom = atom.name,
line = ev_line, line = ev_line,
msg = string.format( msg = string.format("%s: negative immediate %d at arg %d on unsigned %d-bit field (truncated to %d by IMM_MASK)"
"%s: negative immediate %d at arg %d on unsigned %d-bit field (truncated to %d by IMM_MASK)", , ev_ident, signed_value, rule.arg, width, low_bits),
ev_ident, signed_value, rule.arg, width, low_bits),
} }
else else
findings[#findings + 1] = { findings[#findings + 1] = {
@@ -3277,20 +3261,20 @@ local function check_immediate_field_width(atom, pipe_ctx, findings)
kind = "error", kind = "error",
atom = atom.name, atom = atom.name,
line = ev_line, line = ev_line,
msg = string.format( msg = string.format("%s: immediate %d at arg %d overflows %d-bit unsigned field (valid 0..%d)"
"%s: immediate %d at arg %d overflows %d-bit unsigned field (valid 0..%d)", , ev_ident, value, rule.arg, width, field_max),
ev_ident, value, rule.arg, width, field_max),
} }
end end
end end
end end
end end
end end
end end
end end
end end
end end
-- TODO(Ed): Review this, if its still being used it needs to be re-evaluated for the current tape runtime.
local SCRATCH_GPRS = { local SCRATCH_GPRS = {
R_T0 = true, R_T1 = true, R_T2 = true, R_T3 = true, R_T0 = true, R_T1 = true, R_T2 = true, R_T3 = true,
R_AT = true, R_V0 = true, R_V1 = true, R_AT = true, R_V0 = true, R_V1 = true,
@@ -3331,7 +3315,7 @@ local function collect_gpr_traffic(atom)
for _, arg in ipairs(args) do for _, arg in ipairs(args) do
local g = arg_as_gpr(arg) local g = arg_as_gpr(arg)
if g then if g then
reads[g] = true reads [g] = true
writes[g] = true writes[g] = true
end end
end end
@@ -3376,12 +3360,10 @@ local function check_atom_calls_inferred_traffic(atom, pipe_ctx, findings)
if ident:sub(1, 4) == "mac_" then if ident:sub(1, 4) == "mac_" then
local bare = ident:sub(5) local bare = ident:sub(5)
local idx = pipe_ctx.component_body_index and pipe_ctx.component_body_index[bare] local idx = pipe_ctx.component_body_index and pipe_ctx.component_body_index[bare]
local comp = (pipe_ctx.components_by_name or {})[bare] local comp = (pipe_ctx.components_by_name or {})[bare] or (pipe_ctx.atoms_by_name or {})[bare]
or (pipe_ctx.atoms_by_name or {})[bare] local body_toks = (idx and idx.body_tokens) or (comp and (comp.body_tokens or (comp.paths and comp.paths.tokens)))
local body_toks = (idx and idx.body_tokens)
or (comp and (comp.body_tokens or (comp.paths and comp.paths.tokens)))
local cr, cw = collect_gpr_traffic(body_toks) local cr, cw = collect_gpr_traffic(body_toks)
for k in pairs(cr) do reads[k] = true end for k in pairs(cr) do reads [k] = true end
for k in pairs(cw) do writes[k] = true end for k in pairs(cw) do writes[k] = true end
end end
end end
@@ -3393,6 +3375,7 @@ local function check_atom_calls_inferred_traffic(atom, pipe_ctx, findings)
for k in pairs(inferred) do for k in pairs(inferred) do
if k == "R_0" then if k == "R_0" then
if declared[k] then out[k] = true end if declared[k] then out[k] = true end
-- TODO(Ed): Review usage of this array, this may be outdated and not representattive of the current tape runtime.
elseif SCRATCH_GPRS[k] then elseif SCRATCH_GPRS[k] then
if declared[k] then out[k] = true end if declared[k] then out[k] = true end
else else
@@ -3409,13 +3392,12 @@ local function check_atom_calls_inferred_traffic(atom, pipe_ctx, findings)
line = info.info_line or atom.line, line = info.info_line or atom.line,
check = "atom_calls_inferred_traffic", check = "atom_calls_inferred_traffic",
kind = "warning", kind = "warning",
msg = string.format( msg = string.format("atom '%s' declared [%s]/[%s] != inferred [%s]/[%s]"
"atom '%s' declared [%s]/[%s] != inferred [%s]/[%s]", , atom.name
atom.name, , table.concat(gpr_set_keys(decl_r), ",")
table.concat(gpr_set_keys(decl_r), ","), , table.concat(gpr_set_keys(decl_w), ",")
table.concat(gpr_set_keys(decl_w), ","), , table.concat(gpr_set_keys(reads), ",")
table.concat(gpr_set_keys(reads), ","), , table.concat(gpr_set_keys(writes), ","))
table.concat(gpr_set_keys(writes), ",")),
} }
end end
end end
@@ -3439,13 +3421,12 @@ local function check_component_self_consistency(src, pipe_ctx, findings)
line = ai.info_line or atom.line, line = ai.info_line or atom.line,
check = "component_self_consistency", check = "component_self_consistency",
kind = "warning", kind = "warning",
msg = string.format( msg = string.format("component '%s' atom_reads/atom_writes [%s]/[%s] != body [%s]/[%s]"
"component '%s' atom_reads/atom_writes [%s]/[%s] != body [%s]/[%s]", , name
name, , table.concat(gpr_set_keys(decl_r), ",")
table.concat(gpr_set_keys(decl_r), ","), , table.concat(gpr_set_keys(decl_w), ",")
table.concat(gpr_set_keys(decl_w), ","), , table.concat(gpr_set_keys(reads), ",")
table.concat(gpr_set_keys(reads), ","), , table.concat(gpr_set_keys(writes), ","))
table.concat(gpr_set_keys(writes), ",")),
} }
end end
end end
@@ -3694,8 +3675,8 @@ local function validate(ctx, src, corpus_pipe_ctx)
end end
summaries[#summaries + 1] = { summaries[#summaries + 1] = {
line = 0, line = 0,
msg = string.format("cycles: path-aware min=%d max=%d across %d atoms; worst atom=%s (%d); best-case, no stalls; BD-slot nops absorbed into branch costs", msg = string.format("cycles: path-aware min=%d max=%d across %d atoms; worst atom=%s (%d); best-case, no stalls; BD-slot nops absorbed into branch costs"
total_min, total_max, #atoms, max_atom_name or "?", max_atom_cyc), , total_min, total_max, #atoms, max_atom_name or "?", max_atom_cyc),
} }
end end
@@ -3757,11 +3738,11 @@ function M.run(ctx)
a.source_path = src.path a.source_path = src.path
all_atoms[#all_atoms + 1] = a all_atoms[#all_atoms + 1] = a
end end
for _, f in ipairs(result.findings) do all_findings[#all_findings + 1] = f end for _, f in ipairs(result.findings) do all_findings [#all_findings + 1] = f end
for _, e in ipairs(result.errors) do dir_errors [#dir_errors + 1] = e end for _, e in ipairs(result.errors) do dir_errors [#dir_errors + 1] = e end
for _, w in ipairs(result.warnings) do dir_warnings [#dir_warnings + 1] = w end for _, w in ipairs(result.warnings) do dir_warnings [#dir_warnings + 1] = w end
for _, i_ in ipairs(result.info) do dir_info [#dir_info + 1] = i_ end for _, i_ in ipairs(result.info) do dir_info [#dir_info + 1] = i_ end
for _, s in ipairs(result.summaries or {}) do dir_summaries[#dir_summaries + 1] = s end for _, s in ipairs(result.summaries or {}) do dir_summaries [#dir_summaries + 1] = s end
end end
-- Stash per-module results on the corpus for `report.lua` to consume. -- Stash per-module results on the corpus for `report.lua` to consume.