Add RegUse_ support to the lua metaprogram. Ideated further on type mapping atom comonents to their base component op (math distinctions annotated in the asm).

This commit is contained in:
ed
2026-08-15 15:51:06 -04:00
parent f8b28be02e
commit 8a825a59c7
13 changed files with 551 additions and 317 deletions
+10 -9
View File
@@ -140,16 +140,17 @@ enum { false = 0, true = 1, true_overflow, };
typedef void Proc_(VoidFn) (void); typedef void Proc_(VoidFn) (void);
#define Kilo_(n) (C_(U4, n) << 10) #define Kilo_(n) (C_(U4, n) << 10)
#define Mega_(n) (C_(U4, n) << 20) #define Mega_(n) (C_(U4, n) << 20)
#define Giga_(n) (C_(U4, n) << 30) #define Giga_(n) (C_(U4, n) << 30)
#define Tera_(n) (C_(U4, n) << 40) #define Tera_(n) (C_(U4, n) << 40)
#define null C_(U4, 0) #define null C_(U4, 0)
#define nullptr C_(void*, 0) #define nullptr C_(void*, 0)
#define O_(type, field) C_(U4, & C_(type*,0)->field) #define O_(type, field) C_(U4, & C_(type*,0)->field)
#define OT_(field) O_(typeof_ptr(& field), field)) #define OA_(type, aexpr) C_(U4, & C_(type*,0) aexpr)
#define S_(data) C_(U4, sizeof(data)) #define OT_(field) O_(typeof_ptr(& field), field))
#define S_(data) C_(U4, sizeof(data))
#define sop_1(op,a,b) C_(U1, s1_(a) op s1_(b)) #define sop_1(op,a,b) C_(U1, s1_(a) op s1_(b))
#define sop_2(op,a,b) C_(U2, s2_(a) op s2_(b)) #define sop_2(op,a,b) C_(U2, s2_(a) op s2_(b))
+27 -11
View File
@@ -17,7 +17,7 @@
// source: C:\projects\Pikuma\ps1\code\duffle\bios.h // source: C:\projects\Pikuma\ps1\code\duffle\bios.h
// source: C:\projects\Pikuma\ps1\code\duffle\psyq.h // source: C:\projects\Pikuma\ps1\code\duffle\psyq.h
// source: C:\projects\Pikuma\ps1\code\duffle\pad.c // source: C:\projects\Pikuma\ps1\code\duffle\pad.c
// source: C:\projects\Pikuma\ps1\code\duffle\math.atom.c // source: C:\projects\Pikuma\ps1\code\duffle\math.atom.h
// source: C:\projects\Pikuma\ps1\code\duffle\mips.atom.c // source: C:\projects\Pikuma\ps1\code\duffle\mips.atom.c
// source: C:\projects\Pikuma\ps1\code\duffle\gte.atom.c // source: C:\projects\Pikuma\ps1\code\duffle\gte.atom.c
// source: C:\projects\Pikuma\ps1\code\duffle\gp.atom.c // source: C:\projects\Pikuma\ps1\code\duffle\gp.atom.c
@@ -71,19 +71,35 @@ WORD_COUNT(mac_load_v2s2, 2)
WORD_COUNT(mac_store_v2s2, 2) WORD_COUNT(mac_store_v2s2, 2)
/* atom_dbg_skip */ /* atom_dbg_skip */
#define mac_load_v3s4(rs_x, rs_y, rs_z, r_base, offset) \ #define mac_load_word_v3(tx, ty, tz, base, offset) \
load_word( rs_x, r_base, offset + O_(V3_S4,x)) \ load_word(tx, base, offset + OA_(U4,[0])) \
, load_word( rs_y, r_base, offset + O_(V3_S4,y)) \ , load_word(ty, base, offset + OA_(U4,[1])) \
, load_word( rs_z, r_base, offset + O_(V3_S4,z)) , load_word(tz, base, offset + OA_(U4,[2]))
WORD_COUNT(mac_load_word_v3, 3)
#define mac_load_v3s4(transfer, base, offset) \
mac_load_word_v3(transfer.x, transfer.y, transfer.z, base, offset)
WORD_COUNT(mac_load_v3s4, 3) WORD_COUNT(mac_load_v3s4, 3)
#define mac_load_p3s4(transfer, base, offset) \
mac_load_word_v3(transfer.x, transfer.y, transfer.z, base, offset)
WORD_COUNT(mac_load_p3s4, 3)
/* atom_dbg_skip */ /* atom_dbg_skip */
#define mac_store_v3s4(rt_x, rt_y, rt_z, base, offset) \ #define mac_store_word_v3(tx, ty, tz, base, offset) \
store_word(rt_x, base, offset + O_(V3_S4,x)) \ store_word(tx, base, offset + OA_(U4,[0])) \
, store_word(rt_y, base, offset + O_(V3_S4,y)) \ , store_word(ty, base, offset + OA_(U4,[1])) \
, store_word(rt_z, base, offset + O_(V3_S4,z)) , store_word(tz, base, offset + OA_(U4,[2]))
WORD_COUNT(mac_store_word_v3, 3)
#define mac_store_v3s4(transfer, base, offset) \
mac_store_word_v3(transfer.x, transfer.y, transfer.z, base, offset)
WORD_COUNT(mac_store_v3s4, 3) WORD_COUNT(mac_store_v3s4, 3)
#define mac_store_p3s4(transfer, base, offset) \
mac_store_word_v3(transfer.x, transfer.y, transfer.z, base, offset)
WORD_COUNT(mac_store_p3s4, 3)
/* atom_dbg_skip */ /* atom_dbg_skip */
#define mac_add_si_v3s4(rt_x, rt_y, rt_z, base, offset) \ #define mac_add_si_v3s4(rt_x, rt_y, rt_z, base, offset) \
add_si(rt_x, base, O_(V3_S4,x)) \ add_si(rt_x, base, O_(V3_S4,x)) \
@@ -234,8 +250,8 @@ WORD_COUNT(mac_lzcr_round_even_half_shift, 5)
, gte_mv_to_data_r(to_ir1, C2_IR1) /* IR1 = src.x (preserved in r_tmp — r_mac2_scratch was clobbered to MAC2 in stage 1.5) */ \ , gte_mv_to_data_r(to_ir1, C2_IR1) /* IR1 = src.x (preserved in r_tmp — r_mac2_scratch was clobbered to MAC2 in stage 1.5) */ \
, gte_mv_to_data_r(to_ir2, C2_IR2) \ , gte_mv_to_data_r(to_ir2, C2_IR2) \
, gte_mv_to_data_r(to_ir3, C2_IR3) /* IR3 = src.z (reloaded) */ \ , gte_mv_to_data_r(to_ir3, C2_IR3) /* IR3 = src.z (reloaded) */ \
, LdSlot_ nop_slot1 \ , DmaSlot_ nop_slot1 \
, LdSlot_ nop_slot2 \ , DmaSlot_ nop_slot2 \
, gte_cmdw_gpf \ , gte_cmdw_gpf \
, gte_mv_from_data_r(fr_mac1, C2_MAC1) \ , gte_mv_from_data_r(fr_mac1, C2_MAC1) \
, gte_mv_from_data_r(fr_mac2, C2_MAC2) \ , gte_mv_from_data_r(fr_mac2, C2_MAC2) \
+1 -1
View File
@@ -14,7 +14,7 @@
// source: C:\projects\Pikuma\ps1\code\duffle\bios.h // source: C:\projects\Pikuma\ps1\code\duffle\bios.h
// source: C:\projects\Pikuma\ps1\code\duffle\psyq.h // source: C:\projects\Pikuma\ps1\code\duffle\psyq.h
// source: C:\projects\Pikuma\ps1\code\duffle\pad.c // source: C:\projects\Pikuma\ps1\code\duffle\pad.c
// source: C:\projects\Pikuma\ps1\code\duffle\math.atom.c // source: C:\projects\Pikuma\ps1\code\duffle\math.atom.h
// source: C:\projects\Pikuma\ps1\code\duffle\mips.atom.c // source: C:\projects\Pikuma\ps1\code\duffle\mips.atom.c
// source: C:\projects\Pikuma\ps1\code\duffle\gte.atom.c // source: C:\projects\Pikuma\ps1\code\duffle\gte.atom.c
// source: C:\projects\Pikuma\ps1\code\duffle\gp.atom.c // source: C:\projects\Pikuma\ps1\code\duffle\gp.atom.c
+8 -8
View File
@@ -150,8 +150,8 @@ MipsAtomComp_Proc_(ab, {
gte_mv_to_data_r(to_ir1, C2_IR1), /* IR1 = src.x (preserved in r_tmp — r_mac2_scratch was clobbered to MAC2 in stage 1.5) */ gte_mv_to_data_r(to_ir1, C2_IR1), /* IR1 = src.x (preserved in r_tmp — r_mac2_scratch was clobbered to MAC2 in stage 1.5) */
gte_mv_to_data_r(to_ir2, C2_IR2), gte_mv_to_data_r(to_ir2, C2_IR2),
gte_mv_to_data_r(to_ir3, C2_IR3), /* IR3 = src.z (reloaded) */ gte_mv_to_data_r(to_ir3, C2_IR3), /* IR3 = src.z (reloaded) */
LdSlot_ nop_slot1, DmaSlot_ nop_slot1,
LdSlot_ nop_slot2, DmaSlot_ nop_slot2,
gte_cmdw_gpf, gte_cmdw_gpf,
gte_mv_from_data_r(fr_mac1, C2_MAC1), gte_mv_from_data_r(fr_mac1, C2_MAC1),
gte_mv_from_data_r(fr_mac2, C2_MAC2), gte_mv_from_data_r(fr_mac2, C2_MAC2),
@@ -284,7 +284,7 @@ MipsAtom_Proc_(aa, {
/* Load src.x/y/z from r_src_ptr (caller-determined address) into r_tmp/r_recip_est/r_branch_tmp. /* Load src.x/y/z from r_src_ptr (caller-determined address) into r_tmp/r_recip_est/r_branch_tmp.
* r.rt1_src_x holds src.x throughout stages 1-2 — r_mac2_scratch is clobbered to MAC2 in stage 1.5 (line below). */ * r.rt1_src_x holds src.x throughout stages 1-2 — r_mac2_scratch is clobbered to MAC2 in stage 1.5 (line below). */
mac_load_v3s4(r.src_x, r.recip_est, r.t5.src_z, r.src_ptr, 0), mac_load_word_v3(r.src_x, r.recip_est, r.t5.src_z, r.src_ptr, 0),
/* Stage 1: mtc2 src → IR1/2/3, SQR fires. */ /* Stage 1: mtc2 src → IR1/2/3, SQR fires. */
LdSlot_ mac_gte_sqr_v3s4(r.src_x, r.recip_est, r.t5.src_z, LdSlot_ nop), LdSlot_ mac_gte_sqr_v3s4(r.src_x, r.recip_est, r.t5.src_z, LdSlot_ nop),
@@ -293,8 +293,8 @@ MipsAtom_Proc_(aa, {
mac_gte_mv_from_data_r_mac123(r.t3.mac1_scratch, r.t4.mac2_scratch, r.norm), LdSlot_ nop, mac_gte_mv_from_data_r_mac123(r.t3.mac1_scratch, r.t4.mac2_scratch, r.norm), LdSlot_ nop,
add_u_self( r.norm, r.t3.mac1_scratch), add_u_self( r.norm, r.t3.mac1_scratch),
add_u_self( r.norm, r.t4.mac2_scratch), add_u_self( r.norm, r.t4.mac2_scratch),
gte_mv_to_data_r( r.norm, C2_LZCS), LdSlot_ nop2, gte_mv_to_data_r( r.norm, C2_LZCS), DmaSlot_ nop2,
gte_mv_from_data_r(r.shift, C2_LZCR), LdSlot_ nop, gte_mv_from_data_r(r.shift, C2_LZCR), DmaSlot_ nop,
/* Stage 3: round LZCR to even, compute half-shift, align |v|² to bit 24. /* Stage 3: round LZCR to even, compute half-shift, align |v|² to bit 24.
* r_norm holds |v|² sum; r_shift holds the LZCR count from mfc2. * r_norm holds |v|² sum; r_shift holds the LZCR count from mfc2.
@@ -328,13 +328,13 @@ MipsAtom_Proc_(aa, {
r.recip_est, r.recip_est,
r.t5.src_z, /* IR3 = src.z (reloaded) */ r.t5.src_z, /* IR3 = src.z (reloaded) */
r.t4.mac2_scratch, r.recip_est, r.t5.src_z, r.t4.mac2_scratch, r.recip_est, r.t5.src_z,
LdSlot_ add_si(r.dst_ptr, r.scratch, dst_offset), // pre-laoding destination to register here. DmaSlot_ add_si(r.dst_ptr, r.scratch, dst_offset), // pre-laoding destination to register here.
LdSlot_ nop DmaSlot_ nop
), ),
/* sra by r_shift = (31-LZCR)/2 (saved before sqrtbl lookup) */ /* sra by r_shift = (31-LZCR)/2 (saved before sqrtbl lookup) */
mac_shift_aright_var_v3_self(r.t4.mac2_scratch, r.recip_est, r.t5.src_z, r.shift), mac_shift_aright_var_v3_self(r.t4.mac2_scratch, r.recip_est, r.t5.src_z, r.shift),
/* Store result.x/y/z to r_dst_ptr (caller-determined dst address). */ /* Store result.x/y/z to r_dst_ptr (caller-determined dst address). */
mac_store_v3s4(r.t4.mac2_scratch, r.recip_est, r.t5.src_z, r.dst_ptr, 0), mac_store_word_v3(r.t4.mac2_scratch, r.recip_est, r.t5.src_z, r.dst_ptr, 0),
mac_yield() mac_yield()
}) })
+4 -1
View File
@@ -101,6 +101,7 @@ enum {
}; };
typedef U2 Reg; // Register parameter used with atom or atom component procedures typedef U2 Reg; // Register parameter used with atom or atom component procedures
#define Reg_(type) tmpl(Reg,type) // Just a way to template register allocations of C-struct types.
typedef U4 const MipsCode; // Underlying type to mips asm words. typedef U4 const MipsCode; // Underlying type to mips asm words.
typedef Slice_(MipsCode); typedef Slice_(MipsCode);
@@ -143,6 +144,8 @@ typedef Slice_(MipsAtom);
// Inline-only callers (the generated `mac_<name>` aliases) skip the `ab` arg via metaprogram filtering; escape callers (ac_<name> invoked as a function) pass a long-lived builder. // Inline-only callers (the generated `mac_<name>` aliases) skip the `ab` arg via metaprogram filtering; escape callers (ac_<name> invoked as a function) pass a long-lived builder.
#define MipsAtomComp_Proc_(ab, ...) { MipsCode atom_comp_code[] align_(4) = __VA_ARGS__; atombuilder_push(ab, slice_from_array(MipsCode, atom_comp_code)); } #define MipsAtomComp_Proc_(ab, ...) { MipsCode atom_comp_code[] align_(4) = __VA_ARGS__; atombuilder_push(ab, slice_from_array(MipsCode, atom_comp_code)); }
#define MipsAtomComp_ProcMap_(ab, base_command) atom_dbg_skip MipsAtomComp_Proc_(ab, {base_command })
/* Line-table anchor: gcc only adds a file to the .debug_line file table when the contains line-numbered content. /* Line-table anchor: gcc only adds a file to the .debug_line file table when the contains line-numbered content.
Files containing only atoms and atom components. Files containing only atoms and atom components.
Place `ATOM_FILE_LINE_MARKER();` once at file scope in any `.atom.c` that defines atoms. Place `ATOM_FILE_LINE_MARKER();` once at file scope in any `.atom.c` that defines atoms.
@@ -372,7 +375,7 @@ FI_ void regfile_reset(RegFile_R rf) {
rf->GPR[0] = u4_lo(regfile_abi_mask); rf->GPR[0] = u4_lo(regfile_abi_mask);
rf->GPR[1] = u4_hi(regfile_abi_mask); rf->GPR[1] = u4_hi(regfile_abi_mask);
} }
FI_ void regfile_reset_mask(RegFile_R rf, U4 mask) { FI_ void regfile_reset_to_mask(RegFile_R rf, U4 mask) {
rf->GPR[0] = u4_lo(mask); rf->GPR[0] = u4_lo(mask);
rf->GPR[1] = u4_hi(mask); rf->GPR[1] = u4_hi(mask);
} }
@@ -7,9 +7,10 @@
ATOM_FILE_DEBUGGER_LINE_MARKER(math_atom_c); ATOM_FILE_DEBUGGER_LINE_MARKER(math_atom_c);
#pragma region MACs (Mips Atom Component) typedef Struct_(Reg_V3_S4) { Reg x, y, z; }; // Register allocation of a V3_S4
typedef Struct_(Reg_P3_S4) { Reg x, y, z; }; // Register allocation of a P3_S4
// FI_ Slice_MipsCode ac_load_imm #pragma region MACs (Mips Atom Component)
FI_ Slice_MipsCode ac_load_v2s2(AtomBuilder_R ab, U4 rs_x, U4 rs_y, U4 r_base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ab, { FI_ Slice_MipsCode ac_load_v2s2(AtomBuilder_R ab, U4 rs_x, U4 rs_y, U4 r_base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ab, {
load_half( rs_x, r_base, offset + O_(V3_S2,x)), load_half( rs_x, r_base, offset + O_(V3_S2,x)),
@@ -21,23 +22,23 @@ FI_ Slice_MipsCode ac_store_v2s2(AtomBuilder_R ab, U4 rt_x, U4 rt_y, U4 base, U4
store_half(rt_y, base, offset + O_(V2_S2,y)), store_half(rt_y, base, offset + O_(V2_S2,y)),
}) })
FI_ Slice_MipsCode ac_load_v3s4(AtomBuilder_R ab, U4 rs_x, U4 rs_y, U4 rs_z, U4 r_base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ab, { FI_ Slice_MipsCode ac_load_word_v3(AtomBuilder_R ab, Reg tx, Reg ty, Reg tz, Reg base, U2 offset) atom_dbg_skip MipsAtomComp_Proc_(ab, {
load_word( rs_x, r_base, offset + O_(V3_S4,x)), load_word(tx, base, offset + OA_(U4,[0])),
load_word( rs_y, r_base, offset + O_(V3_S4,y)), load_word(ty, base, offset + OA_(U4,[1])),
load_word( rs_z, r_base, offset + O_(V3_S4,z)), load_word(tz, base, offset + OA_(U4,[2])),
}) })
// TODO(Ed): we could generate these mappings properly..
#define ac_load_p3s4 ac_load_v3s4
#define mac_load_p3s4 mac_load_v3s4
FI_ Slice_MipsCode ac_store_v3s4(AtomBuilder_R ab, U4 rt_x, U4 rt_y, U4 rt_z, U4 base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ab, { FI_ Slice_MipsCode ac_load_v3s4(AtomBuilder_R ab, Reg_(V3_S4) transfer, Reg base, U2 offset) MipsAtomComp_ProcMap_(ab, mac_load_word_v3(transfer.x, transfer.y, transfer.z, base, offset))
store_word(rt_x, base, offset + O_(V3_S4,x)), FI_ Slice_MipsCode ac_load_p3s4(AtomBuilder_R ab, Reg_(P3_S4) transfer, Reg base, U2 offset) MipsAtomComp_ProcMap_(ab, mac_load_word_v3(transfer.x, transfer.y, transfer.z, base, offset))
store_word(rt_y, base, offset + O_(V3_S4,y)),
store_word(rt_z, base, offset + O_(V3_S4,z)), FI_ Slice_MipsCode ac_store_word_v3(AtomBuilder_R ab, U4 tx, U4 ty, U4 tz, U4 base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ab, {
store_word(tx, base, offset + OA_(U4,[0])),
store_word(ty, base, offset + OA_(U4,[1])),
store_word(tz, base, offset + OA_(U4,[2])),
}) })
// TODO(Ed): we could generate these mappings properly..
#define ac_store_p3s4 ac_store_v3s4 FI_ Slice_MipsCode ac_store_v3s4(AtomBuilder_R ab, Reg_(V3_S4) transfer, Reg base, U2 offset) MipsAtomComp_ProcMap_(ab, mac_store_word_v3(transfer.x, transfer.y, transfer.z, base, offset))
#define mac_store_p3s4 mac_store_v3s4 FI_ Slice_MipsCode ac_store_p3s4(AtomBuilder_R ab, Reg_(P3_S4) transfer, Reg base, U2 offset) MipsAtomComp_ProcMap_(ab, mac_store_word_v3(transfer.x, transfer.y, transfer.z, base, offset))
FI_ Slice_MipsCode ac_add_si_v3s4(AtomBuilder_R ab, Reg rt_x, Reg rt_y, Reg rt_z, Reg base, U2 offset) FI_ Slice_MipsCode ac_add_si_v3s4(AtomBuilder_R ab, Reg rt_x, Reg rt_y, Reg rt_z, Reg base, U2 offset)
atom_dbg_skip MipsAtomComp_Proc_(ab, { atom_dbg_skip MipsAtomComp_Proc_(ab, {
+13 -15
View File
@@ -10,7 +10,7 @@
# include "duffle/pad.h" # include "duffle/pad.h"
# include "duffle/word_count.metadata.h" # include "duffle/word_count.metadata.h"
# include "duffle/psyq.h" # include "duffle/psyq.h"
# include "duffle/math.atom.c" # include "duffle/math.atom.h"
# include "duffle/mips.atom.c" # include "duffle/mips.atom.c"
# include "duffle/gte.atom.c" # include "duffle/gte.atom.c"
# include "duffle/gp.atom.c" # include "duffle/gp.atom.c"
@@ -129,7 +129,7 @@ typedef Struct_(Binds_ResolveLookAtSub) {
}; };
typedef Struct_(RegUse_resolve_look_at__input_and_sub_proc) { typedef Struct_(RegUse_resolve_look_at__input_and_sub_proc) {
Reg const scratch; Reg scratch;
Reg target; Reg eye; Reg up_in; Reg target; Reg eye; Reg up_in;
Reg t0; Reg t1; Reg t2; Reg t3; Reg t4; Reg t0; Reg t1; Reg t2; Reg t3; Reg t4;
}; };
@@ -152,7 +152,7 @@ typedef Struct_(RegUse_resolve_look_at__input_and_sub_proc) {
* Pool cost: 8 GPRs + R_T4 (carrier) + R_AT + R_V0 (hardcoded) = 11 GPRs. * Pool cost: 8 GPRs + R_T4 (carrier) + R_AT + R_V0 (hardcoded) = 11 GPRs.
*/ */
internal MipsAtom* resolve_look_at__input_and_sub_proc(AtomArena_R aa, RegUse_resolve_look_at__input_and_sub_proc r) internal MipsAtom* resolve_look_at__input_and_sub_proc(AtomArena_R aa, RegUse_resolve_look_at__input_and_sub_proc r)
MipsAtom_Proc_(aa, atom_info(atom_bind(Binds_ResolveLookAtSub)){ atom_info(atom_bind(Binds_ResolveLookAtSub)) MipsAtom_Proc_(aa, {
load_word(r.target, R_TapePtr, O_(Binds_ResolveLookAtSub,target)), load_word(r.target, R_TapePtr, O_(Binds_ResolveLookAtSub,target)),
load_word(r.eye, R_TapePtr, O_(Binds_ResolveLookAtSub,eye)), load_word(r.eye, R_TapePtr, O_(Binds_ResolveLookAtSub,eye)),
load_word(r.up_in, R_TapePtr, O_(Binds_ResolveLookAtSub,up_in)), load_word(r.up_in, R_TapePtr, O_(Binds_ResolveLookAtSub,up_in)),
@@ -160,20 +160,20 @@ MipsAtom_Proc_(aa, atom_info(atom_bind(Binds_ResolveLookAtSub)){
add_ui_self( R_TapePtr, S_(Binds_ResolveLookAtSub)), add_ui_self( R_TapePtr, S_(Binds_ResolveLookAtSub)),
/* Stage up_in.x/y/z into the scratchpad. */ /* Stage up_in.x/y/z into the scratchpad. */
mac_load_p3s4( r.t0, r.t1, r.t2, r.up_in, 0), mac_load_word_v3( r.t0, r.t1, r.t2, r.up_in, 0),
mac_store_p3s4(r.t0, r.t1, r.t2, r.scratch, O_(ResolveLookAtScratch,up_in)), mac_store_word_v3(r.t0, r.t1, r.t2, r.scratch, O_(ResolveLookAtScratch,up_in)),
// Stage eye.x/y/z into the scratchpad (atom 6 reads these for the translation column). // Stage eye.x/y/z into the scratchpad (atom 6 reads these for the translation column).
mac_load_p3s4( r.t0, r.t1, r.t2, r.eye, 0), mac_load_word_v3( r.t0, r.t1, r.t2, r.eye, 0),
mac_store_p3s4(r.t0, r.t1, r.t2, r.scratch, O_(ResolveLookAtScratch,eye)), mac_store_word_v3(r.t0, r.t1, r.t2, r.scratch, O_(ResolveLookAtScratch,eye)),
/* Compute fwd = target - eye. */ /* Compute fwd = target - eye. */
// mac_load_p3s4(t3, R_AT, t4, r.eye, 0), // mac_load_p3s4(t3, R_AT, t4, r.eye, 0),
mac_load_p3s4(r.t3, R_AT, r.t4, r.target, 0), mac_load_word_v3(r.t3, R_AT, r.t4, r.target, 0),
mac_sub_v3s4( mac_sub_v3s4(
r.t3, R_AT, r.t4, r.t3, R_AT, r.t4,
r.t0, r.t1, r.t2), r.t0, r.t1, r.t2),
mac_store_v3s4(r.t3, R_AT, r.t4, r.scratch, O_(ResolveLookAtScratch,fwd)), mac_store_word_v3(r.t3, R_AT, r.t4, r.scratch, O_(ResolveLookAtScratch,fwd)),
mac_yield() mac_yield()
}) })
@@ -199,9 +199,9 @@ internal MipsAtom* resolve_look_at__cross_uz_up_into_right_proc(AtomArena_R aa,
nop, nop,
/* Load a (uz).x/y/z into r_a/r_b/r_c. */ /* Load a (uz).x/y/z into r_a/r_b/r_c. */
mac_load_v3s4(r.a, r.b, r.c, r.g, 0), mac_load_word_v3(r.a, r.b, r.c, r.g, 0),
/* Load b (up_in).x/y/z into r_d + R_AT/R_V0 (R_AT/R_V0 are hardcoded scratch). */ /* Load b (up_in).x/y/z into r_d + R_AT/R_V0 (R_AT/R_V0 are hardcoded scratch). */
mac_load_v3s4(r.d, R_AT, r.t0, r.h, 0), LdSlot_ mac_load_word_v3(r.d, R_AT, r.t0, r.h, 0), LdSlot_ // (taken by gte_mv_from_ctrl_r)
/* Save the two RT control-register slots OP will clobber. We reuse r_g/r_h (scratch pointers, no longer needed) as the save targets. */ /* Save the two RT control-register slots OP will clobber. We reuse r_g/r_h (scratch pointers, no longer needed) as the save targets. */
gte_mv_from_ctrl_r(r.target0, gte_cr_RT11), /* r_g = C2 r0 (RT11|RT12) */ gte_mv_from_ctrl_r(r.target0, gte_cr_RT11), /* r_g = C2 r0 (RT11|RT12) */
@@ -228,8 +228,7 @@ internal MipsAtom* resolve_look_at__cross_uz_up_into_right_proc(AtomArena_R aa,
gte_mv_to_data_r(r.t0, C2_IR3), /* IR3 = up_in.z */ gte_mv_to_data_r(r.t0, C2_IR3), /* IR3 = up_in.z */
DmaSlot_ nop2, /* MTC2 retirement (CPU→COP2 2-slot delay) */ DmaSlot_ nop2, /* MTC2 retirement (CPU→COP2 2-slot delay) */
// gte_cmdw_cross, /* OP: MAC1/2/3 = uz × up_in gte_cmdw_cross, /* OP: MAC1/2/3 = uz × up_in
gte_cmdw_outer_product, /* OP: MAC1/2/3 = uz × up_in
* MAC1 = IR3*D2 - IR2*D3 = up_in.z*uz.y.high - up_in.y*uz.z.high * MAC1 = IR3*D2 - IR2*D3 = up_in.z*uz.y.high - up_in.y*uz.z.high
* MAC2 = IR1*D3 - IR3*D1 = up_in.x*uz.z.high - up_in.z*uz.x * MAC2 = IR1*D3 - IR3*D1 = up_in.x*uz.z.high - up_in.z*uz.x
* MAC3 = IR2*D1 - IR1*D2 = up_in.y*uz.x - up_in.x*uz.y.high * MAC3 = IR2*D1 - IR1*D2 = up_in.y*uz.x - up_in.x*uz.y.high
@@ -249,9 +248,8 @@ internal MipsAtom* resolve_look_at__cross_uz_up_into_right_proc(AtomArena_R aa,
/* Right-shift MAC by 12 to convert from GTE's S12.20 fixed-point scale back to libpsyx OuterProduct12 convention (S12.0, fp_one=4096=1<<12). /* Right-shift MAC by 12 to convert from GTE's S12.20 fixed-point scale back to libpsyx OuterProduct12 convention (S12.0, fp_one=4096=1<<12).
* Without this, MAC values (~16M for unit-vector cross products) overflow the GTE's 16-bit IR registers when atom 3 normalizes via mtc2. */ * Without this, MAC values (~16M for unit-vector cross products) overflow the GTE's 16-bit IR registers when atom 3 normalizes via mtc2. */
mac_shift_aright_v3_self(r.a, r.b, r.c, 12), mac_shift_aright_v3_self(r.a, r.b, r.c, 12),
/* Store out.x/y/z to r_f (out ptr = scratch+32). */ /* Store out.x/y/z to r_f (out ptr = scratch+32). */
mac_store_v3s4(r.a, r.b, r.c, r.f, 0), mac_store_word_v3(r.a, r.b, r.c, r.f, 0),
mac_yield() mac_yield()
}) })
+30 -24
View File
@@ -32,7 +32,7 @@
#pragma region Duffle TUs #pragma region Duffle TUs
#include "duffle/pad.c" #include "duffle/pad.c"
#include "duffle/math.atom.c" #include "duffle/math.atom.h"
#include "duffle/mips.atom.c" #include "duffle/mips.atom.c"
#include "duffle/gte.atom.c" #include "duffle/gte.atom.c"
#include "duffle/gp.atom.c" #include "duffle/gp.atom.c"
@@ -149,20 +149,22 @@ internal void resolve_look_at_init(void) {
U4 pin_mask = regfile_abi_mask | (1 << R_ResolveScratch); U4 pin_mask = regfile_abi_mask | (1 << R_ResolveScratch);
RegFile rf = regfile(pin_mask); RegFile rf = regfile(pin_mask);
#define ralloc() regfile_alloc(& rf)
smem.resolve_look_at_atom_addrs[0] = resolve_look_at__input_and_sub_proc(& ab, smem.resolve_look_at_atom_addrs[0] = resolve_look_at__input_and_sub_proc(& ab,
RegUse_(resolve_look_at__input_and_sub_proc) { RegUse_(resolve_look_at__input_and_sub_proc) {
.scratch = R_ResolveScratch, .scratch = R_ResolveScratch,
.target = regfile_alloc(& rf), .target = ralloc(),
.eye = regfile_alloc(& rf), .eye = ralloc(),
.up_in = regfile_alloc(& rf), .up_in = ralloc(),
.t0 = regfile_alloc(& rf), .t0 = ralloc(),
.t1 = regfile_alloc(& rf), .t1 = ralloc(),
.t2 = regfile_alloc(& rf), .t2 = ralloc(),
.t3 = regfile_alloc(& rf), .t3 = ralloc(),
.t4 = regfile_alloc(& rf), .t4 = ralloc(),
} }
); );
regfile_reset_to_mask(& rf, pin_mask);
/* === ATOM 1: normalize fwd→uz === */ /* === ATOM 1: normalize fwd→uz === */
U2 src_offset = O_(ResolveLookAtScratch, fwd); U2 src_offset = O_(ResolveLookAtScratch, fwd);
@@ -170,27 +172,30 @@ internal void resolve_look_at_init(void) {
smem.resolve_look_at_atom_addrs[1] = normalize_v3s4_proc(& ab, smem.resolve_look_at_atom_addrs[1] = normalize_v3s4_proc(& ab,
src_offset, dst_offset, RegUse_(normalize_v3s4_proc){ src_offset, dst_offset, RegUse_(normalize_v3s4_proc){
.scratch = R_ResolveScratch, .scratch = R_ResolveScratch,
.src_ptr = R_T0, .src_ptr = ralloc(),
.dst_ptr = R_T1, .dst_ptr = ralloc(),
.recip_est = R_T6, .recip_est = ralloc(),
.norm = R_T7, .norm = ralloc(),
.shift = R_V0, .shift = ralloc(),
.src_x = R_T2, .src_x = ralloc(),
.t3 = R_T3, .t3 = ralloc(),
.t4 = R_T5, .t4 = ralloc(),
.t5 = R_V1, .t5 = ralloc(),
}); });
regfile_reset_to_mask(& rf, pin_mask);
/* === ATOM 2: cross uz×up_in→right === */ /* === ATOM 2: cross uz×up_in→right === */
smem.resolve_look_at_atom_addrs[2] = resolve_look_at__cross_uz_up_into_right_proc(& ab, smem.resolve_look_at_atom_addrs[2] = resolve_look_at__cross_uz_up_into_right_proc(& ab,
RegUse_(resolve_look_at__cross_uz_up_into_right_proc) { RegUse_(resolve_look_at__cross_uz_up_into_right_proc) {
.scratch = R_ResolveScratch, .scratch = R_ResolveScratch,
.a = R_T0, .b = R_T1, .c = R_T2, .a = ralloc(),
.d = R_T3, .b = ralloc(),
.f = R_T5, .c = ralloc(),
.t1 = R_T6, .d = ralloc(),
.t2 = R_T7, .f = ralloc(),
.t0 = R_V0, .t1 = ralloc(),
.t2 = ralloc(),
.t0 = ralloc(),
}); });
/* === ATOM 3: normalize right→ux === */ /* === ATOM 3: normalize right→ux === */
@@ -285,6 +290,7 @@ internal void resolve_look_at_init(void) {
/* Sanity check: arena didn't overflow. */ /* Sanity check: arena didn't overflow. */
assert(ab.used <= ResolveLookAtArena_Size); assert(ab.used <= ResolveLookAtArena_Size);
#undef ralloc
} }
/* Emit the resolve_look_at bundle into the tape. Called once per frame from update(). /* Emit the resolve_look_at bundle into the tape. Called once per frame from update().
+9 -2
View File
@@ -2248,8 +2248,15 @@ local function _project_emission_inner(root_body_entry, ctx_table)
end end
local function apply_sub(sub_map, operand) local function apply_sub(sub_map, operand)
if sub_map and type(operand) == "string" and sub_map[operand] then if not (sub_map and type(operand) == "string") then return operand end
return sub_map[operand] if sub_map[operand] then return sub_map[operand] end
local dot = operand:find(".", 1, true)
if dot then
local head = operand:sub(1, dot - 1)
local mapped = sub_map[head]
if type(mapped) == "string" then
return mapped .. operand:sub(dot)
end
end end
return operand return operand
end end
+13 -1
View File
@@ -83,6 +83,7 @@ local function canonical_word_entries(atom)
line = event.call_line or item.line or 0, line = event.call_line or item.line or 0,
text = event.call_text or item.call_text or "", text = event.call_text or item.call_text or "",
body_line = event.body_line or item.body_line or item.line or 0, body_line = event.body_line or item.body_line or item.line or 0,
gpr_keys = event.gpr_keys,
invocation = (event.outermost_invocation_id invocation = (event.outermost_invocation_id
and paths.invocations and paths.invocations
and paths.invocations[event.outermost_invocation_id]) or nil, and paths.invocations[event.outermost_invocation_id]) or nil,
@@ -491,8 +492,19 @@ function M.render_atom_source_map(atom)
local lines = {} local lines = {}
lines[#lines + 1] = string.format("ATOM %s %d", (atom.raw_name or atom.name), total) lines[#lines + 1] = string.format("ATOM %s %d", (atom.raw_name or atom.name), total)
for _, entry in ipairs(entries) do for _, entry in ipairs(entries) do
lines[#lines + 1] = string.format("WORD %d LINE %d TEXT %s", local word_line = string.format("WORD %d LINE %d TEXT %s",
entry.pos, entry.line, entry.text) entry.pos, entry.line, entry.text)
local keys = {}
for pos = 1, 16 do
local k = entry.gpr_keys and entry.gpr_keys[pos]
if type(k) == "string" and k:sub(1, 7) == "reguse:" then
keys[#keys + 1] = k
end
end
if #keys > 0 then
word_line = word_line .. " KEYS " .. table.concat(keys, ",")
end
lines[#lines + 1] = word_line
end end
lines[#lines + 1] = "ENDATOM" lines[#lines + 1] = "ENDATOM"
return table.concat(lines, "\n") .. "\n" return table.concat(lines, "\n") .. "\n"
+382 -226
View File
@@ -4,8 +4,8 @@
--- - `build/gen/<dir_basename>.annotations.txt` — one per source-directory containing atoms; aggregates across all sources in the directory. --- - `build/gen/<dir_basename>.annotations.txt` — one per source-directory containing atoms; aggregates across all sources in the directory.
--- - `build/gen/annotation_validation.txt` — the project summary. --- - `build/gen/annotation_validation.txt` — the project summary.
--- ---
--- The annotation pass emits `errors.h` files per module and the canonical `corpus.sources_by_dir` projection groups sources by directory. --- The canonical `corpus.sources_by_dir` projection groups sources by directory.
--- This pass iterates the dir projection directly and re-validates each source via `annotation.validate()` to get the detailed per-source results. --- This pass builds one ModuleView per directory and walks SECTION_RENDERERS.
-- ════════════════════════════════════════════════════════════════════════════ -- ════════════════════════════════════════════════════════════════════════════
-- Module-scope requires + package.path setup -- Module-scope requires + package.path setup
@@ -20,11 +20,6 @@
local _bootstrap_dir = debug.getinfo(1, "S").source:match("^@?(.*[/\\])") or "./" local _bootstrap_dir = debug.getinfo(1, "S").source:match("^@?(.*[/\\])") or "./"
local duffle = dofile(_bootstrap_dir .. "../duffle_paths.lua") local duffle = dofile(_bootstrap_dir .. "../duffle_paths.lua")
-- Load the annotation pass so we can re-validate each source against the canonical corpus projection.
-- The annotation pass exposes `M.validate`, which returns the per-source AnnotationResult (atoms / annots / macros / binds / errors / warnings)
-- that the report pass renders into the per-module `<dir_basename>.annotations.txt` output.
local annotation = dofile(_bootstrap_dir .. "annotation.lua")
-- Load atoms_source_map for the `render_source_map` / `render_provenance` module functions (used by `render_module_atoms_md` to produce `<module>.atoms.md` without re-walking source tokens). -- Load atoms_source_map for the `render_source_map` / `render_provenance` module functions (used by `render_module_atoms_md` to produce `<module>.atoms.md` without re-walking source tokens).
-- The pass itself emits no per-source files anymore; we only consume the two pure renderers here. -- The pass itself emits no per-source files anymore; we only consume the two pure renderers here.
-- Defined BEFORE the renderer functions below so their upvalues resolve to this local (not the global `atoms_source_map`, which is nil). -- Defined BEFORE the renderer functions below so their upvalues resolve to this local (not the global `atoms_source_map`, which is nil).
@@ -225,7 +220,7 @@ local function render_module_atoms_md(dir, dir_sources, wc)
for _, atom in ipairs(atoms_list) do for _, atom in ipairs(atoms_list) do
lines[#lines + 1] = string.format( lines[#lines + 1] = string.format(
"### atom: %s (line %d, %d words)", "### atom: %s (line %d, %d words)",
atom.name, atom.line or 0, #(atom.paths.items or {})) atom.name, atom.line or 0, #((atom.paths or {}).word_events or {}))
lines[#lines + 1] = "" lines[#lines + 1] = ""
lines[#lines + 1] = "**Sourcemap** — per-word call site:" lines[#lines + 1] = "**Sourcemap** — per-word call site:"
lines[#lines + 1] = "```" lines[#lines + 1] = "```"
@@ -246,17 +241,349 @@ local function render_module_atoms_md(dir, dir_sources, wc)
return table.concat(lines, "\n") .. "\n" return table.concat(lines, "\n") .. "\n"
end end
--- Render the consolidated per-module markdown (`build/<module>.atom_meta_report.md`). local function decl_words(atom)
--- Aggregates annotation + static-analysis content across all sources in `dir`. local p = atom.paths or {}
--- Annotations come from re-running `annotation.validate()` per source (the existing pattern); return #(p.word_events or {})
--- static-analysis comes from `corpus.static_analysis_results[dir_basename]` (populated by `static_analysis.lua` — no second corpus_pipe_ctx build). end
--- @param dir string
--- @param dir_sources SourceFile[] local function count_kinds(decls)
--- @param annot_results AnnotationResult[] local n = { atom = 0, atom_proc = 0, comp_bare = 0, comp_proc = 0 }
--- @param sa_results table -- corpus.static_analysis_results[dir_basename] for _, a in ipairs(decls or {}) do
--- @return string if n[a.kind] ~= nil then n[a.kind] = n[a.kind] + 1 end
local function render_module_meta_report(dir, dir_sources, annot_results, sa_results) end
return n
end
local function slot_suffix(key)
if type(key) ~= "string" or key:sub(1, 7) ~= "reguse:" then return nil end
return key:match("([^:]+)$")
end
local function build_module_view(dir, dir_sources, corpus)
local decls = {}
for _, src in ipairs(dir_sources or {}) do
for _, a in ipairs((src.scan and src.scan.atoms) or {}) do
if not a.source_path then a.source_path = src.path end
decls[#decls + 1] = a
end
end
local dir_basename = source_basename(dir) local dir_basename = source_basename(dir)
local sa = (corpus.static_analysis_results or {})[dir_basename] or {}
local schemas = {}
for name, schema in pairs(corpus.reg_use_schemas or {}) do
for _, a in ipairs(decls) do
if a.reg_use_schema_name == name then
schemas[#schemas + 1] = schema
break
end
end
end
return {
dir = dir,
sources = dir_sources or {},
decls = decls,
schemas = schemas,
findings = sa.findings or {},
sa = sa,
corpus = corpus,
}
end
local function render_section_declarations(add, view)
if #view.decls == 0 then add("_(none)_"); add(""); return end
add("| kind | name | source | line | words | min | max | branches | paths |")
add("|------|------|--------|------|-------|-----|-----|----------|-------|")
for _, a in ipairs(view.decls) do
local p = a.paths or {}
add(string.format("| %s | %s | %s | %d | %d | %s | %s | %s | %s |",
a.kind or "?",
a.name or "?",
source_basename(a.source_path or ""),
a.line or 0,
decl_words(a),
tostring(p.cycles_min or ""),
tostring(p.cycles_max or ""),
tostring(p.branches or ""),
tostring(p.paths or "")))
end
add("")
end
local function render_section_components(add, view)
local rows = {}
local index = (view.corpus and view.corpus.component_body_index) or {}
for _, a in ipairs(view.decls) do
if a.kind == "comp_bare" or a.kind == "comp_proc" then
local idx = index[a.name] or {}
local args = idx.arg_names or {}
rows[#rows + 1] = {
name = a.name,
kind = a.kind,
args = table.concat(args, ", "),
words = decl_words(a),
map = a.map_command or "",
}
end
end
if #rows == 0 then add("_(none)_"); add(""); return end
add("| name | kind | arg_names | words | map |")
add("|------|------|-----------|-------|-----|")
for _, r in ipairs(rows) do
add(string.format("| %s | %s | %s | %d | %s |",
r.name, r.kind, r.args ~= "" and r.args or "", r.words, r.map))
end
add("")
end
local function render_section_reguse(add, view)
local wrote = false
for _, schema in ipairs(view.schemas or {}) do
wrote = true
add(string.format("### %s", schema.name or "?"))
for _, slot in ipairs(schema.slots or {}) do
local aliases = table.concat(slot.aliases or { slot.name }, ", ")
local ro = slot.readonly and " readonly" or ""
add(string.format("- slot `%s` aliases %s%s", slot.name, aliases, ro))
end
for _, a in ipairs(view.decls) do
if a.reg_use_schema_name == schema.name then
add(string.format("- bound `%s` param `%s`", a.name, a.reg_use_param_name or "?"))
end
end
add("")
end
local errors = (view.corpus and view.corpus.reg_use_errors) or {}
if #errors > 0 then
wrote = true
add("### parse errors")
for _, err in ipairs(errors) do
add(string.format("- `%s` %s", err.kind or "?", err.schema_name or ""))
end
add("")
end
if not wrote then add("_(none)_"); add("") end
end
local function render_section_annotations(add, view)
local rows = {}
for _, src in ipairs(view.sources) do
for _, info in ipairs((src.scan and src.scan.atom_infos) or {}) do
rows[#rows + 1] = {
source = source_basename(src.path),
line = info.info_line or 0,
name = info.atom_name or "?",
binds = info.binds or "",
reads = (#(info.reads or {}) > 0 and table.concat(info.reads, ",")) or "",
writes = (#(info.writes or {}) > 0 and table.concat(info.writes, ",")) or "",
phase = info.phase or "",
}
end
end
if #rows == 0 then add("_(none)_"); add(""); return end
add("| source | line | name | binds | reads | writes | phase |")
add("|--------|------|------|-------|-------|--------|-------|")
for _, r in ipairs(rows) do
add(string.format("| %s | %d | %s | %s | %s | %s | %s |",
r.source, r.line, r.name, r.binds, r.reads, r.writes, r.phase))
end
add("")
end
local function render_section_binds(add, view)
local wrote = false
for _, src in ipairs(view.sources) do
for _, b in ipairs((src.scan and src.scan.binds) or {}) do
wrote = true
local line = b.line or 0
if src.scan.line_of and type(b.line) == "number" then
line = src.scan.line_of(b.line) or b.line
end
add(string.format("### %s (%s:%s, %s bytes)",
b.name, source_basename(src.path), tostring(line), tostring(b.bytes or "")))
for _, f in ipairs(b.fields or {}) do
add(string.format("- `+%s %s`", tostring(f.offset or "?"), f.name or "?"))
end
add("")
end
end
if not wrote then add("_(none)_"); add("") end
end
local function render_section_phases(add, view)
local corpus = view.corpus or {}
local wrote = false
for phase, entry in pairs(corpus.atom_phases or {}) do
wrote = true
add(string.format("- phase `%s`: %s", phase, table.concat(entry.atoms or {}, ", ")))
end
for name, entry in pairs(corpus.atom_views or {}) do
wrote = true
add(string.format("- view `%s` binds `%s`", name, entry.binds_name or ""))
end
for name, entry in pairs(corpus.atom_ctxs or {}) do
wrote = true
add(string.format("- ctx `%s` rbind `%s`", name, entry.rbind_atom or ""))
end
if not wrote then add("_(none)_") end
add("")
end
local function render_section_aliases(add, view)
local reg = (view.corpus and view.corpus.register_alias_registry) or {}
local names = {}
for name in pairs(reg) do names[#names + 1] = name end
table.sort(names)
if #names == 0 then add("_(none)_"); add(""); return end
add("| alias | type |")
add("|-------|------|")
for _, name in ipairs(names) do
local e = reg[name]
add(string.format("| %s | %s |", name, (e and e.default_type) or ""))
end
add("")
end
local function render_section_autoreg(add, view)
local corpus = view.corpus or {}
local wrote = false
local function dump(label, table_map)
local scopes = {}
for scope in pairs(table_map or {}) do scopes[#scopes + 1] = scope end
table.sort(scopes)
for _, scope in ipairs(scopes) do
wrote = true
local syms = {}
for sym, gpr in pairs(table_map[scope] or {}) do
if type(gpr) == "string" and gpr ~= sym then
syms[#syms + 1] = string.format("%s → %s", sym, gpr)
else
syms[#syms + 1] = tostring(sym)
end
end
table.sort(syms)
add(string.format("- %s `%s`: %s", label, scope, table.concat(syms, ", ")))
end
end
dump("atom", corpus.atom_auto_regs)
dump("phase", corpus.phase_auto_regs)
if not wrote then add("_(none)_") end
add("")
end
local function render_section_collisions(add, view)
local cols = (view.corpus and view.corpus.collisions) or {}
if #cols == 0 then add("_(none)_"); add(""); return end
for _, c in ipairs(cols) do
local first = c.first_site or {}
local other = c.conflicting_site or {}
add(string.format("- `%s` `%s` first %s:%s conflict %s:%s",
c.kind or "?", c.name or "?",
tostring(first.path or "?"), tostring(first.line or "?"),
tostring(other.path or "?"), tostring(other.line or "?")))
end
add("")
end
local function render_section_findings(add, view)
local by_atom = {}
for _, f in ipairs(view.findings or {}) do
local key = f.atom or "?"
by_atom[key] = by_atom[key] or {}
by_atom[key][#by_atom[key] + 1] = f
end
if next(by_atom) == nil then add("_(none)_"); add(""); return end
local seen = {}
local function emit(name, fs)
add("### " .. name)
for _, f in ipairs(fs) do
local msg = f.msg or ""
local slot = slot_suffix(f.gpr_key or f.producer_destination)
if slot and not msg:find("(slot ", 1, true) then
msg = msg .. " (slot " .. slot .. ")"
end
add(string.format("- `[%s/%s] %s`", f.kind or "info", f.check or "?", msg))
end
add("")
end
for _, a in ipairs(view.decls) do
if by_atom[a.name] then
seen[a.name] = true
emit(a.name, by_atom[a.name])
end
end
local leftovers = {}
for name in pairs(by_atom) do
if not seen[name] then leftovers[#leftovers + 1] = name end
end
table.sort(leftovers)
for _, name in ipairs(leftovers) do emit(name, by_atom[name]) end
end
local function render_section_relations(add, view)
local wrote = false
for _, a in ipairs(view.decls) do
local rels = (a.paths and a.paths.relations) or {}
if #rels > 0 then
wrote = true
add("### " .. a.name)
for _, rel in ipairs(rels) do
local dest = rel.destination or rel.producer_destination or ""
local slot = slot_suffix(dest)
local dest_s = tostring(dest)
if slot then dest_s = dest_s .. " (slot " .. slot .. ")" end
add(string.format("- `%s` words %s → %s dest %s",
rel.semantic or "?",
tostring(rel.producer_word or "?"),
tostring(rel.consumer_word or "?"),
dest_s))
end
add("")
end
end
if not wrote then add("_(none)_"); add("") end
end
local function render_section_forward(add, view)
local wrote = false
for _, a in ipairs(view.decls) do
local gpr = a.paths and a.paths.forward_state and a.paths.forward_state.gpr_values
if gpr and next(gpr) ~= nil then
wrote = true
add("### " .. a.name)
local keys = {}
for k in pairs(gpr) do keys[#keys + 1] = k end
table.sort(keys)
for _, k in ipairs(keys) do
local slot = gpr[k]
add(string.format("- `%s` %s", k, (slot and slot.kind) or "unknown"))
end
add("")
end
end
if not wrote then add("_(none)_"); add("") end
end
local SECTION_RENDERERS = {
{ header = "## Declarations", render = render_section_declarations },
{ header = "## Components", render = render_section_components },
{ header = "## RegUse schemas", render = render_section_reguse },
{ header = "## Annotations", render = render_section_annotations },
{ header = "## Binds_* structs", render = render_section_binds },
{ header = "## Phases / views / ctx", render = render_section_phases },
{ header = "## Register aliases", render = render_section_aliases },
{ header = "## Auto-reg", render = render_section_autoreg },
{ header = "## Collisions", render = render_section_collisions },
{ header = "## Findings", render = render_section_findings },
{ header = "## Relations", render = render_section_relations },
{ header = "## Forward GPR", render = render_section_forward },
}
--- Render the consolidated per-module markdown (`build/<module>.atom_meta_report.md`).
--- One ModuleView from the corpus; SECTION_RENDERERS walks it.
--- @param view table
--- @return string
local function render_module_meta_report(view)
local dir_basename = source_basename(view.dir)
local lines = { local lines = {
"# " .. dir_basename .. " — atom meta report", "# " .. dir_basename .. " — atom meta report",
"> Auto-generated by ps1_meta.lua (passes/report.lua). Do not edit.", "> Auto-generated by ps1_meta.lua (passes/report.lua). Do not edit.",
@@ -264,199 +591,41 @@ local function render_module_meta_report(dir, dir_sources, annot_results, sa_res
} }
local function add(s) lines[#lines + 1] = s end local function add(s) lines[#lines + 1] = s end
-- Module summary table. local kinds = count_kinds(view.decls)
local n_atoms = 0 local n_annot, n_binds, n_macros = 0, 0, 0
local n_annot = 0 for _, src in ipairs(view.sources) do
local n_binds = 0 n_annot = n_annot + #((src.scan and src.scan.atom_infos) or {})
local n_macros = 0 n_binds = n_binds + #((src.scan and src.scan.binds) or {})
local n_bare, n_proc = 0, 0 n_macros = n_macros + #((src.scan and src.scan.macros) or {})
for _, r in ipairs(annot_results) do
n_atoms = n_atoms + #r.atoms
n_annot = n_annot + #r.annots
n_binds = n_binds + #r.binds
n_macros = n_macros + #r.macros
end end
for _, a in ipairs(sa_results.atoms or {}) do local n_err, n_warn, n_info = 0, 0, 0
if a.kind == "comp_bare" then n_bare = n_bare + 1 for _, f in ipairs(view.findings or {}) do
elseif a.kind == "comp_proc" then n_proc = n_proc + 1 if f.kind == "error" then n_err = n_err + 1
elseif f.kind == "warning" then n_warn = n_warn + 1
else n_info = n_info + 1
end end
end end
add("## Module summary"); add("") add("## Module summary"); add("")
add("| metric | value |"); add("|--------|-------|") add("| metric | value |"); add("|--------|-------|")
add(string.format("| sources | %d |", #dir_sources)) add(string.format("| sources | %d |", #view.sources))
add(string.format("| atoms | %d (atoms: %d, comp_bare: %d, comp_proc: %d) |", add(string.format("| decls | %d (atom: %d, atom_proc: %d, comp_bare: %d, comp_proc: %d) |",
#(sa_results.atoms or {}), #view.decls, kinds.atom, kinds.atom_proc, kinds.comp_bare, kinds.comp_proc))
#(sa_results.atoms or {}) - n_bare - n_proc, n_bare, n_proc))
add(string.format("| annotations | %d |", n_annot)) add(string.format("| annotations | %d |", n_annot))
add(string.format("| binds structs | %d |", n_binds)) add(string.format("| binds structs | %d |", n_binds))
add(string.format("| macro decls | %d |", n_macros)) add(string.format("| macro decls | %d |", n_macros))
add(string.format("| findings | %d (errors: %d, warnings: %d, info: %d) |", add(string.format("| findings | %d (errors: %d, warnings: %d, info: %d) |",
#(sa_results.findings or {}), #(view.findings or {}), n_err, n_warn, n_info))
#(sa_results.errors or {}),
#(sa_results.warnings or {}),
#(sa_results.info or {})))
add("") add("")
-- Sources
add("## Sources"); add("") add("## Sources"); add("")
for _, s in ipairs(dir_sources) do add("- `" .. s.path .. "`") end for _, s in ipairs(view.sources) do add("- `" .. s.path .. "`") end
add("") add("")
-- Atoms (annotation) for _, row in ipairs(SECTION_RENDERERS) do
add("## Atoms"); add("") add(row.header); add("")
add("| kind | name | source | line |"); add("|------|------|--------|------|") row.render(add, view)
for _, r in ipairs(annot_results) do
local src_name = source_basename(r.source)
for _, a in ipairs(r.atoms) do
add(string.format("| atom | %s | %s | %d |", a.name, src_name, a.line))
end
end end
add("")
-- Annotations
add("## Annotations"); add("")
if #annot_results == 0 then
add("_(none)_")
else
add("| source | line | name | binds | reads | writes |")
add("|--------|------|------|-------|-------|--------|")
for _, r in ipairs(annot_results) do
local src_name = source_basename(r.source)
for _, a in ipairs(r.annots) do
local binds = a.binds or ""
local reads = (#a.reads > 0 and table.concat(a.reads, ",")) or ""
local writes = (#a.writes > 0 and table.concat(a.writes, ",")) or ""
add(string.format("| %s | %d | %s | %s | %s | %s |"
, src_name, a.line, a.name, binds, reads, writes))
end
end
end
add("")
-- Binds_* structs
add("## Binds_* structs"); add("")
if #annot_results == 0 then
add("_(none)_")
else
for _, r in ipairs(annot_results) do
local src_name = source_basename(r.source)
for _, b in ipairs(r.binds) do
add(string.format("### %s (%s:%d, %d bytes)",
b.name, src_name, b.line, b.bytes))
for _, f in ipairs(b.fields) do
add(string.format("- `+%d %s`", f.offset, f.name))
end
add("")
end
end
end
-- Macro decls
add("## Macro word-count declarations"); add("")
if #annot_results == 0 then
add("_(none)_")
else
add("| source | line | macro declaration |")
add("|--------|------|-------------------|")
for _, r in ipairs(annot_results) do
local src_name = source_basename(r.source)
for _, m in ipairs(r.macros) do
add(string.format("| %s | %d | %s |",
src_name, m.line, m.name))
end
end
end
add("")
-- Findings by atom (static-analysis)
add("## Static analysis — findings by atom"); add("")
local by_atom = {}
for _, f in ipairs(sa_results.findings or {}) do
by_atom[f.atom] = by_atom[f.atom] or {}
by_atom[f.atom][#by_atom[f.atom] + 1] = f
end
if next(by_atom) == nil then
add("_(no findings)_")
else
for _, a in ipairs(sa_results.atoms or {}) do
local fs = by_atom[a.name]
if fs then
add(string.format("### %s", a.name))
for _, f in ipairs(fs) do
add(string.format("- `[%s] %s`", f.check, f.msg))
end
add("")
end
end
end
-- Errors / Warnings / Info
local function add_findings(label, entries)
add(string.format("## %s", label))
if #entries == 0 then
add("_(none)_")
else
for _, e in ipairs(entries) do
add(string.format("- line %d %s", e.line, e.msg))
end
end
add("")
end
add_findings("Errors", sa_results.errors or {})
add_findings("Warnings", sa_results.warnings or {})
add_findings("Info", sa_results.info or {})
-- Per-atom cycle counts (path-aware)
add("## Per-atom cycle counts (path-aware, best case, no stalls)"); add("")
add("| atom | source | min | max | branches | paths | notes |")
add("|------|--------|-----|-----|----------|-------|-------|")
local sorted = {}
for _, a in ipairs(sa_results.atoms or {}) do sorted[#sorted + 1] = a end
table.sort(sorted, function(x, y)
return ((x.paths or {}).cycles_max or 0) > ((y.paths or {}).cycles_max or 0)
end)
for _, a in ipairs(sorted) do
local p = a.paths or {}
local src_name = a.source_path and source_basename(a.source_path) or ""
local notes = ""
if p.has_loops then notes = notes .. " [loop!]" end
if p.unknown_macros and #p.unknown_macros > 0 then
notes = notes .. " [unknown: " .. table.concat(p.unknown_macros, ", ") .. "]"
end
add(string.format("| %s | %s | %d | %d | %d | %d | %s |",
a.name, src_name,
p.cycles_min or 0, p.cycles_max or 0,
p.branches or 0, p.paths or 0, notes))
end
add("")
-- Per-source scan summary
add("## Per-source scan summary"); add("")
for _, src in ipairs(dir_sources) do
local src_atoms = {}
for _, a in ipairs(sa_results.atoms or {}) do
if a.source_path == src.path then src_atoms[#src_atoms + 1] = a end
end
if #src_atoms > 0 then
local mn, mx = math.huge, -1
for _, a in ipairs(src_atoms) do
local p = a.paths or {}
if (p.cycles_min or 0) < mn then mn = p.cycles_min or 0 end
if (p.cycles_max or 0) > mx then mx = p.cycles_max or 0 end
end
local path_str
if mx > 0 then
path_str = string.format(" cycles=%d..%d", mn, mx)
else
path_str = string.format(" %d cycles", mn)
end
add(string.format("- `%s` — %d atom%s%s",
src.basename, #src_atoms,
#src_atoms == 1 and "" or "s", path_str))
end
end
add("")
return table.concat(lines, "\n") .. "\n" return table.concat(lines, "\n") .. "\n"
end end
@@ -474,19 +643,8 @@ local REPORT_RENDERERS = {
basename = function(dir_basename) return dir_basename .. ".atom_meta_report" end, basename = function(dir_basename) return dir_basename .. ".atom_meta_report" end,
once = false, once = false,
gather = function(ctx, dir, dir_sources) gather = function(ctx, dir, dir_sources)
-- Annotations: re-run `annotation.validate()` per source (the existing pattern). local corpus = ctx.shared.corpus
local annot_results = {} return render_module_meta_report(build_module_view(dir, dir_sources, corpus))
for _, src in ipairs(dir_sources) do
if src.scan then
local r = annotation.validate(ctx, src, nil)
r.source = src.path
annot_results[#annot_results + 1] = r
end
end
-- Static-analysis: read stashed projection (no re-validate).
local dir_basename = dir:match("([^/\\]+)$") or dir
local sa_results = (ctx.shared.corpus.static_analysis_results or {})[dir_basename] or {}
return render_module_meta_report(dir, dir_sources, annot_results, sa_results)
end, end,
}, },
{ {
@@ -554,32 +712,30 @@ function M.run(ctx)
end end
end end
-- For the summary, compute per-module totals once (re-validating annotations per source — same pattern as the meta_report renderer). local view = build_module_view(dir, dir_sources, corpus)
local annot_results = {} local n_annot, n_binds, n_macros = 0, 0, 0
for _, src in ipairs(dir_sources) do for _, src in ipairs(dir_sources) do
if src.scan then n_annot = n_annot + #((src.scan and src.scan.atom_infos) or {})
local r = annotation.validate(ctx, src, nil) n_binds = n_binds + #((src.scan and src.scan.binds) or {})
r.source = src.path n_macros = n_macros + #((src.scan and src.scan.macros) or {})
annot_results[#annot_results + 1] = r end
local n_err, n_warn, n_info = 0, 0, 0
for _, f in ipairs(view.findings or {}) do
if f.kind == "error" then n_err = n_err + 1
elseif f.kind == "warning" then n_warn = n_warn + 1
else n_info = n_info + 1
end end
end end
local n_annot, n_binds, n_macros = 0, 0, 0
for _, r in ipairs(annot_results) do
n_annot = n_annot + #r.annots
n_binds = n_binds + #r.binds
n_macros = n_macros + #r.macros
end
local sa_results = (corpus.static_analysis_results or {})[dir_basename] or {}
all_modules[#all_modules + 1] = { all_modules[#all_modules + 1] = {
module = dir_basename, module = dir_basename,
atoms = #(sa_results.atoms or {}), atoms = #view.decls,
annots = n_annot, annots = n_annot,
binds = n_binds, binds = n_binds,
macros = n_macros, macros = n_macros,
findings = #(sa_results.findings or {}), findings = #(view.findings or {}),
errors = #(sa_results.errors or {}), errors = n_err,
warnings = #(sa_results.warnings or {}), warnings = n_warn,
info = #(sa_results.info or {}), info = n_info,
} }
end end
+28
View File
@@ -6,6 +6,7 @@
--- MipsAtom_Proc_ (kind = "atom_proc", body inside last {}) --- MipsAtom_Proc_ (kind = "atom_proc", body inside last {})
--- MipsAtomComp_ (kind = "comp_bare") --- MipsAtomComp_ (kind = "comp_bare")
--- MipsAtomComp_Proc_ (kind = "comp_proc", body inside last {}) --- MipsAtomComp_Proc_ (kind = "comp_proc", body inside last {})
--- MipsAtomComp_ProcMap_ (kind = "comp_proc", body is the one command)
--- atom_dbg_skip — bare whole-atom/component debug-step marker; following declaration disambiguates --- atom_dbg_skip — bare whole-atom/component debug-step marker; following declaration disambiguates
--- MipsCode code_<name> (kind = "raw_atom", offsets pass only) --- MipsCode code_<name> (kind = "raw_atom", offsets pass only)
--- typedef Struct_(Binds_X) { fields } --- typedef Struct_(Binds_X) { fields }
@@ -1380,6 +1381,32 @@ local function parse_mips_atom_comp_proc(source, pos, ident_end, line_of, out)
return after_paren return after_paren
end end
--- Parse: `MipsAtomComp_ProcMap_(ab, command)` — body is the one command (second arg).
--- Reuses the proc name walk. Kind is `comp_proc`. The C expansion wraps
--- `atom_dbg_skip MipsAtomComp_Proc_(ab, {command })`; source-as-written is the map.
--- @param source string
--- @param pos integer
--- @param ident_end integer
--- @param line_of fun(pos: integer): integer
--- @param out SourceScan
--- @return integer
local function parse_mips_atom_comp_proc_map(source, pos, ident_end, line_of, out)
local inner, after_paren, open_paren = read_parens_after(source, ident_end)
if not inner then return after_paren end
local args = duffle.split_top_level_commas(inner)
if #args < 2 then return after_paren end
local command = duffle.trim(args[2])
if command == "" then return after_paren end
local raw_name = duffle.find_function_decl_for(source, open_paren, SLICE_MIPS_CODE_LEN)
if not raw_name then raw_name = "?" end
local name = strip_ac_prefix(raw_name)
local body_off = open_paren + 1 + (inner:find(command, 1, true) or 1) - 1
register_atom(out, "comp_proc", line_of(pos), name, command, body_off, raw_name, pos, after_paren, source)
local entry = out.atoms[#out.atoms]
entry.map_command = command
return after_paren
end
--- Parse: `MipsAtom_Proc_(<name>, <abuilder>, { <body> })` — body is inside the LAST `{` in args. --- Parse: `MipsAtom_Proc_(<name>, <abuilder>, { <body> })` — body is inside the LAST `{` in args.
--- Per Task 12.10: full support for the runtime-proc atom form. Registers the atom --- Per Task 12.10: full support for the runtime-proc atom form. Registers the atom
--- with kind `"atom_proc"` so offsets.lua / components.lua can emit --- with kind `"atom_proc"` so offsets.lua / components.lua can emit
@@ -2109,6 +2136,7 @@ local DECL_PARSERS = {
MipsAtom_Proc_ = parse_mips_atom_proc, MipsAtom_Proc_ = parse_mips_atom_proc,
MipsAtomComp_ = parse_mips_atom_comp, MipsAtomComp_ = parse_mips_atom_comp,
MipsAtomComp_Proc_ = parse_mips_atom_comp_proc, MipsAtomComp_Proc_ = parse_mips_atom_comp_proc,
MipsAtomComp_ProcMap_ = parse_mips_atom_comp_proc_map,
-- `atom_dbg_skip` is the only debug-skip parser entry. Every other -- `atom_dbg_skip` is the only debug-skip parser entry. Every other
-- identifier follows the ordinary unrelated-token path; there is no alias. -- identifier follows the ordinary unrelated-token path; there is no alias.
atom_dbg_skip = parse_dbg_skip_marker, atom_dbg_skip = parse_dbg_skip_marker,
+9 -3
View File
@@ -1322,8 +1322,14 @@ local function check_hazard_nop_use(atom, _pipe_ctx, findings)
if is_load_delay then if is_load_delay then
-- Determine the destination register from the load's `writes` field. -- Determine the destination register from the load's `writes` field.
local prev_writes = gpr_effects[prev_ident] and gpr_effects[prev_ident].writes or {} local prev_writes = gpr_effects[prev_ident] and gpr_effects[prev_ident].writes or {}
local prev_args = prev_ev.args or {} local dest_pos = prev_writes[1]
local load_dest = prev_writes[1] and prev_args[prev_writes[1]] or "<load-destination>" local load_dest = dest_pos and (gpr_identity(prev_ev, dest_pos) or (prev_ev.args or {})[dest_pos]) or "<load-destination>"
local authored = dest_pos and (prev_ev.args or {})[dest_pos] or load_dest
local shown = authored
if type(load_dest) == "string" and load_dest:sub(1, 7) == "reguse:" then
local slot = load_dest:match("([^:]+)$")
if slot then shown = authored .. " (slot " .. slot .. ")" end
end
findings[#findings + 1] = { findings[#findings + 1] = {
check = "hazard_nop_use", check = "hazard_nop_use",
kind = "info", kind = "info",
@@ -1336,7 +1342,7 @@ local function check_hazard_nop_use(atom, _pipe_ctx, findings)
producer_destination = load_dest, producer_destination = load_dest,
consumer_token = "<would-be-consumer>", consumer_token = "<would-be-consumer>",
msg = string.format("%s at line %d: nop at word %d is modeled-required (load-delay slot for %s)" msg = string.format("%s at line %d: nop at word %d is modeled-required (load-delay slot for %s)"
, atom.name, ev_line, ev_word, load_dest , atom.name, ev_line, ev_word, shown
), ),
} }
else else