From 144c605ad8af3a7b60a89cf12ccb1bb28a76aac4 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Mon, 10 Aug 2026 23:04:43 -0400 Subject: [PATCH] some more review. not working still. --- code/duffle/gen/macs.h | 62 ----- code/duffle/gte.atom.c | 211 ++++++++-------- code/duffle/lottes_tape.h | 4 +- code/hello_camera/gen/macs.h | 347 -------------------------- code/hello_camera/gen/offsets.h | 30 --- code/hello_camera/hello_camera.atom.c | 270 +------------------- code/hello_camera/hello_camera.c | 116 +++++---- scripts/duffle.lua | 8 +- scripts/duffle_paths.lua | 5 +- scripts/passes/auto_reg.lua | 8 +- scripts/passes/components.lua | 72 +++--- scripts/passes/dwarf_injection.lua | 35 +-- scripts/passes/offsets.lua | 8 + scripts/passes/static_analysis.lua | 6 +- 14 files changed, 257 insertions(+), 925 deletions(-) diff --git a/code/duffle/gen/macs.h b/code/duffle/gen/macs.h index 520a0b6..a0334d5 100644 --- a/code/duffle/gen/macs.h +++ b/code/duffle/gen/macs.h @@ -175,68 +175,6 @@ WORD_COUNT(mac_gte_sqr_v3, 8) , shift_aright_var(r_dz, r_dz, r_shift) WORD_COUNT(mac_gte_gpf_scale, 13) -#define mac_normalize_v3s4(...) \ - load_word(r_src, R_TapePtr, O_(Binds_NormalizeV3S4,src)) /* pop src ptr (scratch addr) */ \ -, load_word(r_dst, R_TapePtr, O_(Binds_NormalizeV3S4,dst)) /* pop dst ptr (scratch addr) */ \ -, add_ui_self( R_TapePtr, S_(Binds_NormalizeV3S4)) \ -, load_word(r_sx, r_src, O_(V3_S4,x)) \ -, load_word(r_sy, r_src, O_(V3_S4,y)) \ -, load_word(r_sz, r_src, O_(V3_S4,z)) \ -, nop /* load-delay */ /* ── 48-word normalize body (preserved verbatim from ac_normalize_v3s4) ─────── */ /* Stage 1: mtc2 src → IR1/2/3, SQR fires (MAC1/2/3 = IR², IR ← MAC saturated) */ \ -, gte_mv_to_data_r(r_sx, C2_IR1) \ -, gte_mv_to_data_r(r_sy, C2_IR2) \ -, gte_mv_to_data_r(r_sz, C2_IR3) \ -, nop \ -, gte_cmdw_sqr /* Stage 2: mfc2 MAC1/2/3, sum, mtc2 LZCS */ \ -, gte_mv_from_data_r(r_sq_y, C2_MAC1) /* r_sq_y = MAC1 = sx² */ \ -, gte_mv_from_data_r(r_sq_z, C2_MAC2) /* r_sq_z = MAC2 = sy² */ \ -, gte_mv_from_data_r(r_recip_est, C2_MAC3) /* r_recip_est = MAC3 = sz² */ \ -, nop /* MFC2→GPR load delay (1 slot) */ \ -, add_u(r_recip_est, r_recip_est, r_sq_z) /* r_recip_est += sy² */ \ -, add_u(r_recip_est, r_recip_est, r_sq_y) /* r_recip_est += sx² (sum = |v|²) */ \ -, gte_mv_to_data_r( r_recip_est, C2_LZCS) /* LZCS = |v|² */ \ -, nop2 \ -, gte_mv_from_data_r(r_lzcr, C2_LZCR) /* r_lzcr = LZCR (count of leading bits) */ \ -, nop /* MFC2→GPR load delay (1 slot) */ /* Stage 3: compute shift amount, align |v|² to bit 24, lookup 1/|v| */ \ -, and_i( r_lzcr, r_lzcr, -2) /* r_lzcr &= ~1 (force even for halving) */ \ -, li_s( r_shift, 31) /* r_shift = 31 */ \ -, sub_s( r_shift, r_shift, r_lzcr) /* r_shift = 31 - LZCR */ \ -, shift_aright( r_shift, r_shift, 1) /* r_shift = (31 - LZCR) / 2 */ \ -, add_si( r_tmp, r_lzcr, -24) /* r_tmp = LZCR - 24 (signed, for branch) */ \ -, branch_lt_zero(r_tmp, atom_offset(srav_path, aligned_done)) \ -, nop \ -, jump_rel( atom_offset(aligned_done, srav_path)) \ -, shift_lleft_var(r_recip_est, r_recip_est, r_tmp) /* BD-slot of branch_equal: r_recip_est = |v|² << (LZCR - 24) */ \ -, atom_label(srav_path) /* SRAV path: |v|² is small (top bit < bit 24) */ \ -, li_s( r_tmp, 24) \ -, sub_s( r_tmp, r_tmp, r_lzcr) /* r_tmp = 24 - LZCR */ \ -, shift_aright_var(r_recip_est, r_recip_est, r_tmp) /* r_recip_est = |v|² >> (24 - LZCR) */ \ -, atom_label(aligned_done) /* Both paths converge here with |v|² aligned to bit 24 */ /* r_recip_est now holds |v|² aligned to bit 24 — convert to byte offset, -64 to skip zero pad. */ \ -, add_si( r_recip_est, r_recip_est, -64) \ -, shift_lleft( r_recip_est, r_recip_est, 1) /* r_recip_est *= 2 (half-word index) */ /* Reference OUR local sqrtbl via &-address split. Compiler/linker resolves both halves. */ \ -, load_upper_i( r_tmp, u4_hi(& gte_normalize_sqr_tbl)) /* lui */ \ -, or_i_self( r_tmp, u4_lo(& gte_normalize_sqr_tbl)) /* ori */ \ -, add_u( r_tmp, r_tmp, r_recip_est) /* r_tmp = sqrtbl base + byte offset (matches libgte 0x80016118: addu t5,t5,t4) */ \ -, load_half( r_recip_est, r_tmp, 0) /* r_recip_est = sqrtbl[r_recip_est] = 1/|v| estimate */ \ -, nop /* retire load_half before MTC2 (matches libgte 0x80016120: nop) */ /* Stage 4: mtc2 IR0..3, GPF (MAC = IR0*IR), mfc2 MAC, srav finalize */ \ -, gte_mv_to_data_r(r_recip_est, C2_IR0) /* IR0 = 1/|v| estimate */ \ -, gte_mv_to_data_r(r_sx, C2_IR1) /* IR1 = src.x */ \ -, gte_mv_to_data_r(r_sy, C2_IR2) /* IR2 = src.y */ \ -, gte_mv_to_data_r(r_sz, C2_IR3) /* IR3 = src.z */ \ -, nop2 /* COP2 transfer latency (2 slots) */ \ -, gte_cmdw_gpf \ -, gte_mv_from_data_r(r_sx, C2_MAC1) /* MAC1 → r_sx (overwrites src.x with raw reciprocal-scaled) */ \ -, gte_mv_from_data_r(r_sy, C2_MAC2) \ -, gte_mv_from_data_r(r_sz, C2_MAC3) \ -, shift_aright_var(r_sx, r_sx, r_shift) \ -, shift_aright_var(r_sy, r_sy, r_shift) \ -, shift_aright_var(r_sz, r_sz, r_shift) /* ── I/O wrapper tail (~3 words) ───────────────────────────────────────────── */ \ -, store_word(r_sx, r_dst, O_(V3_S4,x)) \ -, store_word(r_sy, r_dst, O_(V3_S4,y)) \ -, store_word(r_sz, r_dst, O_(V3_S4,z)) /* ── atom_reads(R_TapePtr) atom_writes(R_TapePtr) ────────────────────────── */ \ -, mac_yield() -WORD_COUNT(mac_normalize_v3s4, 62) - #define mac_gcmd_push(cmd, reg_transfer, reg_base, port) \ load_upper_i(reg_transfer, cmd >> 16) \ , or_i_self( reg_transfer, cmd & 0xFFFF) \ diff --git a/code/duffle/gte.atom.c b/code/duffle/gte.atom.c index 8e6a328..3b3cbce 100644 --- a/code/duffle/gte.atom.c +++ b/code/duffle/gte.atom.c @@ -101,7 +101,8 @@ FI_ Slice_MipsCode ac_gte_gpf_scale(MipsAtomBuilder_R ab, U4 r_sx, U4 r_sy, U4 r * Octave 1 (entries 48- 95): mantissa in [0x10000, 0x20000) output ~[0.707, 0.500] * Octave 2 (entries 96-143): mantissa in [0x20000, 0x40000) output ~[0.500, 0.354] * Octave 3 (entries144-191): mantissa in [0x40000, 0x80000) output ~[0.354, 0.251] - * Within each octave, 8 sub-entries interpolate over the 8 fractional bits of the mantissa (the byte `(0x80 | (i mod 8))` for the lower-byte of the aligned value). + * Within each octave, 8 sub-entries interpolate over the 8 fractional bits of the mantissa + * (the byte `(0x80 | (i mod 8))` for the lower-byte of the aligned value). * Sampling the first value of each octave: * [0] 0x1000 = 1.0000 ; 1 / sqrt(1.0000) * [48] 0x0e4f = 0.8940 ; 1 / sqrt(1.2500) @@ -153,125 +154,115 @@ internal S2 const gte_normalize_sqr_tbl[192] align_(2) = { }; /* ─── Full normalize (all 4 stages inline) ─── + * Generic 4-stage GTE normalize (SQR → sum+LZCR → align+sqrtbl → GPF+srav). + * + * Parameterized by caller-provided scratch base + src/dst offsets. + * The caller passes r_src_offset and r_dst_offset as compile-time constants + * (typically derived from O_ macros in the caller's struct schema, e.g., `O_(CallerBundleScratch, fwd)`). + * + * This design lets any caller (with a scratch base + struct schema) use `normalize_v3s4_proc` + * without putting magic offsets in the C-side bundle helper — the offsets come from O_ macros at the call site. + * + * Body uses 9 GPRs (r_src_ptr..r_branch_tmp): + * r_src_ptr, r_dst_ptr : src/dst pointers (computed from r_scratch + caller offsets) + * r_tmp : scratch (reserved for misc use) + * r_mac1_scratch : MAC1 result scratch (before sum into r_recip_est) + * r_mac2_scratch : MAC2 result scratch (clobbered to IR1 in stage 4) + * r_recip_est : |v|² sum + shift-input + sqrtbl[index] (the main chain) + * r_lzcr : LZCR value (consumed by stage 3 alignment calc) + * r_shift : final srav amount (consumed by stage 4 shift_aright_var) + * r_branch_tmp : scratch (shift count, branch target, sqrtbl base addr) + * + * Atom_labels are srav_path / aligned_done + * (NOT namespaced — they're internal to this proc; + * the metaprogram's per-atom-name enum emission handles any collision across different atoms/files that share the same labels). + * + * Pool cost: 11 GPRs (well within the 9-10 caller-trash GPR budget when r_scratch is a wave-context carrier). + * * Direct port of PSYQ libgte msc02.rel.text VectorNormal disassembly (0x800160a0..0x8001615c). - * - * Component variants that could apply: - * - `ac_gte_sqr_v3` (line ~56) covers stage 1's `mtc2 IR1/2/3 + nop + gte_cmdw_sqr`. - * We do NOT call it because the inlined version of stage 1 is followed immediately by stage 2's `mfc2 MAC1/2/3` chain - * (the operands of `ac_gte_sqr_v3`'s r_sq_x/r_sq_y/r_sq_z would each require an explicit GPR to receive the MAC result, - * then a move to land in r_recip_est for the partial-sum chain). - * Inlining saves ~3 cycles of `or`-merge + register pressure - * (squared MAC3 lands DIRECTLY in r_recip_est which doubles as the partial-sum accumulator and the LZCS input — see r_recip_est row below). - * - `ac_gte_gpf_scale` (line ~71) covers stage 4's `mtc2 IR0..3 + nop2 + gte_cmdw_gpf + mfc2 MAC1/2/3 + sra`. - * We do NOT call it for the symmetric reason: the normalize in-place semantics overwrite the input regs (r_sx/r_sy/r_sz) with the normalized output, - * which `ac_gte_gpf_scale`'s r_dx/r_dy/r_dz output GPRs would not match. - * `gte_cmdw_sqr` and `gte_cmdw_gpf` primitive macros ARE used in the inlined body, so changes to those primitives - * (e.g., the libgte `fake_cmd` signature bits) propagate automatically. The components remain available for callers that want the explicit GPR-shape variants. - * - * Argument aliasing (9 unique physical regs needed, can drop to 8 with r_sq_y ≡ r_lzcr): - * r_sx, r_sy, r_sz : src components in regs (clobbered by mtc2 → IR1/2/3 in stage 1, then by mfc2 MAC1/2/3 in stage 4 — in-place semantics) - * r_sq_y, r_sq_z : MAC2, MAC3 → DIE after stage 2 accumulate (r_sq_y can alias r_lzcr after stage 2 to save one reg) - * r_recip_est : ≡ r_sqmag — multi-purpose (holds |v|² in stage 2, shift-input in stage 3, sqrtbl[index] in stage 4) - * r_lzcr : LZCR value, alive across stage 3 (srav path needs `24 - LZCR`) - * r_shift : (31 - LZCR & ~1) >> 1 — final srav amount (stages 3-4) - * r_tmp : scratch (shift count, branch target, lookup addr, table base) - * - * GPR ccount peak: 9. + * Words: ~59 (matches libgte 0x800160a0..0x8001615c at +/- 0-2 words for BD-slot reshuffling). + * Sqrtbl: hardcoded to 0x800185B4 (libgte msc02.rel.data). Note: swapped to local. * Pipeline: clobbers IR0..3, MAC1..3, LZCS, LZCR. - * Words: ~35 (pending re-gen; matches libgte 0x800160a0..0x8001615c at +/- 0-2 words for BD-slot reshuffling). - * Sqrtbl: hardcoded to 0x800185B4 (libgte msc02.rel.data). Note: swapped to local. */ - -/* ─── Binds_NormalizeV3S4 — declared here so the MipsAtom_Proc_ body can reference - * O_(Binds_NormalizeV3S4,*). Inlined at the proc-call site; not exposed in gen/macs.h. */ -typedef Struct_(Binds_NormalizeV3S4) { - U4 src; /* V3_S4* (scratch address — read from tape) */ - U4 dst; /* V3_S4* (scratch address — write to tape) */ -}; - -/* NOTE: The bundle-specific scratchpad offset schema was intentionally kept out of this file. - * gte.atom.c is the GENERIC GTE primitives file — it exposes only the parameter-style normalize_v3s4_proc for any future caller. */ -I_ void normalize_v3s4_proc( - MipsAtomBuilder_R ab - , U4 r_src /* GPR code: scratch base carrier (wave-context, e.g., R_T4) */ - , U4 r_dst /* GPR code: scratch dst pointer carrier (wave-context, e.g., R_T5) */ - , U4 r_sx, U4 r_sy, U4 r_sz /* GPR codes: src.x/y/z scratch (atom-local) */ - , U4 r_sq_y, U4 r_sq_z /* GPR codes: MAC1/2 scratch (atom-local) */ - , U4 r_recip_est /* GPR code: |v|² sum + shift-input + sqrtbl[index] (atom-local) */ - , U4 r_lzcr /* GPR code: LZCR value (atom-local) */ - , U4 r_shift /* GPR code: final srav amount (atom-local) */ - , U4 r_tmp /* GPR code: scratch (shift count, branch target, lookup addr, table base) */ -) + */ /* MipsAtom_Proc_ wrapper: declares the static MipsCode[] body, then calls atombuilder_unroll(ab, ...) to copy the encoded instructions into the caller's MipsAtomBuilder arena. */ +I_ void normalize_v3s4_proc(MipsAtomBuilder_R ab, U4 r_scratch /* GPR code: scratch base carrier (e.g., R_T4 = R_ResolveScratch) */ + , U4 r_src_offset, U4 r_dst_offset /* GPR codes: PARAMETERIZED offsets (caller passes O_ macros) */ + , U4 r_src_ptr, U4 r_dst_ptr, U4 r_tmp /* GPR codes: 3 scratch regs (src/dst computed + tmp) */ + , U4 r_mac1_scratch, U4 r_mac2_scratch /* GPR codes: 2 more: MAC1/MAC2 scratch */ + , U4 r_recip_est /* GPR code: |v|² sum + shift-input + sqrtbl[index] */ + , U4 r_lzcr, U4 r_shift /* GPR codes: lzcr + final srav amount */ + , U4 r_branch_tmp /* GPR code: scratch (shift count, branch target, lookup addr) */ +) MipsAtom_Proc_(normalize_v3s4, ab, { - /* ── I/O wrapper (~10 words: 3 bind-pop + 3 src-load + 1 nop + 3 dst-store) ─── */ - load_word(r_src, R_TapePtr, O_(Binds_NormalizeV3S4,src)), /* pop src ptr (scratch addr) */ - load_word(r_dst, R_TapePtr, O_(Binds_NormalizeV3S4,dst)), /* pop dst ptr (scratch addr) */ - add_ui_self( R_TapePtr, S_(Binds_NormalizeV3S4)), - load_word(r_sx, r_src, O_(V3_S4,x)), - load_word(r_sy, r_src, O_(V3_S4,y)), - load_word(r_sz, r_src, O_(V3_S4,z)), + add_si(r_src_ptr, r_scratch, r_src_offset), /* r_src_ptr = &src */ + add_si(r_dst_ptr, r_scratch, r_dst_offset), /* r_dst_ptr = &dst */ + nop, + + /* Load src.x/y/z from r_src_ptr (caller-determined address) into r_mac2_scratch/r_recip_est/r_branch_tmp. */ + load_word(r_mac2_scratch, r_src_ptr, O_(V3_S4,x)), + load_word(r_recip_est, r_src_ptr, O_(V3_S4,y)), + load_word(r_branch_tmp, r_src_ptr, O_(V3_S4,z)), nop, /* load-delay */ - /* ── 48-word normalize body (preserved verbatim from ac_normalize_v3s4) ─────── */ - // Stage 1: mtc2 src → IR1/2/3, SQR fires (MAC1/2/3 = IR², IR ← MAC saturated) - gte_mv_to_data_r(r_sx, C2_IR1), - gte_mv_to_data_r(r_sy, C2_IR2), - gte_mv_to_data_r(r_sz, C2_IR3), + /* Stage 1: mtc2 src → IR1/2/3, SQR fires. */ + gte_mv_to_data_r(r_mac2_scratch, C2_IR1), + gte_mv_to_data_r(r_recip_est, C2_IR2), + gte_mv_to_data_r(r_branch_tmp, C2_IR3), nop, gte_cmdw_sqr, - // Stage 2: mfc2 MAC1/2/3, sum, mtc2 LZCS - gte_mv_from_data_r(r_sq_y, C2_MAC1), /* r_sq_y = MAC1 = sx² */ - gte_mv_from_data_r(r_sq_z, C2_MAC2), /* r_sq_z = MAC2 = sy² */ - gte_mv_from_data_r(r_recip_est, C2_MAC3), /* r_recip_est = MAC3 = sz² */ - nop, /* MFC2→GPR load delay (1 slot) */ - add_u(r_recip_est, r_recip_est, r_sq_z), /* r_recip_est += sy² */ - add_u(r_recip_est, r_recip_est, r_sq_y), /* r_recip_est += sx² (sum = |v|²) */ - gte_mv_to_data_r( r_recip_est, C2_LZCS), /* LZCS = |v|² */ + + /* Stage 2: mfc2 MAC1/2/3, sum, mtc2 LZCS. */ + gte_mv_from_data_r(r_mac1_scratch, C2_MAC1), + gte_mv_from_data_r(r_mac2_scratch, C2_MAC2), + gte_mv_from_data_r(r_lzcr, C2_MAC3), + nop, + add_u(r_lzcr, r_lzcr, r_mac2_scratch), + add_u(r_lzcr, r_lzcr, r_mac1_scratch), + gte_mv_to_data_r(r_lzcr, C2_LZCS), nop2, - gte_mv_from_data_r(r_lzcr, C2_LZCR), /* r_lzcr = LZCR (count of leading bits) */ - nop, /* MFC2→GPR load delay (1 slot) */ - // Stage 3: compute shift amount, align |v|² to bit 24, lookup 1/|v| - and_i( r_lzcr, r_lzcr, -2), /* r_lzcr &= ~1 (force even for halving) */ - li_s( r_shift, 31), /* r_shift = 31 */ - sub_s( r_shift, r_shift, r_lzcr), /* r_shift = 31 - LZCR */ - shift_aright( r_shift, r_shift, 1), /* r_shift = (31 - LZCR) / 2 */ - add_si( r_tmp, r_lzcr, -24), /* r_tmp = LZCR - 24 (signed, for branch) */ - branch_lt_zero(r_tmp, atom_offset(srav_path, aligned_done)), nop, - jump_rel( atom_offset(aligned_done, srav_path)), - shift_lleft_var(r_recip_est, r_recip_est, r_tmp), /* BD-slot of branch_equal: r_recip_est = |v|² << (LZCR - 24) */ -atom_label(srav_path) /* SRAV path: |v|² is small (top bit < bit 24) */ - li_s( r_tmp, 24), - sub_s( r_tmp, r_tmp, r_lzcr), /* r_tmp = 24 - LZCR */ - shift_aright_var(r_recip_est, r_recip_est, r_tmp), /* r_recip_est = |v|² >> (24 - LZCR) */ -atom_label(aligned_done) /* Both paths converge here with |v|² aligned to bit 24 */ - /* r_recip_est now holds |v|² aligned to bit 24 — convert to byte offset, -64 to skip zero pad. */ - add_si( r_recip_est, r_recip_est, -64), - shift_lleft( r_recip_est, r_recip_est, 1), /* r_recip_est *= 2 (half-word index) */ - /* Reference OUR local sqrtbl via &-address split. Compiler/linker resolves both halves. */ - load_upper_i( r_tmp, u4_hi(& gte_normalize_sqr_tbl)), /* lui */ - or_i_self( r_tmp, u4_lo(& gte_normalize_sqr_tbl)), /* ori */ - add_u( r_tmp, r_tmp, r_recip_est), /* r_tmp = sqrtbl base + byte offset (matches libgte 0x80016118: addu t5,t5,t4) */ - load_half( r_recip_est, r_tmp, 0), /* r_recip_est = sqrtbl[r_recip_est] = 1/|v| estimate */ - nop, /* retire load_half before MTC2 (matches libgte 0x80016120: nop) */ - // Stage 4: mtc2 IR0..3, GPF (MAC = IR0*IR), mfc2 MAC, srav finalize - gte_mv_to_data_r(r_recip_est, C2_IR0), /* IR0 = 1/|v| estimate */ - gte_mv_to_data_r(r_sx, C2_IR1), /* IR1 = src.x */ - gte_mv_to_data_r(r_sy, C2_IR2), /* IR2 = src.y */ - gte_mv_to_data_r(r_sz, C2_IR3), /* IR3 = src.z */ - nop2, /* COP2 transfer latency (2 slots) */ - gte_cmdw_gpf, - gte_mv_from_data_r(r_sx, C2_MAC1), /* MAC1 → r_sx (overwrites src.x with raw reciprocal-scaled) */ - gte_mv_from_data_r(r_sy, C2_MAC2), - gte_mv_from_data_r(r_sz, C2_MAC3), - shift_aright_var(r_sx, r_sx, r_shift), - shift_aright_var(r_sy, r_sy, r_shift), - shift_aright_var(r_sz, r_sz, r_shift), + gte_mv_from_data_r(r_shift, C2_LZCR), + nop, - /* ── I/O wrapper tail (~3 words) ───────────────────────────────────────────── */ - store_word(r_sx, r_dst, O_(V3_S4,x)), - store_word(r_sy, r_dst, O_(V3_S4,y)), - store_word(r_sz, r_dst, O_(V3_S4,z)), + /* Stage 3: compute srav amount (r_lzcr) + align |v|² to bit 24. */ + and_i( r_shift, r_shift, -2), + li_s( r_lzcr, 31), + sub_s( r_lzcr, r_lzcr, r_shift), + shift_aright(r_lzcr, r_lzcr, 1), + /* r_branch_tmp = LZCR - 24 (overwrites r_branch_tmp; src.z no longer needed after SQR) */ + add_si( r_branch_tmp, r_shift, -24), + branch_lt_zero(r_branch_tmp, atom_offset(srav_path, aligned_done)), nop, + jump_rel(atom_offset(aligned_done, srav_path)), + shift_lleft_var(r_lzcr, r_lzcr, r_branch_tmp), /* when r_branch_tmp < 0 (LZCR < 24): shift r_lzcr left by (24-LZCR) */ + atom_label(srav_path) + li_s( r_branch_tmp, 24), + sub_s( r_branch_tmp, r_branch_tmp, r_shift), + shift_aright_var(r_lzcr, r_lzcr, r_branch_tmp), /* when r_branch_tmp >= 0 (LZCR >= 24): shift r_lzcr right by (LZCR-24) */ + atom_label(aligned_done) + /* r_lzcr holds |v|² aligned to bit 24. */ + add_si( r_lzcr, r_lzcr, -64), + shift_lleft(r_lzcr, r_lzcr, 1), + load_upper_i(r_branch_tmp, u4_hi(& gte_normalize_sqr_tbl)), + or_i_self( r_branch_tmp, u4_lo(& gte_normalize_sqr_tbl)), + add_u(r_branch_tmp, r_branch_tmp, r_lzcr), + load_half(r_lzcr, r_branch_tmp, 0), nop, + + /* Stage 4: GPF + srav finalize (r_lzcr = srav_amount carried from stage 3). */ + gte_mv_to_data_r(r_lzcr, C2_IR0), + gte_mv_to_data_r(r_mac2_scratch, C2_IR1), + gte_mv_to_data_r(r_recip_est, C2_IR2), + gte_mv_to_data_r(r_branch_tmp, C2_IR3), + nop2, gte_cmdw_gpf, + gte_mv_from_data_r(r_mac2_scratch, C2_MAC1), + gte_mv_from_data_r(r_recip_est, C2_MAC2), + gte_mv_from_data_r(r_branch_tmp, C2_MAC3), + shift_aright_var(r_mac2_scratch, r_mac2_scratch, r_lzcr), + shift_aright_var(r_recip_est, r_recip_est, r_lzcr), + shift_aright_var(r_branch_tmp, r_branch_tmp, r_lzcr), + + /* Store result.x/y/z to r_dst_ptr (caller-determined dst address). */ + store_word(r_mac2_scratch, r_dst_ptr, O_(V3_S4,x)), + store_word(r_recip_est, r_dst_ptr, O_(V3_S4,y)), + store_word(r_branch_tmp, r_dst_ptr, O_(V3_S4,z)), - /* ── atom_reads(R_TapePtr) atom_writes(R_TapePtr) ────────────────────────── */ mac_yield() }) diff --git a/code/duffle/lottes_tape.h b/code/duffle/lottes_tape.h index b01a416..72018e9 100644 --- a/code/duffle/lottes_tape.h +++ b/code/duffle/lottes_tape.h @@ -27,9 +27,9 @@ * to author and compose programs with. From here various conventions can be further applied. * To make things easier to understand it may be better to focus on what this ABI does not have. * It does not have have any branching within the tape but relative branches within atoms or between atoms. - * Branching nearly is always downstream. Stack usage is non-existent. + * Branching nearly is always downstream. Atuomatic stack usage is non-existent. * Push/Pop, FIFO, or Arena/Bump data structures are used by atoms explicitly. - * In it's current form with the C11 macro dsl, the user also has fullfill manual register allocation per atom. + * In it's current form with the C11 macro DSL, the user also has fullfill manual register allocation per atom. * * One of the remarkable things about utilizing this ABI is its essentially interopable with CPUs, GPUs, FPGA, * or, basically anything from the 5th generation consoles and onward. diff --git a/code/hello_camera/gen/macs.h b/code/hello_camera/gen/macs.h index 9ebc717..d6e52b7 100644 --- a/code/hello_camera/gen/macs.h +++ b/code/hello_camera/gen/macs.h @@ -39,350 +39,3 @@ WORD_COUNT(mac_put_disp_env, 5) , mac_gcmd_push(gp0_word_nop(), reg_transfer, reg_base, port) WORD_COUNT(mac_put_draw_env, 16) -#define mac_resolve_look_at__input_and_sub(...) \ - load_word(r_target_ptr, R_TapePtr, O_(Binds_ResolveLookAtSub,target)) \ -, load_word(r_eye_ptr, R_TapePtr, O_(Binds_ResolveLookAtSub,eye)) \ -, load_word(r_up_in_ptr, R_TapePtr, O_(Binds_ResolveLookAtSub,up_in)) \ -, add_ui_self( R_TapePtr, S_(Binds_ResolveLookAtSub)) \ -, load_word(r_scratch, R_TapePtr, O_(Binds_ResolveLookAtScratch,scratch_base)) \ -, add_ui_self( R_TapePtr, S_(Binds_ResolveLookAtScratch)) /* Stage eye.x/y/z into the scratchpad (atom 6 reads these for the translation - * column). Reuse r_tmp0/r_tmp1/r_tmp2. Offsets via O_(ResolveLookAtScratch,*). */ \ -, load_word(r_tmp0, r_eye_ptr, O_(P3_S4,x)) \ -, load_word(r_tmp1, r_eye_ptr, O_(P3_S4,y)) \ -, load_word(r_tmp2, r_eye_ptr, O_(P3_S4,z)) \ -, nop /* load-delay */ \ -, store_word(r_tmp0, r_scratch, O_(ResolveLookAtScratch,eye.x)) \ -, store_word(r_tmp1, r_scratch, O_(ResolveLookAtScratch,eye.y)) \ -, store_word(r_tmp2, r_scratch, O_(ResolveLookAtScratch,eye.z)) /* Stage up_in.x/y/z into the scratchpad (atom 2 reads these for the outer - * product with uz). Reuse r_tmp0/r_tmp1/r_tmp2. */ \ -, load_word(r_tmp0, r_up_in_ptr, O_(V3_S4,x)) \ -, load_word(r_tmp1, r_up_in_ptr, O_(V3_S4,y)) \ -, load_word(r_tmp2, r_up_in_ptr, O_(V3_S4,z)) \ -, nop /* load-delay */ \ -, store_word(r_tmp0, r_scratch, O_(ResolveLookAtScratch,up_in.x)) \ -, store_word(r_tmp1, r_scratch, O_(ResolveLookAtScratch,up_in.y)) \ -, store_word(r_tmp2, r_scratch, O_(ResolveLookAtScratch,up_in.z)) /* Compute fwd = target - eye. */ \ -, load_word(r_tmp0, r_target_ptr, O_(P3_S4,x)) \ -, load_word(r_tmp1, r_target_ptr, O_(P3_S4,y)) \ -, load_word(r_tmp2, r_target_ptr, O_(P3_S4,z)) \ -, load_word(r_tmp3, r_eye_ptr, O_(P3_S4,x)) \ -, load_word(R_AT, r_eye_ptr, O_(P3_S4,y)) \ -, load_word(R_V0, r_eye_ptr, O_(P3_S4,z)) \ -, nop /* load-delay */ \ -, sub_u(r_tmp0, r_tmp0, r_tmp3) \ -, sub_u(r_tmp1, r_tmp1, R_AT) \ -, sub_u(r_tmp2, r_tmp2, R_V0) /* Store fwd.x/y/z (atom 1 reads these as the normalize src). */ \ -, store_word(r_tmp0, r_scratch, O_(ResolveLookAtScratch,fwd.x)) \ -, store_word(r_tmp1, r_scratch, O_(ResolveLookAtScratch,fwd.y)) \ -, store_word(r_tmp2, r_scratch, O_(ResolveLookAtScratch,fwd.z)) \ -, mac_yield() -WORD_COUNT(mac_resolve_look_at__input_and_sub, 34) - -#define mac_resolve_look_at__cross_uz_up_in_to_right(...) \ - add_si(r_g, r_scratch, O_(ResolveLookAtScratch,uz)) /* r_g = &uz */ \ -, add_si(r_h, r_scratch, O_(ResolveLookAtScratch,up_in)) /* r_h = &up_in */ \ -, add_si(r_f, r_scratch, O_(ResolveLookAtScratch,right)) /* r_f = &right (out) */ \ -, nop /* Load a (uz).x/y/z into r_a/r_b/r_c. */ \ -, load_word(r_a, r_g, O_(V3_S4,x)) \ -, load_word(r_b, r_g, O_(V3_S4,y)) \ -, load_word(r_c, r_g, O_(V3_S4,z)) \ -, nop /* Load b (up_in).x/y/z into r_d + R_AT/R_V0 (hardcoded; reusing the - * body's last two loads is fine because the load-delay slot is the nop - * after the third load, and mtc2 below doesn't read these regs). */ \ -, load_word(r_d, r_h, O_(V3_S4,x)) \ -, load_word(R_AT, r_h, O_(V3_S4,y)) \ -, load_word(R_V0, r_h, O_(V3_S4,z)) \ -, nop /* mtc2 a → IR1/2/3, b → D1/2/3 (VXY0/VZ0/VXY1). */ \ -, gte_mv_to_data_r(r_a, C2_IR1) \ -, gte_mv_to_data_r(r_b, C2_IR2) \ -, gte_mv_to_data_r(r_c, C2_IR3) \ -, gte_mv_to_data_r(r_d, C2_VXY0) /* D1 = b.x */ \ -, gte_mv_to_data_r(R_AT, C2_VZ0) /* D2 = b.y */ \ -, gte_mv_to_data_r(R_V0, C2_VXY1) /* D3 = b.z */ \ -, nop2 /* MTC2 retirement (CPU→COP2 2-slot delay) */ \ -, gte_cmdw_outer_product /* OP fires; MAC1/2/3 = a × b */ /* mfc2 MAC1/2/3 → r_a/r_b/r_c (out.x/y/z). */ \ -, gte_mv_from_data_r(r_a, C2_MAC1) \ -, gte_mv_from_data_r(r_b, C2_MAC2) \ -, gte_mv_from_data_r(r_c, C2_MAC3) \ -, nop /* MFC2 retirement */ /* Store out.x/y/z to r_f (out ptr = scratch+32). */ \ -, store_word(r_a, r_f, O_(V3_S4,x)) \ -, store_word(r_b, r_f, O_(V3_S4,y)) \ -, store_word(r_c, r_f, O_(V3_S4,z)) \ -, mac_yield() -WORD_COUNT(mac_resolve_look_at__cross_uz_up_in_to_right, 29) - -#define mac_resolve_look_at__cross_uz_ux_to_up(...) \ - add_si(r_g, r_scratch, O_(ResolveLookAtScratch,uz)) /* r_g = &uz */ \ -, add_si(r_h, r_scratch, O_(ResolveLookAtScratch,ux)) /* r_h = &ux */ \ -, add_si(r_f, r_scratch, O_(ResolveLookAtScratch,up)) /* r_f = &up (out) */ \ -, nop /* Load a (uz).x/y/z into r_a/r_b/r_c. */ \ -, load_word(r_a, r_g, O_(V3_S4,x)) \ -, load_word(r_b, r_g, O_(V3_S4,y)) \ -, load_word(r_c, r_g, O_(V3_S4,z)) \ -, nop /* Load b (ux).x/y/z into r_d + R_AT/R_V0. */ \ -, load_word(r_d, r_h, O_(V3_S4,x)) \ -, load_word(R_AT, r_h, O_(V3_S4,y)) \ -, load_word(R_V0, r_h, O_(V3_S4,z)) \ -, nop /* mtc2 a → IR1/2/3, b → D1/2/3 (VXY0/VZ0/VXY1). */ \ -, gte_mv_to_data_r(r_a, C2_IR1) \ -, gte_mv_to_data_r(r_b, C2_IR2) \ -, gte_mv_to_data_r(r_c, C2_IR3) \ -, gte_mv_to_data_r(r_d, C2_VXY0) \ -, gte_mv_to_data_r(R_AT, C2_VZ0) \ -, gte_mv_to_data_r(R_V0, C2_VXY1) \ -, nop2 \ -, gte_cmdw_outer_product \ -, gte_mv_from_data_r(r_a, C2_MAC1) \ -, gte_mv_from_data_r(r_b, C2_MAC2) \ -, gte_mv_from_data_r(r_c, C2_MAC3) \ -, nop \ -, store_word(r_a, r_f, O_(V3_S4,x)) \ -, store_word(r_b, r_f, O_(V3_S4,y)) \ -, store_word(r_c, r_f, O_(V3_S4,z)) \ -, mac_yield() -WORD_COUNT(mac_resolve_look_at__cross_uz_ux_to_up, 29) - -#define mac_resolve_look_at__normalize_fwd_to_uz(...) \ - add_si(r_a, r_scratch, O_(ResolveLookAtScratch,fwd)) /* r_a = &fwd */ \ -, add_si(r_b, r_scratch, O_(ResolveLookAtScratch,uz)) /* r_b = &uz */ \ -, nop /* Load src.x/y/z from r_a into r_e/r_f/r_i. */ \ -, load_word(r_e, r_a, O_(V3_S4,x)) \ -, load_word(r_f, r_a, O_(V3_S4,y)) \ -, load_word(r_i, r_a, O_(V3_S4,z)) \ -, nop /* load-delay */ /* Stage 1: mtc2 src → IR1/2/3, SQR fires. */ \ -, gte_mv_to_data_r(r_e, C2_IR1) \ -, gte_mv_to_data_r(r_f, C2_IR2) \ -, gte_mv_to_data_r(r_i, C2_IR3) \ -, nop \ -, gte_cmdw_sqr /* Stage 2: mfc2 MAC1/2/3, sum, mtc2 LZCS. */ \ -, gte_mv_from_data_r(r_d, C2_MAC1) \ -, gte_mv_from_data_r(r_g, C2_MAC2) \ -, gte_mv_from_data_r(r_recip_est, C2_MAC3) \ -, nop \ -, add_u(r_recip_est, r_recip_est, r_g) \ -, add_u(r_recip_est, r_recip_est, r_d) \ -, gte_mv_to_data_r(r_recip_est, C2_LZCS) \ -, nop2 \ -, gte_mv_from_data_r(r_h, C2_LZCR) \ -, nop /* Stage 3: compute shift amount, align |v|² to bit 24. */ \ -, and_i( r_h, r_h, -2) \ -, li_s( r_shift, 31) \ -, sub_s( r_shift, r_shift, r_h) \ -, shift_aright(r_shift, r_shift, 1) \ -, add_si( r_a, r_h, -24) /* r_a = LZCR - 24 (overlapping with r_recip_est; src ptr no longer needed) */ \ -, branch_lt_zero(r_a, atom_offset(srav_path_fwd_to_uz, aligned_done_fwd_to_uz)) \ -, nop \ -, jump_rel(atom_offset(aligned_done_fwd_to_uz, srav_path_fwd_to_uz)) \ -, shift_lleft_var(r_recip_est, r_recip_est, r_a) \ -, atom_label(srav_path_fwd_to_uz) \ -, li_s( r_a, 24) \ -, sub_s( r_a, r_a, r_h) \ -, shift_aright_var(r_recip_est, r_recip_est, r_a) \ -, atom_label(aligned_done_fwd_to_uz) /* r_recip_est holds |v|² aligned to bit 24. */ \ -, add_si( r_recip_est, r_recip_est, -64) \ -, shift_lleft(r_recip_est, r_recip_est, 1) \ -, load_upper_i(r_a, u4_hi(& gte_normalize_sqr_tbl)) \ -, or_i_self(r_a, u4_lo(& gte_normalize_sqr_tbl)) \ -, add_u(r_a, r_a, r_recip_est) \ -, load_half(r_recip_est, r_a, 0) \ -, nop /* Stage 4: GPF + srav finalize. */ \ -, gte_mv_to_data_r(r_recip_est, C2_IR0) \ -, gte_mv_to_data_r(r_e, C2_IR1) \ -, gte_mv_to_data_r(r_f, C2_IR2) \ -, gte_mv_to_data_r(r_i, C2_IR3) \ -, nop2 \ -, gte_cmdw_gpf \ -, gte_mv_from_data_r(r_e, C2_MAC1) \ -, gte_mv_from_data_r(r_f, C2_MAC2) \ -, gte_mv_from_data_r(r_i, C2_MAC3) \ -, shift_aright_var(r_e, r_e, r_shift) \ -, shift_aright_var(r_f, r_f, r_shift) \ -, shift_aright_var(r_i, r_i, r_shift) /* Store result.x/y/z to r_b (dst ptr = scratch+16). */ \ -, store_word(r_e, r_b, O_(V3_S4,x)) \ -, store_word(r_f, r_b, O_(V3_S4,y)) \ -, store_word(r_i, r_b, O_(V3_S4,z)) \ -, mac_yield() -WORD_COUNT(mac_resolve_look_at__normalize_fwd_to_uz, 59) - -#define mac_resolve_look_at__normalize_right_to_ux(...) \ - add_si(r_a, r_scratch, O_(ResolveLookAtScratch,right)) /* r_a = &right */ \ -, add_si(r_b, r_scratch, O_(ResolveLookAtScratch,ux)) /* r_b = &ux */ \ -, nop \ -, load_word(r_e, r_a, O_(V3_S4,x)) \ -, load_word(r_f, r_a, O_(V3_S4,y)) \ -, load_word(r_i, r_a, O_(V3_S4,z)) \ -, nop \ -, gte_mv_to_data_r(r_e, C2_IR1) \ -, gte_mv_to_data_r(r_f, C2_IR2) \ -, gte_mv_to_data_r(r_i, C2_IR3) \ -, nop \ -, gte_cmdw_sqr \ -, gte_mv_from_data_r(r_d, C2_MAC1) \ -, gte_mv_from_data_r(r_g, C2_MAC2) \ -, gte_mv_from_data_r(r_recip_est, C2_MAC3) \ -, nop \ -, add_u(r_recip_est, r_recip_est, r_g) \ -, add_u(r_recip_est, r_recip_est, r_d) \ -, gte_mv_to_data_r(r_recip_est, C2_LZCS) \ -, nop2 \ -, gte_mv_from_data_r(r_h, C2_LZCR) \ -, nop \ -, and_i( r_h, r_h, -2) \ -, li_s( r_shift, 31) \ -, sub_s( r_shift, r_shift, r_h) \ -, shift_aright(r_shift, r_shift, 1) \ -, add_si( r_a, r_h, -24) \ -, branch_lt_zero(r_a, atom_offset(srav_path_right_to_ux, aligned_done_right_to_ux)) \ -, nop \ -, jump_rel(atom_offset(aligned_done_right_to_ux, srav_path_right_to_ux)) \ -, shift_lleft_var(r_recip_est, r_recip_est, r_a) \ -, atom_label(srav_path_right_to_ux) \ -, li_s( r_a, 24) \ -, sub_s( r_a, r_a, r_h) \ -, shift_aright_var(r_recip_est, r_recip_est, r_a) \ -, atom_label(aligned_done_right_to_ux) \ -, add_si( r_recip_est, r_recip_est, -64) \ -, shift_lleft(r_recip_est, r_recip_est, 1) \ -, load_upper_i(r_a, u4_hi(& gte_normalize_sqr_tbl)) \ -, or_i_self(r_a, u4_lo(& gte_normalize_sqr_tbl)) \ -, add_u(r_a, r_a, r_recip_est) \ -, load_half(r_recip_est, r_a, 0) \ -, nop \ -, gte_mv_to_data_r(r_recip_est, C2_IR0) \ -, gte_mv_to_data_r(r_e, C2_IR1) \ -, gte_mv_to_data_r(r_f, C2_IR2) \ -, gte_mv_to_data_r(r_i, C2_IR3) \ -, nop2 \ -, gte_cmdw_gpf \ -, gte_mv_from_data_r(r_e, C2_MAC1) \ -, gte_mv_from_data_r(r_f, C2_MAC2) \ -, gte_mv_from_data_r(r_i, C2_MAC3) \ -, shift_aright_var(r_e, r_e, r_shift) \ -, shift_aright_var(r_f, r_f, r_shift) \ -, shift_aright_var(r_i, r_i, r_shift) \ -, store_word(r_e, r_b, O_(V3_S4,x)) \ -, store_word(r_f, r_b, O_(V3_S4,y)) \ -, store_word(r_i, r_b, O_(V3_S4,z)) \ -, mac_yield() -WORD_COUNT(mac_resolve_look_at__normalize_right_to_ux, 59) - -#define mac_resolve_look_at__normalize_up_to_uy(...) \ - add_si(r_a, r_scratch, O_(ResolveLookAtScratch,up)) /* r_a = &up */ \ -, add_si(r_b, r_scratch, O_(ResolveLookAtScratch,uy)) /* r_b = &uy */ \ -, nop \ -, load_word(r_e, r_a, O_(V3_S4,x)) \ -, load_word(r_f, r_a, O_(V3_S4,y)) \ -, load_word(r_i, r_a, O_(V3_S4,z)) \ -, nop \ -, gte_mv_to_data_r(r_e, C2_IR1) \ -, gte_mv_to_data_r(r_f, C2_IR2) \ -, gte_mv_to_data_r(r_i, C2_IR3) \ -, nop \ -, gte_cmdw_sqr \ -, gte_mv_from_data_r(r_d, C2_MAC1) \ -, gte_mv_from_data_r(r_g, C2_MAC2) \ -, gte_mv_from_data_r(r_recip_est, C2_MAC3) \ -, nop \ -, add_u(r_recip_est, r_recip_est, r_g) \ -, add_u(r_recip_est, r_recip_est, r_d) \ -, gte_mv_to_data_r(r_recip_est, C2_LZCS) \ -, nop2 \ -, gte_mv_from_data_r(r_h, C2_LZCR) \ -, nop \ -, and_i( r_h, r_h, -2) \ -, li_s( r_shift, 31) \ -, sub_s( r_shift, r_shift, r_h) \ -, shift_aright(r_shift, r_shift, 1) \ -, add_si( r_a, r_h, -24) \ -, branch_lt_zero(r_a, atom_offset(srav_path_up_to_uy, aligned_done_up_to_uy)) \ -, nop \ -, jump_rel(atom_offset(aligned_done_up_to_uy, srav_path_up_to_uy)) \ -, shift_lleft_var(r_recip_est, r_recip_est, r_a) \ -, atom_label(srav_path_up_to_uy) \ -, li_s( r_a, 24) \ -, sub_s( r_a, r_a, r_h) \ -, shift_aright_var(r_recip_est, r_recip_est, r_a) \ -, atom_label(aligned_done_up_to_uy) \ -, add_si( r_recip_est, r_recip_est, -64) \ -, shift_lleft(r_recip_est, r_recip_est, 1) \ -, load_upper_i(r_a, u4_hi(& gte_normalize_sqr_tbl)) \ -, or_i_self(r_a, u4_lo(& gte_normalize_sqr_tbl)) \ -, add_u(r_a, r_a, r_recip_est) \ -, load_half(r_recip_est, r_a, 0) \ -, nop \ -, gte_mv_to_data_r(r_recip_est, C2_IR0) \ -, gte_mv_to_data_r(r_e, C2_IR1) \ -, gte_mv_to_data_r(r_f, C2_IR2) \ -, gte_mv_to_data_r(r_i, C2_IR3) \ -, nop2 \ -, gte_cmdw_gpf \ -, gte_mv_from_data_r(r_e, C2_MAC1) \ -, gte_mv_from_data_r(r_f, C2_MAC2) \ -, gte_mv_from_data_r(r_i, C2_MAC3) \ -, shift_aright_var(r_e, r_e, r_shift) \ -, shift_aright_var(r_f, r_f, r_shift) \ -, shift_aright_var(r_i, r_i, r_shift) \ -, store_word(r_e, r_b, O_(V3_S4,x)) \ -, store_word(r_f, r_b, O_(V3_S4,y)) \ -, store_word(r_i, r_b, O_(V3_S4,z)) \ -, mac_yield() -WORD_COUNT(mac_resolve_look_at__normalize_up_to_uy, 59) - -#define mac_resolve_look_at__populate_and_translate(...) \ - load_word(r_look_at, R_TapePtr, O_(Binds_ResolveLookAtPopAndTrans,look_at)) \ -, add_ui_self( R_TapePtr, S_(Binds_ResolveLookAtPopAndTrans)) /* Compute the 4 scratch pointers in their dedicated GPRs. */ \ -, add_si(r_pux, r_scratch, O_(ResolveLookAtScratch,ux)) /* r_pux = &ux */ \ -, add_si(r_puy, r_scratch, O_(ResolveLookAtScratch,uy)) /* r_puy = &uy */ \ -, add_si(r_puz, r_scratch, O_(ResolveLookAtScratch,uz)) /* r_puz = &uz */ \ -, add_si(r_peye, r_scratch, O_(ResolveLookAtScratch,eye)) /* r_peye = &eye */ \ -, nop /* ── m[0] = (S2)ux ── */ \ -, load_word(r_tmp0, r_pux, O_(V3_S4,x)) \ -, load_word(r_tmp1, r_pux, O_(V3_S4,y)) \ -, load_word(r_tmp2, r_pux, O_(V3_S4,z)) \ -, nop \ -, store_half(r_tmp0, r_look_at, O_(MT3_S2S4,m[0][0])) \ -, store_half(r_tmp1, r_look_at, O_(MT3_S2S4,m[0][1])) \ -, store_half(r_tmp2, r_look_at, O_(MT3_S2S4,m[0][2])) /* ── m[1] = (S2)uy ── */ \ -, load_word(r_tmp0, r_puy, O_(V3_S4,x)) \ -, load_word(r_tmp1, r_puy, O_(V3_S4,y)) \ -, load_word(r_tmp2, r_puy, O_(V3_S4,z)) \ -, nop \ -, store_half(r_tmp0, r_look_at, O_(MT3_S2S4,m[1][0])) \ -, store_half(r_tmp1, r_look_at, O_(MT3_S2S4,m[1][1])) \ -, store_half(r_tmp2, r_look_at, O_(MT3_S2S4,m[1][2])) /* ── m[2] = (S2)uz ── */ \ -, load_word(r_tmp0, r_puz, O_(V3_S4,x)) \ -, load_word(r_tmp1, r_puz, O_(V3_S4,y)) \ -, load_word(r_tmp2, r_puz, O_(V3_S4,z)) \ -, nop \ -, store_half(r_tmp0, r_look_at, O_(MT3_S2S4,m[2][0])) \ -, store_half(r_tmp1, r_look_at, O_(MT3_S2S4,m[2][1])) \ -, store_half(r_tmp2, r_look_at, O_(MT3_S2S4,m[2][2])) /* ── Translation column t[i] = R * (-eye) ───────────────────────────── - * pos = -eye: load eye.x/y/z from r_peye, negate via sub_u from R_0. */ \ -, load_word(r_tmp0, r_peye, O_(P3_S4,x)) \ -, load_word(r_tmp1, r_peye, O_(P3_S4,y)) \ -, load_word(r_tmp2, r_peye, O_(P3_S4,z)) \ -, nop \ -, sub_u(r_tmp0, R_0, r_tmp0) /* pos.x = -eye.x */ \ -, sub_u(r_tmp1, R_0, r_tmp1) \ -, sub_u(r_tmp2, R_0, r_tmp2) /* mtc2 IR1/2/3 = pos (for MVMVA — input vector registers). */ \ -, gte_mv_to_data_r(r_tmp0, C2_IR1) \ -, gte_mv_to_data_r(r_tmp1, C2_IR2) \ -, gte_mv_to_data_r(r_tmp2, C2_IR3) \ -, nop2 /* MVMVA: MAC1/2/3 = R * IR with cv=0 (no TR vector), mx=0 (rotation matrix), - * sf=0 (no shift), v=0 (V0 = IR1/2/3, no far-plane clipping). The pre-set - * rotation matrix is the one set by the preceding set_gte_world atom. - * gte_cmdw_mvmva is parameterless and defaults to cv=0/mx=0/sf=0/v=0. */ \ -, gte_cmdw_mvmva \ -, nop /* GTE interlock */ /* mfc2 MAC1/2/3 → r_tmp0/r_tmp1/r_tmp2 (sign-extended into 32-bit GPRs). - * MAC1/2/3 hold R*v with no TR add and no perspective divide — exactly the - * 3 distinct world-space translation values we need for t[0..2]. */ \ -, gte_mv_from_data_r(r_tmp0, C2_MAC1) \ -, gte_mv_from_data_r(r_tmp1, C2_MAC2) \ -, gte_mv_from_data_r(r_tmp2, C2_MAC3) \ -, nop \ -, store_word(r_tmp0, r_look_at, O_(MT3_S2S4,t[0])) \ -, store_word(r_tmp1, r_look_at, O_(MT3_S2S4,t[1])) \ -, store_word(r_tmp2, r_look_at, O_(MT3_S2S4,t[2])) \ -, mac_yield() -WORD_COUNT(mac_resolve_look_at__populate_and_translate, 50) - diff --git a/code/hello_camera/gen/offsets.h b/code/hello_camera/gen/offsets.h index cac639d..2e95a28 100644 --- a/code/hello_camera/gen/offsets.h +++ b/code/hello_camera/gen/offsets.h @@ -8,36 +8,6 @@ #pragma region hello_camera -// --- atom: resolve_look_at__normalize_fwd_to_uz (62 words) --- - -#define _atom_offset_srav_path_fwd_to_uz_aligned_done_fwd_to_uz 6 -#define _atom_offset_aligned_done_fwd_to_uz_srav_path_fwd_to_uz 1 - -enum { - atom_offset_srav_path_fwd_to_uz_aligned_done_fwd_to_uz = _atom_offset_srav_path_fwd_to_uz_aligned_done_fwd_to_uz, - atom_offset_aligned_done_fwd_to_uz_srav_path_fwd_to_uz = _atom_offset_aligned_done_fwd_to_uz_srav_path_fwd_to_uz, -}; - -// --- atom: resolve_look_at__normalize_right_to_ux (62 words) --- - -#define _atom_offset_srav_path_right_to_ux_aligned_done_right_to_ux 6 -#define _atom_offset_aligned_done_right_to_ux_srav_path_right_to_ux 1 - -enum { - atom_offset_srav_path_right_to_ux_aligned_done_right_to_ux = _atom_offset_srav_path_right_to_ux_aligned_done_right_to_ux, - atom_offset_aligned_done_right_to_ux_srav_path_right_to_ux = _atom_offset_aligned_done_right_to_ux_srav_path_right_to_ux, -}; - -// --- atom: resolve_look_at__normalize_up_to_uy (62 words) --- - -#define _atom_offset_srav_path_up_to_uy_aligned_done_up_to_uy 6 -#define _atom_offset_aligned_done_up_to_uy_srav_path_up_to_uy 1 - -enum { - atom_offset_srav_path_up_to_uy_aligned_done_up_to_uy = _atom_offset_srav_path_up_to_uy_aligned_done_up_to_uy, - atom_offset_aligned_done_up_to_uy_srav_path_up_to_uy = _atom_offset_aligned_done_up_to_uy_srav_path_up_to_uy, -}; - // --- atom: pad_input_cube_rotation (60 words) --- #define _atom_offset_dpad_left_exit_dpad_left 6 diff --git a/code/hello_camera/hello_camera.atom.c b/code/hello_camera/hello_camera.atom.c index 9f36ef2..50b9ee4 100644 --- a/code/hello_camera/hello_camera.atom.c +++ b/code/hello_camera/hello_camera.atom.c @@ -173,24 +173,22 @@ typedef Struct_(ResolveLookAtScratch) { }; /* ─── resolve_look_at bundle chain atoms ──────────────────────────── - * 7 unique atom procs in the resolve_look_at bundle (4 chain atoms + 3 normalize variants). - * All 7 are runtime-built MipsAtom_Proc_ atoms: each function declares a static MipsCode[] body, - * then calls atombuilder_unroll() to append it to the caller's MipsAtomBuilder arena. - * resolve_look_at_init() uses this pattern to pre-build the bundle into the static arena (smem.resolve_look_at_arena). + * 4 unique atom procs in the resolve_look_at bundle (4 chain atoms + 3 calls to generic normalize_v3s4_proc). + * All 4 chain atoms are runtime-built MipsAtom_Proc_ atoms: each function declares a static MipsCode[] body, + * then calls atombuilder_unroll() to append it to the caller's MipsAtomBuilder arena. resolve_look_at_init() + * uses this pattern to pre-build the bundle into the static arena (smem.resolve_look_at_arena). * * Atom roster: - * 0: resolve_look_at__input_and_sub (chain atom) - * 1: resolve_look_at__normalize_fwd_to_uz (normalize wrapper) - * 2: resolve_look_at__cross_uz_up_in_to_right (chain atom) - * 3: resolve_look_at__normalize_right_to_ux (normalize wrapper) - * 4: resolve_look_at__cross_uz_ux_to_up (chain atom) - * 5: resolve_look_at__normalize_up_to_uy (normalize wrapper) - * 6: resolve_look_at__populate_and_translate (chain atom) + * 0: resolve_look_at__input_and_sub (chain atom) + * 1: normalize_v3s4_proc (gte.atom.c) (generic normalize; called for fwd→uz) + * 2: resolve_look_at__cross_uz_up_in_to_right (chain atom) + * 3: normalize_v3s4_proc (gte.atom.c) (generic normalize; called for right→ux) + * 4: resolve_look_at__cross_uz_ux_to_up (chain atom) + * 5: normalize_v3s4_proc (gte.atom.c) (generic normalize; called for up→uy) + * 6: resolve_look_at__populate_and_translate (chain atom) * - * The 3 normalize wrappers are CHAIN-SPECIFIC — they hardcode src/dst scratch offsets in the body - * (computed via r_scratch + O_(ResolveLookAtScratch, fld)). - * The generic normalize_v3s4_proc (in gte.atom.c) takes src/dst as GPR parameters and is NOT used by this bundle. - * (Layering rule: gte.atom.c contains only generic GTE primitives; bundle-specific code are within this file.) + * The generic normalize_v3s4_proc is a parameterized 4-stage GTE normalize (SQR → mfc2 → LZCS → GPF → srav); + * it accepts scratch base + offset args so any caller (with a scratch base + struct schema) can use it. */ typedef Struct_(Binds_ResolveLookAtSub) { @@ -392,248 +390,6 @@ I_ void resolve_look_at__cross_uz_ux_to_up_proc(MipsAtomBuilder_R ab, U4 r_scrat mac_yield() }) -/* Atoms 1, 3, 5 in the bundle: chain-specific normalize wrappers around the generic normalize_v3s4_proc (gte.atom.c). - * The generic proc takes src/dst as GPR parameters; these wrappers HARDCODE src/dst via r_scratch + O_(ResolveLookAtScratch, fld) - * so the C-side bundle helper doesn't need to push scratchpad addresses via tb_data between atoms. - * - * The 4-stage normalize body (SQR → mfc2 → LZCS → GPF → srav) is identical to the generic version (GPR-renamed); cycle counts match. - * The only difference is the (src, dst) scratch offsets and the per-proc atom_label suffixes - * (srav_path_fwd_to_uz, srav_path_right_to_ux, srav_path_up_to_uy) so the per-proc-instance offsets are emitted disjointly in gen/offsets.h. - * - * GPR pool (10 free regs: R_T0..R_T3, R_T5..R_T7, R_V0, R_V1, R_AT; R_T4 reserved for R_ResolveScratch): - * r_a : src ptr (overlaps with r_recip_est carrier after the 3 src-loads) - * r_b : dst ptr (saved throughout) - * r_e/r_f/r_i : src.x/y/z → result.x/y/z (preserved across stages 1-2 via r_d/r_g/r_recip_est scratch) - * r_d/r_g : MAC1/2 scratch (dead after stage 2) - * r_h : LZCR (saved across stages 3-4) - * r_recip_est : |v|² accumulator + sqrtbl[index] + 1/|v| (saved throughout) - * r_shift : final srav amount (saved across stages 3-4) - * - * The Lua metaprogram (Task 12.10) auto-emits: - * - `mac_resolve_look_at__normalize__to_` alias in gen/macs.h - * - `atom_offset__srav_path__to___aligned_done__to_` defs in gen/offsets.h - */ - -/* Atom 1: normalize fwd (scratch+0) → uz (scratch+16). */ -I_ void resolve_look_at__normalize_fwd_to_uz_proc(MipsAtomBuilder_R ab, U4 r_scratch - , U4 r_a, U4 r_b /* src/dst scratch pointers */ - , U4 r_e, U4 r_f, U4 r_i /* src.x/y/z → result.x/y/z */ - , U4 r_d, U4 r_g /* MAC1/2 scratch (dead after stage 2) */ - , U4 r_h /* LZCR */ - , U4 r_recip_est - , U4 r_shift -) MipsAtom_Proc_(resolve_look_at__normalize_fwd_to_uz, ab, { - /* Compute src/dst pointers from r_scratch. */ - add_si(r_a, r_scratch, O_(ResolveLookAtScratch,fwd)), /* r_a = &fwd */ - add_si(r_b, r_scratch, O_(ResolveLookAtScratch,uz)), /* r_b = &uz */ - nop, - - /* Load src.x/y/z from r_a into r_e/r_f/r_i. */ - load_word(r_e, r_a, O_(V3_S4,x)), - load_word(r_f, r_a, O_(V3_S4,y)), - load_word(r_i, r_a, O_(V3_S4,z)), - nop, /* load-delay */ - - /* Stage 1: mtc2 src → IR1/2/3, SQR fires. */ - gte_mv_to_data_r(r_e, C2_IR1), - gte_mv_to_data_r(r_f, C2_IR2), - gte_mv_to_data_r(r_i, C2_IR3), - nop, gte_cmdw_sqr, - - /* Stage 2: mfc2 MAC1/2/3, sum, mtc2 LZCS. */ - gte_mv_from_data_r(r_d, C2_MAC1), - gte_mv_from_data_r(r_g, C2_MAC2), - gte_mv_from_data_r(r_recip_est, C2_MAC3), - nop, - add_u(r_recip_est, r_recip_est, r_g), - add_u(r_recip_est, r_recip_est, r_d), - gte_mv_to_data_r(r_recip_est, C2_LZCS), - nop2, - gte_mv_from_data_r(r_h, C2_LZCR), - nop, - - /* Stage 3: compute shift amount, align |v|² to bit 24. */ - and_i( r_h, r_h, -2), - li_s( r_shift, 31), - sub_s( r_shift, r_shift, r_h), - shift_aright(r_shift, r_shift, 1), - add_si( r_a, r_h, -24), /* r_a = LZCR - 24 (overlapping with r_recip_est; src ptr no longer needed) */ - branch_lt_zero(r_a, atom_offset(srav_path_fwd_to_uz, aligned_done_fwd_to_uz)), nop, - jump_rel(atom_offset(aligned_done_fwd_to_uz, srav_path_fwd_to_uz)), - shift_lleft_var(r_recip_est, r_recip_est, r_a), - atom_label(srav_path_fwd_to_uz) - li_s( r_a, 24), - sub_s( r_a, r_a, r_h), - shift_aright_var(r_recip_est, r_recip_est, r_a), - atom_label(aligned_done_fwd_to_uz) - /* r_recip_est holds |v|² aligned to bit 24. */ - add_si( r_recip_est, r_recip_est, -64), - shift_lleft(r_recip_est, r_recip_est, 1), - load_upper_i(r_a, u4_hi(& gte_normalize_sqr_tbl)), - or_i_self(r_a, u4_lo(& gte_normalize_sqr_tbl)), - add_u(r_a, r_a, r_recip_est), - load_half(r_recip_est, r_a, 0), - nop, - - /* Stage 4: GPF + srav finalize. */ - gte_mv_to_data_r(r_recip_est, C2_IR0), - gte_mv_to_data_r(r_e, C2_IR1), - gte_mv_to_data_r(r_f, C2_IR2), - gte_mv_to_data_r(r_i, C2_IR3), - nop2, - gte_cmdw_gpf, - gte_mv_from_data_r(r_e, C2_MAC1), - gte_mv_from_data_r(r_f, C2_MAC2), - gte_mv_from_data_r(r_i, C2_MAC3), - shift_aright_var(r_e, r_e, r_shift), - shift_aright_var(r_f, r_f, r_shift), - shift_aright_var(r_i, r_i, r_shift), - - /* Store result.x/y/z to r_b (dst ptr = scratch+16). */ - store_word(r_e, r_b, O_(V3_S4,x)), - store_word(r_f, r_b, O_(V3_S4,y)), - store_word(r_i, r_b, O_(V3_S4,z)), - - mac_yield() -}) - -/* Atom 3: normalize right (scratch+32) → ux (scratch+48). */ -I_ void resolve_look_at__normalize_right_to_ux_proc(MipsAtomBuilder_R ab, U4 r_scratch - , U4 r_a, U4 r_b - , U4 r_e, U4 r_f, U4 r_i - , U4 r_d, U4 r_g - , U4 r_h - , U4 r_recip_est - , U4 r_shift -) MipsAtom_Proc_(resolve_look_at__normalize_right_to_ux, ab, { - add_si(r_a, r_scratch, O_(ResolveLookAtScratch,right)), /* r_a = &right */ - add_si(r_b, r_scratch, O_(ResolveLookAtScratch,ux)), /* r_b = &ux */ - nop, - load_word(r_e, r_a, O_(V3_S4,x)), - load_word(r_f, r_a, O_(V3_S4,y)), - load_word(r_i, r_a, O_(V3_S4,z)), - nop, - gte_mv_to_data_r(r_e, C2_IR1), - gte_mv_to_data_r(r_f, C2_IR2), - gte_mv_to_data_r(r_i, C2_IR3), - nop, gte_cmdw_sqr, - gte_mv_from_data_r(r_d, C2_MAC1), - gte_mv_from_data_r(r_g, C2_MAC2), - gte_mv_from_data_r(r_recip_est, C2_MAC3), - nop, - add_u(r_recip_est, r_recip_est, r_g), - add_u(r_recip_est, r_recip_est, r_d), - gte_mv_to_data_r(r_recip_est, C2_LZCS), - nop2, - gte_mv_from_data_r(r_h, C2_LZCR), - nop, - and_i( r_h, r_h, -2), - li_s( r_shift, 31), - sub_s( r_shift, r_shift, r_h), - shift_aright(r_shift, r_shift, 1), - add_si( r_a, r_h, -24), - branch_lt_zero(r_a, atom_offset(srav_path_right_to_ux, aligned_done_right_to_ux)), nop, - jump_rel(atom_offset(aligned_done_right_to_ux, srav_path_right_to_ux)), - shift_lleft_var(r_recip_est, r_recip_est, r_a), - atom_label(srav_path_right_to_ux) - li_s( r_a, 24), - sub_s( r_a, r_a, r_h), - shift_aright_var(r_recip_est, r_recip_est, r_a), - atom_label(aligned_done_right_to_ux) - add_si( r_recip_est, r_recip_est, -64), - shift_lleft(r_recip_est, r_recip_est, 1), - load_upper_i(r_a, u4_hi(& gte_normalize_sqr_tbl)), - or_i_self(r_a, u4_lo(& gte_normalize_sqr_tbl)), - add_u(r_a, r_a, r_recip_est), - load_half(r_recip_est, r_a, 0), - nop, - gte_mv_to_data_r(r_recip_est, C2_IR0), - gte_mv_to_data_r(r_e, C2_IR1), - gte_mv_to_data_r(r_f, C2_IR2), - gte_mv_to_data_r(r_i, C2_IR3), - nop2, - gte_cmdw_gpf, - gte_mv_from_data_r(r_e, C2_MAC1), - gte_mv_from_data_r(r_f, C2_MAC2), - gte_mv_from_data_r(r_i, C2_MAC3), - shift_aright_var(r_e, r_e, r_shift), - shift_aright_var(r_f, r_f, r_shift), - shift_aright_var(r_i, r_i, r_shift), - store_word(r_e, r_b, O_(V3_S4,x)), - store_word(r_f, r_b, O_(V3_S4,y)), - store_word(r_i, r_b, O_(V3_S4,z)), - mac_yield() -}) - -/* Atom 5: normalize up (scratch+64) → uy (scratch+80). */ -I_ void resolve_look_at__normalize_up_to_uy_proc(MipsAtomBuilder_R ab - , U4 r_scratch - , U4 r_a, U4 r_b - , U4 r_e, U4 r_f, U4 r_i - , U4 r_d, U4 r_g - , U4 r_h - , U4 r_recip_est - , U4 r_shift -) MipsAtom_Proc_(resolve_look_at__normalize_up_to_uy, ab, { - add_si(r_a, r_scratch, O_(ResolveLookAtScratch,up)), /* r_a = &up */ - add_si(r_b, r_scratch, O_(ResolveLookAtScratch,uy)), /* r_b = &uy */ - nop, - load_word(r_e, r_a, O_(V3_S4,x)), - load_word(r_f, r_a, O_(V3_S4,y)), - load_word(r_i, r_a, O_(V3_S4,z)), - nop, - gte_mv_to_data_r(r_e, C2_IR1), - gte_mv_to_data_r(r_f, C2_IR2), - gte_mv_to_data_r(r_i, C2_IR3), - nop, gte_cmdw_sqr, - gte_mv_from_data_r(r_d, C2_MAC1), - gte_mv_from_data_r(r_g, C2_MAC2), - gte_mv_from_data_r(r_recip_est, C2_MAC3), - nop, - add_u(r_recip_est, r_recip_est, r_g), - add_u(r_recip_est, r_recip_est, r_d), - gte_mv_to_data_r(r_recip_est, C2_LZCS), - nop2, - gte_mv_from_data_r(r_h, C2_LZCR), - nop, - and_i( r_h, r_h, -2), - li_s( r_shift, 31), - sub_s( r_shift, r_shift, r_h), - shift_aright(r_shift, r_shift, 1), - add_si( r_a, r_h, -24), - branch_lt_zero(r_a, atom_offset(srav_path_up_to_uy, aligned_done_up_to_uy)), nop, - jump_rel(atom_offset(aligned_done_up_to_uy, srav_path_up_to_uy)), - shift_lleft_var(r_recip_est, r_recip_est, r_a), - atom_label(srav_path_up_to_uy) - li_s( r_a, 24), - sub_s( r_a, r_a, r_h), - shift_aright_var(r_recip_est, r_recip_est, r_a), - atom_label(aligned_done_up_to_uy) - add_si( r_recip_est, r_recip_est, -64), - shift_lleft(r_recip_est, r_recip_est, 1), - load_upper_i(r_a, u4_hi(& gte_normalize_sqr_tbl)), - or_i_self(r_a, u4_lo(& gte_normalize_sqr_tbl)), - add_u(r_a, r_a, r_recip_est), - load_half(r_recip_est, r_a, 0), - nop, - gte_mv_to_data_r(r_recip_est, C2_IR0), - gte_mv_to_data_r(r_e, C2_IR1), - gte_mv_to_data_r(r_f, C2_IR2), - gte_mv_to_data_r(r_i, C2_IR3), - nop2, - gte_cmdw_gpf, - gte_mv_from_data_r(r_e, C2_MAC1), - gte_mv_from_data_r(r_f, C2_MAC2), - gte_mv_from_data_r(r_i, C2_MAC3), - shift_aright_var(r_e, r_e, r_shift), - shift_aright_var(r_f, r_f, r_shift), - shift_aright_var(r_i, r_i, r_shift), - store_word(r_e, r_b, O_(V3_S4,x)), - store_word(r_f, r_b, O_(V3_S4,y)), - store_word(r_i, r_b, O_(V3_S4,z)), - mac_yield() -}) - typedef Struct_(Binds_ResolveLookAtPopAndTrans) { U4 look_at; /* U4 (MT3_S2S4* — destination matrix address) */ }; diff --git a/code/hello_camera/hello_camera.c b/code/hello_camera/hello_camera.c index e63a7f7..55e9251 100644 --- a/code/hello_camera/hello_camera.c +++ b/code/hello_camera/hello_camera.c @@ -133,20 +133,27 @@ resolve_look_at_c11(MT3_S2S4* look_at, P3_S4* eye, P3_S4* target, V3_S4* up_in) } /* Pre-build all 7 chain atoms of the resolve_look_at bundle into the static arena. - * Called ONCE from main() before the frame loop. - * After this returns, the smem.resolve_look_at_atom_addrs[] array contains valid MIPS atom pointers + * Called ONCE from main() before the frame loop. + * After this returns, the smem.resolve_look_at_atom_addrs[] array contains valid MIPS atom pointers * for the frame-time bundle helper to emit via tb_emit(tb, captured_addr). * - * 7 atoms are within hello_camera.atom.c: + * 4 unique procs in hello_camera.atom.c (chain atoms 0, 2, 4, 6); atoms 1, 3, 5 + * share the GENERIC normalize_v3s4_proc from gte.atom.c (called 3x with different + * O_(ResolveLookAtScratch,...) offsets): * 0: resolve_look_at__input_and_sub_proc - * 1: resolve_look_at__normalize_fwd_to_uz_proc + * 1: normalize_v3s4_proc (fwd → uz; offsets 0, 16) * 2: resolve_look_at__cross_uz_up_in_to_right_proc - * 3: resolve_look_at__normalize_right_to_ux_proc + * 3: normalize_v3s4_proc (right → ux; offsets 32, 48) * 4: resolve_look_at__cross_uz_ux_to_up_proc - * 5: resolve_look_at__normalize_up_to_uy_proc + * 5: normalize_v3s4_proc (up → uy; offsets 64, 80) * 6: resolve_look_at__populate_and_translate_proc * - * (gte.atom.c contains only normalize_v3s4_proc — bundle-specific scratch layout is no longer exposed to the GTE primitives file.) + * Task 12.16 promotion: the bundle-specific resolve_look_at__chain_normalize_proc + * has been promoted to the generic normalize_v3s4_proc (gte.atom.c), which now + * takes r_scratch + r_src_offset + r_dst_offset as U4 parameters. The 3 callers + * pass O_(ResolveLookAtScratch,...) macros as offset args. The metaprogram emits + * one set of `atom_offset__normalize_v3s4__srav_path__aligned_done` defs + * (namespaced by atom name) in duffle/gen/offsets.h, shared by all 3 callers. * * GPR pool per atom: 10 free GPRs (R_T0..R_T3 + R_T5..R_T7 + R_V0 + R_V1 + R_AT). * R_T4 is reserved as the wave-context carrier (R_ResolveScratch). @@ -168,65 +175,76 @@ internal void resolve_look_at_init(void) { R_T2, /* r_up_in_ptr (popped from tape) */ R_T3, R_T5, R_T6, R_T7); /* r_tmp<0-3> */ - /* Atom 1: resolve_look_at__normalize_fwd_to_uz — src=scratch+0, dst=scratch+16 (HARDCODED in body). - * GPR pool: r_scratch (R_T4 carrier) + 10 body GPRs = 11. - * r_a/r_b (R_T0/R_T1) : src/dst pointers (r_a overlaps r_recip_est after the loads) - * r_e/r_f/r_i (R_T2/R_T3/R_T5) : src.x/y/z → result.x/y/z - * r_d/r_g (R_T6/R_T7) : MAC1/2 scratch (dead after stage 2) - * r_h (R_V0) : LZCR - * r_recip_est (R_V1), r_shift (R_AT) : saved throughout */ + /* Atom 1: normalize_v3s4_proc (generic, from gte.atom.c) — src=scratch+0=fwd, dst=scratch+16=uz. + * The proc takes r_src_offset + r_dst_offset as U4 PARAMETERS — we pass the O_(...) macros here (evaluating to numeric literals 0 and 16). + * The 4-stage body is identical across the 3 call sites (atoms 1, 3, 5); only the offset args differ. + * GPR pool: r_scratch (R_T4 carrier) + 9 body GPRs = 10. + * r_src_ptr (R_T0) : src ptr + * r_dst_ptr (R_T1) : dst ptr + * r_tmp (R_T2) : unused (reserved for symmetry) + * r_mac1_scratch (R_T3) : MAC1 scratch + * r_mac2_scratch (R_T5) : src.x → result.x (carries through stages 1-2) + * r_recip_est (R_T6) : src.y → result.y + * r_lzcr (R_T7) : |v|² accumulator + srav amount (single reg) + * r_shift (R_V0) : LZCR (saved across stages 3-4) + * r_branch_tmp (R_V1) : src.z → result.z (reused after stage 1) + */ smem.resolve_look_at_atom_addrs[1] = (MipsAtom*)u4_v(ab->start + ab->used * sizeof(U4)); - resolve_look_at__normalize_fwd_to_uz_proc(ab, - R_ResolveScratch, /* r_scratch (wave-context carrier; src/dst base) */ - R_T0, R_T1, /* r_a, r_b (src/dst ptrs) */ - R_T2, R_T3, R_T5, /* r_e, r_f, r_i (src components) */ - R_T6, R_T7, /* r_d, r_g (MAC scratch) */ - R_V0, /* r_h (LZCR) */ - R_V1, /* r_recip_est */ - R_AT); /* r_shift */ + normalize_v3s4_proc(ab, R_ResolveScratch, /* r_scratch (wave-context carrier) */ + O_(ResolveLookAtScratch, fwd), /* r_src_offset = 0 */ + O_(ResolveLookAtScratch, uz), /* r_dst_offset = 16 */ + R_T0, R_T1, R_T2, /* r_src_ptr, r_dst_ptr, r_tmp */ + R_T3, /* r_mac1_scratch */ + R_T5, /* r_mac2_scratch */ + R_T6, /* r_recip_est */ + R_T7, /* r_lzcr */ + R_V0, /* r_shift */ + R_V1); /* r_branch_tmp */ /* Atom 2: resolve_look_at__cross_uz_up_in_to_right — a=scratch+16, b=scratch+128, * out=scratch+32 (HARDCODED in body). GPR pool: r_scratch + 7 body + R_AT + R_V0 = 10. */ smem.resolve_look_at_atom_addrs[2] = (MipsAtom*)u4_v(ab->start + ab->used * sizeof(U4)); - resolve_look_at__cross_uz_up_in_to_right_proc(ab, - R_ResolveScratch, /* r_scratch (wave-context carrier; src/dst base) */ - R_T0, R_T1, R_T2, /* r_a, r_b, r_c (a.x/y/z → out.x/y/z) */ - R_T3, /* r_d (b.x) */ - R_T5, /* r_f (out ptr = scratch+32) */ - R_T6, /* r_g (a ptr = scratch+16) */ - R_T7); /* r_h (b ptr = scratch+128) */ + resolve_look_at__cross_uz_up_in_to_right_proc(ab, R_ResolveScratch, /* r_scratch (wave-context carrier; src/dst base) */ + R_T0, R_T1, R_T2, /* r_a, r_b, r_c (a.x/y/z → out.x/y/z) */ + R_T3, /* r_d (b.x) */ + R_T5, /* r_f (out ptr = scratch+32) */ + R_T6, /* r_g (a ptr = scratch+16) */ + R_T7); /* r_h (b ptr = scratch+128) */ - /* Atom 3: resolve_look_at__normalize_right_to_ux — src=scratch+32, dst=scratch+48 (HARDCODED). */ + /* Atom 3: normalize_v3s4_proc (generic, from gte.atom.c) — src=scratch+32=right, dst=scratch+48=ux. */ smem.resolve_look_at_atom_addrs[3] = (MipsAtom*)u4_v(ab->start + ab->used * sizeof(U4)); - resolve_look_at__normalize_right_to_ux_proc(ab, - R_ResolveScratch, - R_T0, R_T1, - R_T2, R_T3, R_T5, - R_T6, R_T7, + normalize_v3s4_proc(ab, R_ResolveScratch, + O_(ResolveLookAtScratch, right), /* r_src_offset = 32 */ + O_(ResolveLookAtScratch, ux), /* r_dst_offset = 48 */ + R_T0, R_T1, R_T2, + R_T3, + R_T5, + R_T6, + R_T7, R_V0, - R_V1, - R_AT); + R_V1); /* Atom 4: resolve_look_at__cross_uz_ux_to_up — a=scratch+16, b=scratch+48, out=scratch+64 (HARDCODED). */ smem.resolve_look_at_atom_addrs[4] = (MipsAtom*)u4_v(ab->start + ab->used * sizeof(U4)); - resolve_look_at__cross_uz_ux_to_up_proc(ab, - R_ResolveScratch, + resolve_look_at__cross_uz_ux_to_up_proc(ab, R_ResolveScratch, R_T0, R_T1, R_T2, R_T3, R_T5, /* r_f (out ptr = scratch+64) */ R_T6, /* r_g (a ptr = scratch+16) */ R_T7); /* r_h (b ptr = scratch+48) */ - /* Atom 5: resolve_look_at__normalize_up_to_uy — src=scratch+64, dst=scratch+80 (HARDCODED). */ + /* Atom 5: normalize_v3s4_proc (generic, from gte.atom.c) — src=scratch+64=up, dst=scratch+80=uy. */ smem.resolve_look_at_atom_addrs[5] = (MipsAtom*)u4_v(ab->start + ab->used * sizeof(U4)); - resolve_look_at__normalize_up_to_uy_proc(ab, - R_ResolveScratch, - R_T0, R_T1, - R_T2, R_T3, R_T5, - R_T6, R_T7, + normalize_v3s4_proc(ab, R_ResolveScratch, + O_(ResolveLookAtScratch, up), /* r_src_offset = 64 */ + O_(ResolveLookAtScratch, uy), /* r_dst_offset = 80 */ + R_T0, R_T1, R_T2, + R_T3, + R_T5, + R_T6, + R_T7, R_V0, - R_V1, - R_AT); + R_V1); /* Atom 6: resolve_look_at__populate_and_translate — write look_at->m[][] from ux/uy/uz (computed from r_scratch+offset internally), then compute translation column t[] = R * (-eye). GPR pool: r_look_at + r_scratch + 4 ptr regs + 3 tmp regs = 9. */ @@ -340,10 +358,6 @@ void update(PrimitiveArena* pa, U4* ordering_buf) } // Camera look at (Tape) { - MT3_S2S4* look_at = & smem.cam.look_at; - P3_S4* eye = & smem.cam.pos; - V3_S4* up_in = & v3s4(0, -fp_one, 0); - tb.used = 0; tb_scope_run(& tb) { resolve_look_at(& tb, & smem.cam.look_at, & smem.cam.pos, & smem.cube.pos, & v3s4(0, -fp_one, 0)); } diff --git a/scripts/duffle.lua b/scripts/duffle.lua index b32a0c8..33c7377 100644 --- a/scripts/duffle.lua +++ b/scripts/duffle.lua @@ -217,7 +217,7 @@ local function parse_path_root(input) if not server_end or server_end == server_start then error("UNC path requires //server/share: " .. input, 3) end - local server = input:sub(server_start, server_end - 1) + local server = input:sub(server_start, server_end - 1) local share_start = server_end + 1 while input:sub(share_start, share_start) == "/" do share_start = share_start + 1 @@ -515,8 +515,7 @@ local function splice_c_lines(source) local splice_len = nil if byte == BYTE_BACKSLASH and source:byte(pos + 1) == BYTE_NEWLINE then splice_len = 2 - elseif byte == BYTE_BACKSLASH and source:byte(pos + 1) == BYTE_CR - and source:byte(pos + 2) == BYTE_NEWLINE then + elseif byte == BYTE_BACKSLASH and source:byte(pos + 1) == BYTE_CR and source:byte(pos + 2) == BYTE_NEWLINE then splice_len = 3 end @@ -2095,7 +2094,8 @@ local E_MAC_PREFIX_LEN = 4 --- * Unknown `mac_X` (not in `component_index`): fall back to `word_counts[ident]` if present; otherwise emit one opaque event so the cycle budget accounts for the word. --- * Marker Tokens (`atom_label(...)` / `atom_offset(...)`): Zero events (they are pure metaprogram hints). --- ---- Cycle protection: a per-expansion `visiting` set tracks components currently on the expansion stack; a re-entry produces a deterministic `{kind = "cycle", ...}` error and aborts that branch (does NOT hang, does NOT recurse). +--- Cycle protection: a per-expansion `visiting` set tracks components currently on the expansion stack; +--- a re-entry produces a deterministic `{kind = "cycle", ...}` error and aborts that branch (does NOT hang, does NOT recurse). --- --- Pure: reads `body_entry` / `component_index` / `word_counts`. Memoization is the caller's responsibility. --- Callers wanting `word_events` / `word_event_errors` precomputed for many atoms should memoize them per atom. diff --git a/scripts/duffle_paths.lua b/scripts/duffle_paths.lua index b0d6d65..cdd6fe7 100644 --- a/scripts/duffle_paths.lua +++ b/scripts/duffle_paths.lua @@ -47,15 +47,14 @@ local function find_repo_root() return root end ---- Set `package.path` (for `require("duffle")` + `require("passes.X")`) and ---- `package.cpath` (for `lpeg.dll`). +--- Set `package.path` (for `require("duffle")` + `require("passes.X")`) and `package.cpath` (for `lpeg.dll`). --- --- This script does NOT touch the OS environment: no `os.setenv`, no `os.putenv`, no `$PATH` mods. --- It just sets `package.path` and `package.cpath` (the standard Lua way to register module search dirs). --- lpeg is built by `update_deps.ps1` to `toolchain/lpeg/`, --- which we wire into `package.cpath` here (so `require("lpeg")` from `duffle.lua` resolves without any global state). function M.setup() - local repo_root = find_repo_root() + local repo_root = find_repo_root() if not repo_root then -- Unreachable in practice: find_repo_root() derives the repo root from this script's -- own source path via debug.getinfo(1, "S").source (no subprocess, no git CLI, <1ms). diff --git a/scripts/passes/auto_reg.lua b/scripts/passes/auto_reg.lua index dc84097..2d40b3f 100644 --- a/scripts/passes/auto_reg.lua +++ b/scripts/passes/auto_reg.lua @@ -144,10 +144,10 @@ local function build_user_pins(corpus) end -- Find every physical GPR referenced in the atom body, via EITHER: --- (a) a hardcoded physical GPR ident (R_T\d+|R_V\d+|R_A\d+|R_S\d+) — the existing regex; --- (b) an alias ident (R_) resolved via alias_to_gpr back to its physical GPR ident. --- Returns { [physical_gpr_ident] = count }. The clash-detection and source-pool-exclusion logic --- only needs the presence of each GPR (boolean test), but keeping the count preserves the +-- (a) A hardcoded physical GPR ident (R_T\d+|R_V\d+|R_A\d+|R_S\d+) — the existing regex; +-- (b) An alias ident (R_) resolved via alias_to_gpr back to its physical GPR ident. +-- Returns { [physical_gpr_ident] = count }. Clash-detection and source-pool-exclusion logic +-- only needs the presence of each GPR (boolean test), but keeping count preserves the -- original find_hardcoded_rn shape so callers can switch without churn. -- The alias pattern is sorted lexicographically to keep the regex deterministic. local function find_used_gprs(body_text, alias_to_gpr) diff --git a/scripts/passes/components.lua b/scripts/passes/components.lua index 06bc21d..da25de2 100644 --- a/scripts/passes/components.lua +++ b/scripts/passes/components.lua @@ -3,9 +3,12 @@ --- Ownership: `corpus.word_counts`, `corpus.components`, and `corpus.component_body_index`. --- Scanner owns `declaration_comment` and `debug_skip` on each declaration record; this pass projects both forward. --- ---- Reads the pre-scanned SourceScan payload from `duffle.scan_source` for `MipsAtomComp_(ac_X)`, `MipsAtomComp_Proc_(ac_X, { body })`, and `MipsAtom_Proc_(X, ab, { body })` declarations, +--- Reads the pre-scanned SourceScan payload from `duffle.scan_source` for `MipsAtomComp_(ac_X)` and `MipsAtomComp_Proc_(ac_X, { body })` declarations (kind="comp_bare" / "comp_proc"), --- then resolves the function-args string from the preceding `FI_ Slice_MipsCode ac_X(...)` declaration via a backward walk. --- +--- `MipsAtom_Proc_(X, ab, { body })` declarations (kind="atom_proc") are ATOMS, not components, and are deliberately excluded — +--- atoms get emitted via `tb_emit(tb, code_)` linker symbols, not inlined as `mac_*` macros. +--- --- Emits one `gen/macs.h` per *immediate source directory* with `#define mac_X(sig) \` macros plus `WORD_COUNT(mac_X, N)` entries for downstream offset computation. --- All sources inside the same directory contribute to the same file (per-directory aggregation). --- The directory itself is the namespace, so the filename does not repeat the module name. @@ -76,7 +79,7 @@ local MACS_FILENAME = "macs.h" --- @field args string|nil -- Function-args string (function form only) --- @field line integer -- Source line of the declaration --- @field comment string|nil -- Scanner-owned `declaration_comment`; the components pass reads it from the scanner record ---- @field kind string -- "comp_bare" | "comp_proc" | "atom_proc" +--- @field kind string -- "comp_bare" | "comp_proc" (atom_proc is NOT a component — see `project_components`) --- @field debug_skip boolean -- Mirror of `a.debug_skip` (scanner-owned); true iff a bare `atom_dbg_skip` marker immediately preceded the declaration -- ════════════════════════════════════════════════════════════════════════════ @@ -200,16 +203,17 @@ end local function project_components(source, scan) local out = {} for _, a in ipairs(scan.atoms) do - if a.kind == "comp_bare" or a.kind == "comp_proc" or a.kind == "atom_proc" then - -- `MipsAtom_Proc_` atoms have no `FI_ Slice_MipsCode ac_X(...)` function-decl prelude - -- (the macro sits inside a wrapping `I_ void (...)` body), so the function-args - -- lookup is meaningless; signature defaults to `...` (variadic-ignored). - -- The `mac_` alias expansion discards the `ab` (atom-builder) arg the same way - -- `MipsAtomComp_Proc_` components do. - local args = nil - if a.kind ~= "atom_proc" then - args = find_function_args_for(source, a.raw_name, a.ident_pos) - end + -- Only `MipsAtomComp_(ac_X)` (kind="comp_bare") and `MipsAtomComp_Proc_(ac_X, ...)` (kind="comp_proc") + -- are COMPONENTS — they get inlined via `mac_` aliases inside atom bodies. + -- `MipsAtom_Proc_` (kind="atom_proc") is an ATOM (ends with `mac_yield()`); it gets emitted via + -- `tb_emit(tb, code_)` (linker symbol), NOT inlined as a macro. Including `atom_proc` here + -- would incorrectly emit `mac_` aliases for atoms, polluting `gen/macs.h`. + -- See `docs/duffle_dsl_primer.md` §"mac_* aliases" for the contract. + if a.kind == "comp_bare" or a.kind == "comp_proc" then + -- Function-args lookup is meaningful for `MipsAtomComp_Proc_` components + -- (the macro sits inside `FI_ Slice_MipsCode ac_X(...)`); the alias expansion + -- discards the `ab` (atom-builder) arg the same way both forms do. + local args = find_function_args_for(source, a.raw_name, a.ident_pos) -- Comment ownership: scan_source.lua stamps `declaration_comment` on the record by walking backward past any associated bare marker. -- The pass reads `declaration_comment` directly. local comment = a.declaration_comment or "" @@ -221,7 +225,7 @@ local function project_components(source, scan) body_tokens = a.body_tokens, args = args, comment = comment, - kind = a.kind, -- "comp_bare" | "comp_proc" | "atom_proc"; provenance emitter reads this. + kind = a.kind, -- "comp_bare" | "comp_proc"; provenance emitter reads this. debug_skip = a.debug_skip == true, } end @@ -400,8 +404,7 @@ local function cycle_cost_rec(name, comp_by_name, latency, cache) end --- (internal) Recursive GP0 prim-buffer contribution. Count `store_word` / `store_half` / `store_byte` ---- calls in the component body that target `R_PrimCursor` (these are the ---- RAM-side prim-buffer words the macro contributes), recursing through nested `mac_*` calls. +--- calls in the component body that target `R_PrimCursor` (these are the RAM-side prim-buffer words the macro contributes), recursing through nested `mac_*` calls. --- Only `R_PrimCursor`-targeting stores count. Stores targeting other registers (e.g. `R_OtBase`, heap pointers) are not prim-buffer contributions. --- @param name string --- @param comp_by_name table @@ -484,10 +487,9 @@ end --- Determine the macro signature: function-args list (function form) or variadic-ignored (bare form). --- For `MipsAtomComp_Proc_` components, the leading `ab` (atom-builder) arg is dropped: ---- the generated `mac_` macros are inline-expansion aliases for baked atoms; their bodies ---- don't reference `ab` (the builder is only consumed by the procedural `atombuilder_unroll` line ---- that `MipsAtomComp_Proc_` appends after the body). Inline callers therefore don't need to thread ---- a builder context. +--- the generated `mac_` macros are inline-expansion aliases for baked atoms; their bodies don't reference `ab` +--- (the builder is only consumed by the procedural `atombuilder_unroll` line that `MipsAtomComp_Proc_` appends after the body). +--- Inline callers therefore don't need to thread a builder context. --- @param args_str string|nil --- @return string local function signature_from_args(args_str) @@ -544,7 +546,7 @@ local function build_component_lines(c, counts) -- Marker comment: emitted once for every skipped component. -- The marker is scanner-owned (declared by `atom_dbg_skip` immediately before the declaration in the source); - -- the components pass projects `c.debug_skip` and emits the marker as a generated comment. + -- This pass projects `c.debug_skip` and emits the marker as a generated comment. if c.debug_skip then lines[#lines + 1] = "/* atom_dbg_skip */" end @@ -578,8 +580,8 @@ end --- Build the boilerplate header lines (the `#ifdef INTELLISENSE_DIRECTIVES` block, --- the `// Auto-generated` comment, the `// Source:` line, and the self-contained `WORD_COUNT` macro definition). ---- @param dir string -- the absolute source directory ---- @param sources SourceFile[] -- sources contributing to this directory (for the header comment) +--- @param dir string -- Absolute source directory +--- @param sources SourceFile[] -- Sources contributing to this directory (for the header comment) --- @return string[] local function header_boilerplate(dir, sources) local source_lines = { "// Directory: " .. duffle.to_absolute_path(dir) .. "/" } @@ -610,9 +612,9 @@ end --- Compute the per-directory output path for `.macs.h`. --- e.g. any source in `code/duffle/` produces `code/duffle/gen/macs.h` regardless of source filename. --- The directory name is the namespace; the filename does not repeat it. ---- @param dir string -- the absolute source directory ---- @return string -- the output directory ---- @return string -- the full output path +--- @param dir string -- Absolute source directory +--- @return string -- Output directory +--- @return string -- Full output path local function compute_macs_h_path(dir) local out_dir = dir .. "/" .. GEN_SUBDIR local out_path = out_dir .. "/" .. MACS_FILENAME @@ -622,11 +624,11 @@ end --- Emit a per-directory `.macs.h` header with the aggregated `mac_X` macros + `WORD_COUNT` entries. --- Writes in BINARY mode so LF line endings are preserved (the git blob is LF; Windows text-mode would emit CRLF and break the byte-identical diff). --- @param ctx PassCtx ---- @param dir string -- the absolute source directory ---- @param sources SourceFile[] -- sources contributing to this directory (for the header comment) ---- @param components Component[] -- aggregated components from all sources in this directory ---- @param counts table -- precomputed word counts (from count_all_components) ---- @return string|nil -- path to the written file (nil if no components) +--- @param dir string -- Absolute source directory +--- @param sources SourceFile[] -- Sources contributing to this directory (for the header comment) +--- @param components Component[] -- Aggregated components from all sources in this directory +--- @param counts table -- Precomputed word counts (from count_all_components) +--- @return string|nil -- Path to the written file (nil if no components) local function emit_component_macros_h(ctx, dir, sources, components, counts) if #components == 0 then return nil end local out_dir, out_path = compute_macs_h_path(dir) @@ -665,11 +667,11 @@ local function update_canonical_word_counts(corpus, components, counts) end --- @class ComponentDef ---- @field name string -- bare name (without ac_/mac_ prefix) ---- @field line integer -- definition source line (line of `MipsAtomComp_(ac_X)` / `MipsAtomComp_Proc_(ac_X, ...)`) ---- @field path string -- absolute source path of the definition ---- @field kind string -- "comp_bare" | "comp_proc" | "atom_proc" ---- @field debug_skip boolean -- mirror of the scanner-owned `a.debug_skip`; consumers read this directly +--- @field name string -- Bare name (without ac_/mac_ prefix) +--- @field line integer -- Definition source line (line of `MipsAtomComp_(ac_X)` / `MipsAtomComp_Proc_(ac_X, ...)`) +--- @field path string -- Absolute source path of the definition +--- @field kind string -- "comp_bare" | "comp_proc" (atom_proc is NOT a component) +--- @field debug_skip boolean -- Mirror of the scanner-owned `a.debug_skip`; consumers read this directly --- (internal) Populate `corpus.components` with this source's components-by-name map. --- First declaration wins; later declarations of the same bare name are dropped and recorded as a collision via `corpus.collisions` (kind = "component"). diff --git a/scripts/passes/dwarf_injection.lua b/scripts/passes/dwarf_injection.lua index f0b706d..b76b4bb 100644 --- a/scripts/passes/dwarf_injection.lua +++ b/scripts/passes/dwarf_injection.lua @@ -703,9 +703,9 @@ end --- `{comp_name, call_file, call_line, comp_file, comp_line, start_pos, end_pos, body_lines, debug_skip}`. `body_lines[k]` --- is the k-th word's source line within the component body. --- ---- @param corpus table -- the corpus from `ctx.shared.corpus` +--- @param corpus table -- From `ctx.shared.corpus` --- @param addrs table -- ELF symbols keyed by atom name from `elf_dwarf.read_nm` ---- @return table[] -- list of {name, addr, size_bytes, words, entries, invocations, debug_skip?} +--- @return table[] -- List of {name, addr, size_bytes, words, entries, invocations, debug_skip?} local function build_atom_table(corpus, addrs) -- Cross-ref: keep only atoms present in BOTH the nm symbol table AND `corpus.atoms_by_name`. Output is sorted by ascending addr. local atoms_by_name = corpus.atoms_by_name or {} @@ -834,10 +834,10 @@ end --- This is intentional: silently falling back to a hardcoded GPR would mask the missing opt-in. --- --- Pre-tokenized: `body_tokens` is the scan-source pass's pre-split list of top-level statements (each entry is a single `load_*` call or other statement). ---- @param body_tokens table[] -- the atom's pre-tokenized body statements (from atom.body_tokens) ---- @param binds_name string -- expected Binds_X name (skip pairs with mismatching binds) ---- @param registries table -- merged registries from collect_per_source_registries ---- @return table[] -- list of {reg = , field = } +--- @param body_tokens table[] -- The atom's pre-tokenized body statements (from atom.body_tokens) +--- @param binds_name string -- Expected Binds_X name (skip pairs with mismatching binds) +--- @param registries table -- Merged registries from collect_per_source_registries +--- @return table[] -- List of {reg = , field = } local function parse_body_load_pairs(body_tokens, binds_name, registries) local pairs = {} local reg_index_by_name = (registries and registries.register_alias_registry) or {} @@ -880,9 +880,9 @@ end --- The piece chain uses (DW_OP_regN, DW_OP_piece, ULEB128(field_size)). --- --- Binds fields come from `scan.binds`; the per-source `scan.binds[i].fields` already carries the typed-field record after the scan-source generalization. ---- @param corpus table -- the corpus from `ctx.shared.corpus` ---- @param atom_table table[] -- the cross-ref'd atom table from build_atom_table ---- @param registries table -- merged registries from collect_per_source_registries +--- @param corpus table -- From `ctx.shared.corpus` +--- @param atom_table table[] -- Cross-ref'd atom table from build_atom_table +--- @param registries table -- Merged registries from collect_per_source_registries --- @return table, table -- (rbind_atoms, rbind_structs) local function parse_rbind_atoms(corpus, atom_table, registries) registries = registries or {} @@ -944,7 +944,7 @@ local function parse_rbind_atoms(corpus, atom_table, registries) binds = ai.binds, fields = struct.fields, -- {name, offset} from scan.binds bytes = struct.bytes, - regs = pairs, -- ordered list of {reg, field} + regs = pairs, -- Ordered list of {reg, field} info_line = ai.info_line, } table.insert(struct.atom_names, atom_name) @@ -1036,13 +1036,13 @@ local function build_dwarf_aranges_section(existing, atom_table) -- We bump the unit's length field accordingly. -- -- Unit structure (DWARF4 §7.21): - -- unit_length (4) - -- version (2) + -- unit_length (4) + -- version (2) -- debug_info_offset (4) -- CU DIE offset in .debug_info - -- address_size (1) - -- segment_size (1) - -- entries... (4-byte addr + 4-byte length) - -- terminator (8 bytes: addr=0, length=0) + -- address_size (1) + -- segment_size (1) + -- entries... (4-byte addr + 4-byte length) + -- terminator (8 bytes: addr=0, length=0) -- Walk all units and emit each one (preserving existing structure). -- For the LAST unit, replace the terminator with my entries + new term. @@ -1780,7 +1780,8 @@ local function build_inserted_children(main_cu_offset, main_cu_end_excl, atom_ta 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 -- type_chain_offsets["void|1"] is what step (f) of the per-RR_ 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. The variable's DW_AT_type must reference the pointer_type, not the base_type. Patch below. + 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. -- Capture the pointer_type's offset (the last-thing-emitted DIE start) and overwrite the lookup. -- The pointer_type was emitted as: uleb(9) (1 byte) + 4-byte ref4 = 5 bytes. Its tag byte is at void_chain_offset + 8 (the base_type's 8 bytes: 1 tag + 5 name + 1 byte_size + 1 encoding). local ptr_void_offset = void_chain_offset + 8 diff --git a/scripts/passes/offsets.lua b/scripts/passes/offsets.lua index ae295d7..1982272 100644 --- a/scripts/passes/offsets.lua +++ b/scripts/passes/offsets.lua @@ -4,9 +4,17 @@ --- for `MipsAtom_(name)` and `MipsCode code_` declarations, computes the word offset --- from each `atom_offset(F, T)` marker to its target `atom_label(T)` declaration, and emits --- `gen/offsets.h` with one `#define _atom_offset_F_T = N` per branch. +--- --- Per-directory aggregation: every source in the same directory contributes to the same `gen/offsets.h`. --- The directory itself is the namespace; the filename does not repeat the module name. --- +--- (Task 12.16 note: atom-namespaced enum names — e.g., `atom_offset__normalize_v3s4__srav_path__aligned_done` — +--- were considered to prevent cross-atom label collisions, but the C-side `atom_offset(F, T)` macro in +--- `code/duffle/dsl.atom.h` doesn't know the current atom_name at expansion time, so any namespacing +--- on the metaprogram side breaks the C build. Reverted. The C-side would need a per-atom +--- `CURRENT_ATOM` #define (set by `MipsAtom_`/`MipsAtom_Proc_` macros) plus an updated `atom_offset` +--- macro that uses it. That's a coordinated refactor — deferred to a future track.) +--- --- The offset is `target_word - branch_word - 1` (the standard MIPS branch-immediate encoding: branch_offset = relative_pc_in_words - 1). -- ════════════════════════════════════════════════════════════════════════════ diff --git a/scripts/passes/static_analysis.lua b/scripts/passes/static_analysis.lua index 6b53934..d084d39 100644 --- a/scripts/passes/static_analysis.lua +++ b/scripts/passes/static_analysis.lua @@ -39,8 +39,8 @@ --- `── Info` section renders finding-level info between `── Warnings` and the per-atom cycle counts. --- --- The structural handshake checks (`mac_yield_uniformity`, `hazard_nop_use`, `control_transfer_delay_slot_use`) skip atoms/components with `debug_skip == true`. ---- The `atom_dbg_skip` marker designates runtime-helper declarations whose structure is fixed by the tape runtime (e.g. `tape_exit`, `ac_yield`). ---- Flagging them as "missing mac_yield" or "BD slot is redundant" is signal noise, not a logic failure. +--- `atom_dbg_skip` marker designates runtime-helper declarations whose structure is fixed by the tape runtime (e.g. `tape_exit`, `ac_yield`). +--- Flagging them as "missing mac_yield" or "BD slot is redundant". --- Other checks (transfer_hazards, gpu_portstore_shape, abi_handoff, enum_alias_membership, …) still apply to debug_skip declarations because real hazards / typos can still surface in them. --- --- The orchestrator (`ps1_meta.lua`) wires this module in via the PASSES table: @@ -452,7 +452,7 @@ local function is_cop2_consumer_of(consumer_event, destination, producer_rel) end -- True iff `consumer_event` reads the GPR operand at any position the destination register occupies. --- The read-position lookup consults `duffle.OPERAND_READ_POSITIONS` for the consumer's encoder and walks each `args[pos]` to find an operand-equal match. +-- read_pos lookup consults `duffle.OPERAND_READ_POSITIONS` for the consumer's encoder and walks each `args[pos]` to find an operand-equal match. local function is_gpr_consumer_of(consumer_event, destination) local consumer_token = consumer_event.encoder or consumer_event.ident local read_pos = duffle.OPERAND_READ_POSITIONS or {}