mirror of
https://github.com/Ed94/pikuma_ps1.git
synced 2026-09-08 09:19:06 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a825a59c7 | ||
|
|
f8b28be02e | ||
|
|
ffc66052f8 | ||
|
|
7764612325 |
Vendored
+43
@@ -0,0 +1,43 @@
|
|||||||
|
# Package and install the local VS Code Insiders extensions under .vscode/.
|
||||||
|
# Usage:
|
||||||
|
# .\install_extensions.ps1
|
||||||
|
# .\install_extensions.ps1 -SkipPackage
|
||||||
|
|
||||||
|
param([switch] $SkipPackage)
|
||||||
|
|
||||||
|
$path_vscode = $PSScriptRoot
|
||||||
|
$code_insiders = "C:\apps\Microsoft VS Code Insiders\bin\code-insiders.cmd"
|
||||||
|
if (-not (test-path -literalpath $code_insiders)) {
|
||||||
|
$found = get-command code-insiders -erroraction silentlycontinue
|
||||||
|
if ($found) { $code_insiders = $found.source }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (test-path -literalpath $code_insiders)) { throw "code-insiders not found. Install VS Code Insiders or add it to PATH." }
|
||||||
|
|
||||||
|
$extensions = @(
|
||||||
|
(join-path $path_vscode "tape-atom-syntax"),
|
||||||
|
(join-path $path_vscode "cozy-and-windy")
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($extension in $extensions) {
|
||||||
|
$package_json = join-path $extension "package.json"
|
||||||
|
if (-not (test-path -literalpath $package_json)) { throw "missing $package_json" }
|
||||||
|
|
||||||
|
$manifest = get-content -literalpath $package_json -raw | convertfrom-json
|
||||||
|
$vsix = join-path $extension ("{0}-{1}.vsix" -f $manifest.name, $manifest.version)
|
||||||
|
|
||||||
|
if (-not $SkipPackage) {
|
||||||
|
if (-not $manifest.scripts.package) { throw "$package_json has no scripts.package" }
|
||||||
|
write-host "packaging $($manifest.displayName) ($($manifest.name)@$($manifest.version))"
|
||||||
|
& npm --prefix $extension run package
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "npm run package failed for $extension" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (test-path -literalpath $vsix)) { throw "missing $vsix" }
|
||||||
|
|
||||||
|
write-host "installing $vsix"
|
||||||
|
& $code_insiders --install-extension $vsix --force
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "install failed for $vsix" }
|
||||||
|
}
|
||||||
|
|
||||||
|
write-host "done. reload the Insiders window (Developer: Reload Window)."
|
||||||
@@ -148,6 +148,7 @@ typedef void Proc_(VoidFn) (void);
|
|||||||
#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 OA_(type, aexpr) C_(U4, & C_(type*,0) aexpr)
|
||||||
#define OT_(field) O_(typeof_ptr(& field), field))
|
#define OT_(field) O_(typeof_ptr(& field), field))
|
||||||
#define S_(data) C_(U4, sizeof(data))
|
#define S_(data) C_(U4, sizeof(data))
|
||||||
|
|
||||||
|
|||||||
+57
-28
@@ -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,42 @@ 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 */
|
||||||
|
#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_y, base, O_(V3_S4,y)) \
|
||||||
|
, add_si(rt_z, base, O_(V3_S4,z))
|
||||||
|
WORD_COUNT(mac_add_si_v3s4, 3)
|
||||||
|
|
||||||
/* atom_dbg_skip */
|
/* atom_dbg_skip */
|
||||||
#define mac_sub_v3s4(rds_x, rds_y, rds_z, rt_x, rt_y, rt_z) \
|
#define mac_sub_v3s4(rds_x, rds_y, rds_z, rt_x, rt_y, rt_z) \
|
||||||
sub_s(rds_x, rds_x, rt_x) \
|
sub_s(rds_x, rds_x, rt_x) \
|
||||||
@@ -105,6 +128,24 @@ WORD_COUNT(mac_store_rects2, 4)
|
|||||||
, or_i_self( dst, u4_lo(imm))
|
, or_i_self( dst, u4_lo(imm))
|
||||||
WORD_COUNT(mac_load_word_imm, 2)
|
WORD_COUNT(mac_load_word_imm, 2)
|
||||||
|
|
||||||
|
#define mac_shift_aright_v3_self(dt_x, dt_y, dt_z, shift_amount) \
|
||||||
|
shift_aright(dt_x, dt_x, shift_amount) \
|
||||||
|
, shift_aright(dt_y, dt_y, shift_amount) \
|
||||||
|
, shift_aright(dt_z, dt_z, shift_amount)
|
||||||
|
WORD_COUNT(mac_shift_aright_v3_self, 3)
|
||||||
|
|
||||||
|
#define mac_shift_aright_var_v3(rd_v0, rd_v1, rd_v2, rs_v0, rs_v1, rs_v2, r_shift) \
|
||||||
|
shift_aright_var(rd_v0, rs_v0, r_shift) \
|
||||||
|
, shift_aright_var(rd_v1, rs_v1, r_shift) \
|
||||||
|
, shift_aright_var(rd_v2, rs_v2, r_shift)
|
||||||
|
WORD_COUNT(mac_shift_aright_var_v3, 3)
|
||||||
|
|
||||||
|
#define mac_shift_aright_var_v3_self(rds_v0, rds_v1, rds_v2, r_shift) \
|
||||||
|
shift_aright_var(rds_v0, rds_v0, r_shift) \
|
||||||
|
, shift_aright_var(rds_v1, rds_v1, r_shift) \
|
||||||
|
, shift_aright_var(rds_v2, rds_v2, r_shift)
|
||||||
|
WORD_COUNT(mac_shift_aright_var_v3_self, 3)
|
||||||
|
|
||||||
/* atom_dbg_skip */
|
/* atom_dbg_skip */
|
||||||
#define mac_load_tri_indices(r_face_cusor, r_i0, r_i1, r_i2) \
|
#define mac_load_tri_indices(r_face_cusor, r_i0, r_i1, r_i2) \
|
||||||
load_half_u(r_i0, r_face_cusor, 0 * S_(S2)) \
|
load_half_u(r_i0, r_face_cusor, 0 * S_(S2)) \
|
||||||
@@ -125,19 +166,19 @@ WORD_COUNT(mac_gte_store_f3, 3)
|
|||||||
, add_u_self(R_AT, r_vert_base) \
|
, add_u_self(R_AT, r_vert_base) \
|
||||||
, load_word(R_V0, R_AT, O_(V3_S2,x)) \
|
, load_word(R_V0, R_AT, O_(V3_S2,x)) \
|
||||||
, load_word(R_V1, R_AT, O_(V3_S2,z)) \
|
, load_word(R_V1, R_AT, O_(V3_S2,z)) \
|
||||||
, gte_mv_to_data_r(R_V0, C2_VXY0) \
|
, LdSlot_ gte_mv_to_data_r(R_V0, C2_VXY0) \
|
||||||
, gte_mv_to_data_r(R_V1, C2_VZ0) \
|
, gte_mv_to_data_r(R_V1, C2_VZ0) \
|
||||||
, shift_lleft(R_AT, r_v1, v3s2_byteoff) \
|
, shift_lleft(R_AT, r_v1, v3s2_byteoff) \
|
||||||
, add_u_self(R_AT, r_vert_base) \
|
, add_u_self(R_AT, r_vert_base) \
|
||||||
, load_word(R_V0, R_AT, O_(V3_S2,x)) \
|
, load_word(R_V0, R_AT, O_(V3_S2,x)) \
|
||||||
, load_word(R_V1, R_AT, O_(V3_S2,z)) \
|
, load_word(R_V1, R_AT, O_(V3_S2,z)) \
|
||||||
, gte_mv_to_data_r(R_V0, C2_VXY1) \
|
, LdSlot_ gte_mv_to_data_r(R_V0, C2_VXY1) \
|
||||||
, gte_mv_to_data_r(R_V1, C2_VZ1) \
|
, gte_mv_to_data_r(R_V1, C2_VZ1) \
|
||||||
, shift_lleft(R_AT, r_v2, v3s2_byteoff) \
|
, shift_lleft(R_AT, r_v2, v3s2_byteoff) \
|
||||||
, add_u_self(R_AT, r_vert_base) \
|
, add_u_self(R_AT, r_vert_base) \
|
||||||
, load_word(R_V0, R_AT, O_(V3_S2,x)) \
|
, load_word(R_V0, R_AT, O_(V3_S2,x)) \
|
||||||
, load_word(R_V1, R_AT, O_(V3_S2,z)) \
|
, load_word(R_V1, R_AT, O_(V3_S2,z)) \
|
||||||
, gte_mv_to_data_r(R_V0, C2_VXY2) \
|
, LdSlot_ gte_mv_to_data_r(R_V0, C2_VXY2) \
|
||||||
, gte_mv_to_data_r(R_V1, C2_VZ2)
|
, gte_mv_to_data_r(R_V1, C2_VZ2)
|
||||||
WORD_COUNT(mac_gte_load_tri_verts, 18)
|
WORD_COUNT(mac_gte_load_tri_verts, 18)
|
||||||
|
|
||||||
@@ -162,11 +203,11 @@ WORD_COUNT(mac_gte_store_g4_p3, 1)
|
|||||||
WORD_COUNT(mac_gte_sqr_v3, 8)
|
WORD_COUNT(mac_gte_sqr_v3, 8)
|
||||||
|
|
||||||
/* atom_dbg_skip */
|
/* atom_dbg_skip */
|
||||||
#define mac_gte_sqr_v3s4(r_sx, r_sy, r_sz, nop_slot) \
|
#define mac_gte_sqr_v3s4(r_sx, r_sy, r_sz, delay_slot) \
|
||||||
gte_mv_to_data_r(r_sx, C2_IR1) \
|
gte_mv_to_data_r(r_sx, C2_IR1) \
|
||||||
, gte_mv_to_data_r(r_sy, C2_IR2) \
|
, gte_mv_to_data_r(r_sy, C2_IR2) \
|
||||||
, gte_mv_to_data_r(r_sz, C2_IR3) \
|
, gte_mv_to_data_r(r_sz, C2_IR3) \
|
||||||
, nop_slot \
|
, delay_slot \
|
||||||
, gte_cmdw_sqr
|
, gte_cmdw_sqr
|
||||||
WORD_COUNT(mac_gte_sqr_v3s4, 5)
|
WORD_COUNT(mac_gte_sqr_v3s4, 5)
|
||||||
|
|
||||||
@@ -204,25 +245,13 @@ WORD_COUNT(mac_trans_mt3s3s4, 6)
|
|||||||
, shift_aright(r_mag_sq, r_mag_sq, 1)
|
, shift_aright(r_mag_sq, r_mag_sq, 1)
|
||||||
WORD_COUNT(mac_lzcr_round_even_half_shift, 5)
|
WORD_COUNT(mac_lzcr_round_even_half_shift, 5)
|
||||||
|
|
||||||
#define mac_shift_aright_var_v3(rd_v0, rd_v1, rd_v2, rs_v0, rs_v1, rs_v2, r_shift) \
|
|
||||||
shift_aright_var(rd_v0, rs_v0, r_shift) \
|
|
||||||
, shift_aright_var(rd_v1, rs_v1, r_shift) \
|
|
||||||
, shift_aright_var(rd_v2, rs_v2, r_shift)
|
|
||||||
WORD_COUNT(mac_shift_aright_var_v3, 3)
|
|
||||||
|
|
||||||
#define mac_shift_aright_var_v3_self(rds_v0, rds_v1, rds_v2, r_shift) \
|
|
||||||
shift_aright_var(rds_v0, rds_v0, r_shift) \
|
|
||||||
, shift_aright_var(rds_v1, rds_v1, r_shift) \
|
|
||||||
, shift_aright_var(rds_v2, rds_v2, r_shift)
|
|
||||||
WORD_COUNT(mac_shift_aright_var_v3_self, 3)
|
|
||||||
|
|
||||||
#define mac_gte_general_purpose_interopolation(to_ir0, to_ir1, to_ir2, to_ir3, fr_mac1, fr_mac2, fr_mac3, nop_slot1, nop_slot2) \
|
#define mac_gte_general_purpose_interopolation(to_ir0, to_ir1, to_ir2, to_ir3, fr_mac1, fr_mac2, fr_mac3, nop_slot1, nop_slot2) \
|
||||||
gte_mv_to_data_r(to_ir0, C2_IR0) \
|
gte_mv_to_data_r(to_ir0, C2_IR0) \
|
||||||
, 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) \
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -25,6 +25,14 @@
|
|||||||
#pragma region duffle
|
#pragma region duffle
|
||||||
|
|
||||||
|
|
||||||
|
// --- atom: example_atom (10 words) ---
|
||||||
|
|
||||||
|
#define _atom_offset_example_atom_proc_skip 2
|
||||||
|
|
||||||
|
enum {
|
||||||
|
atom_offset_example_atom_proc_skip = _atom_offset_example_atom_proc_skip,
|
||||||
|
};
|
||||||
|
|
||||||
// --- atom: normalize_v3s4 (47 words) ---
|
// --- atom: normalize_v3s4 (47 words) ---
|
||||||
|
|
||||||
#define _atom_offset_aligned_done_srav_path 3
|
#define _atom_offset_aligned_done_srav_path 3
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
|||||||
})
|
})
|
||||||
|
|
||||||
/* Words: 11; Correctly inserts a primitive into the Ordering Table linked list. */
|
/* Words: 11; Correctly inserts a primitive into the Ordering Table linked list. */
|
||||||
I_ Slice_MipsCode ac_insert_ot_tag(AtomBuilder_R ab, U4 r_ot_base, U4 r_prim_cursor, U4 poly_size) MipsAtomComp_Proc_(ab, {
|
// TODO(Ed): Expose R_T1 as a r_t0, r_V0 as r_t2
|
||||||
|
I_ Slice_MipsCode ac_insert_ot_tag(AtomBuilder_R ab, Reg r_ot_base, Reg r_prim_cursor, U2 poly_size) MipsAtomComp_Proc_(ab, {
|
||||||
shift_lleft( R_T1, R_T1, S_(U4)/2), // T1 = otz * S_(U4) (otz arg is implicit R_T1)
|
shift_lleft( R_T1, R_T1, S_(U4)/2), // T1 = otz * S_(U4) (otz arg is implicit R_T1)
|
||||||
add_u_self( R_T1, r_ot_base), // T1 = & OrderingTable[OTZ]
|
add_u_self( R_T1, r_ot_base), // T1 = & OrderingTable[OTZ]
|
||||||
load_word( R_AT, R_T1, O_(PolyTag,code)), // AT = old_ot_head
|
load_word( R_AT, R_T1, O_(PolyTag,code)), // AT = old_ot_head
|
||||||
|
|||||||
+3
-6
@@ -418,14 +418,11 @@ typedef Struct_(PolyTag) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/* DSL cast convention: every cast uses `C_()`, every pointer qualifier is `R_` (restrict) or `V_` (volatile).
|
|
||||||
* No raw C-style casts. RHS values are assumed to be `U4` — caller passes a `U4` directly. */
|
|
||||||
#define set_len(tag,v) (C_(PolyTag_R,tag)->len = u4_(v))
|
#define set_len(tag,v) (C_(PolyTag_R,tag)->len = u4_(v))
|
||||||
#define set_addr(tag,v) (C_(PolyTag_R,tag)->addr = u4_(v))
|
#define set_addr(tag,v) (C_(PolyTag_R,tag)->addr = u4_(v))
|
||||||
/* `set_code` is no longer in the new PolyTag design — the code byte lives in the primitive body
|
/* `set_code` is no longer in the new PolyTag design
|
||||||
* (e.g. `((Poly_F3*)(p))->code`), not in the tag.
|
* (e.g. `((Poly_F3*)(p))->code`), not in the tag.
|
||||||
* Use the typed primitive structs (Poly_F3, Poly_G4, etc.) and the `set_poly_*` setters,
|
* Use the typed primitive structs (Poly_F3, Poly_G4, etc.) and the `set_poly_*` setters, which set both the tag's length and the code. */
|
||||||
* which set both the tag's length and the code. */
|
|
||||||
#define get_len(tag) C_(U4,C_(PolyTag_R,tag)->len)
|
#define get_len(tag) C_(U4,C_(PolyTag_R,tag)->len)
|
||||||
#define get_addr(tag) C_(U4,C_(PolyTag_R,tag)->addr)
|
#define get_addr(tag) C_(U4,C_(PolyTag_R,tag)->addr)
|
||||||
|
|
||||||
@@ -572,7 +569,7 @@ enum {
|
|||||||
/* Default TPage value libpsyx's SetDefDrawEnv writes (matches the `li v1, 10; sh v1, 20(v0)` sequence at C11_only.elf:0x8001273C). */
|
/* Default TPage value libpsyx's SetDefDrawEnv writes (matches the `li v1, 10; sh v1, 20(v0)` sequence at C11_only.elf:0x8001273C). */
|
||||||
gp0_tpage_default = 10,
|
gp0_tpage_default = 10,
|
||||||
|
|
||||||
/* TPage semi-transparency mode payload values (NOT bit positions). */
|
/* TPage semi-transparency mode payload values. */
|
||||||
gp0_tpage_semi_trans_none = 0x0,
|
gp0_tpage_semi_trans_none = 0x0,
|
||||||
gp0_tpage_semi_trans_alpha = 0x1,
|
gp0_tpage_semi_trans_alpha = 0x1,
|
||||||
gp0_tpage_semi_trans_add = 0x2,
|
gp0_tpage_semi_trans_add = 0x2,
|
||||||
|
|||||||
+19
-41
@@ -28,9 +28,9 @@ FI_ Slice_MipsCode ac_gte_store_f3(AtomBuilder_R ab, U4 r_primitive_cursor) atom
|
|||||||
|
|
||||||
/* Words: 18; Translates indices to vertex addresses and pushes them to GTE */
|
/* Words: 18; Translates indices to vertex addresses and pushes them to GTE */
|
||||||
I_ Slice_MipsCode ac_gte_load_tri_verts(AtomBuilder_R ab, U4 r_vert_base, U4 r_v0, U4 r_v1, U4 r_v2) atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
I_ Slice_MipsCode ac_gte_load_tri_verts(AtomBuilder_R ab, U4 r_vert_base, U4 r_v0, U4 r_v1, U4 r_v2) atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
||||||
shift_lleft(R_AT, r_v0, v3s2_byteoff), add_u_self(R_AT, r_vert_base), load_word(R_V0, R_AT, O_(V3_S2,x)), load_word(R_V1, R_AT, O_(V3_S2,z)), gte_mv_to_data_r(R_V0, C2_VXY0), gte_mv_to_data_r(R_V1, C2_VZ0),
|
shift_lleft(R_AT, r_v0, v3s2_byteoff), add_u_self(R_AT, r_vert_base), load_word(R_V0, R_AT, O_(V3_S2,x)), load_word(R_V1, R_AT, O_(V3_S2,z)), LdSlot_ gte_mv_to_data_r(R_V0, C2_VXY0), gte_mv_to_data_r(R_V1, C2_VZ0),
|
||||||
shift_lleft(R_AT, r_v1, v3s2_byteoff), add_u_self(R_AT, r_vert_base), load_word(R_V0, R_AT, O_(V3_S2,x)), load_word(R_V1, R_AT, O_(V3_S2,z)), gte_mv_to_data_r(R_V0, C2_VXY1), gte_mv_to_data_r(R_V1, C2_VZ1),
|
shift_lleft(R_AT, r_v1, v3s2_byteoff), add_u_self(R_AT, r_vert_base), load_word(R_V0, R_AT, O_(V3_S2,x)), load_word(R_V1, R_AT, O_(V3_S2,z)), LdSlot_ gte_mv_to_data_r(R_V0, C2_VXY1), gte_mv_to_data_r(R_V1, C2_VZ1),
|
||||||
shift_lleft(R_AT, r_v2, v3s2_byteoff), add_u_self(R_AT, r_vert_base), load_word(R_V0, R_AT, O_(V3_S2,x)), load_word(R_V1, R_AT, O_(V3_S2,z)), gte_mv_to_data_r(R_V0, C2_VXY2), gte_mv_to_data_r(R_V1, C2_VZ2),
|
shift_lleft(R_AT, r_v2, v3s2_byteoff), add_u_self(R_AT, r_vert_base), load_word(R_V0, R_AT, O_(V3_S2,x)), load_word(R_V1, R_AT, O_(V3_S2,z)), LdSlot_ gte_mv_to_data_r(R_V0, C2_VXY2), gte_mv_to_data_r(R_V1, C2_VZ2),
|
||||||
})
|
})
|
||||||
|
|
||||||
/* Words: 3; Stores the 3 transformed (V2_S2 screen) vertices of the
|
/* Words: 3; Stores the 3 transformed (V2_S2 screen) vertices of the
|
||||||
@@ -38,7 +38,7 @@ I_ Slice_MipsCode ac_gte_load_tri_verts(AtomBuilder_R ab, U4 r_vert_base, U4 r_v
|
|||||||
* PIPELINE: post-RTPT, pre-RTPS (SXY0=v0.screen, SXY1=v1.screen, SXY2=v2.screen).
|
* PIPELINE: post-RTPT, pre-RTPS (SXY0=v0.screen, SXY1=v1.screen, SXY2=v2.screen).
|
||||||
* MUST be called BEFORE V3-RTPS, otherwise SXY0/1/2 get overwritten with v3
|
* MUST be called BEFORE V3-RTPS, otherwise SXY0/1/2 get overwritten with v3
|
||||||
* (RTPS writes only to SXY2, but to keep the three registers aligned with v0/v1/v2 you must store before RTPS). */
|
* (RTPS writes only to SXY2, but to keep the three registers aligned with v0/v1/v2 you must store before RTPS). */
|
||||||
FI_ Slice_MipsCode ac_gte_store_g4_p012(AtomBuilder_R ab, U4 r_primitive_cursor) atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
FI_ Slice_MipsCode ac_gte_store_g4_p012(AtomBuilder_R ab, Reg r_primitive_cursor) atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
||||||
gte_sw(C2_SXY0, r_primitive_cursor, O_(Poly_G4,p0)),
|
gte_sw(C2_SXY0, r_primitive_cursor, O_(Poly_G4,p0)),
|
||||||
gte_sw(C2_SXY1, r_primitive_cursor, O_(Poly_G4,p1)),
|
gte_sw(C2_SXY1, r_primitive_cursor, O_(Poly_G4,p1)),
|
||||||
gte_sw(C2_SXY2, r_primitive_cursor, O_(Poly_G4,p2)),
|
gte_sw(C2_SXY2, r_primitive_cursor, O_(Poly_G4,p2)),
|
||||||
@@ -65,12 +65,12 @@ FI_ Slice_MipsCode ac_gte_sqr_v3(AtomBuilder_R ab, U4 r_sx, U4 r_sy, U4 r_sz, U4
|
|||||||
* The SQR command always squares IR1/IR2/IR3 — those C2 registers are fixed.
|
* The SQR command always squares IR1/IR2/IR3 — those C2 registers are fixed.
|
||||||
* The GPRs holding the source vector are caller-determined.
|
* The GPRs holding the source vector are caller-determined.
|
||||||
* Words: 5 (3 mtc2 + 1 nop hazard + 1 cmd). */
|
* Words: 5 (3 mtc2 + 1 nop hazard + 1 cmd). */
|
||||||
FI_ Slice_MipsCode ac_gte_sqr_v3s4(AtomBuilder_R ab, Reg r_sx, Reg r_sy, Reg r_sz, MipsCode nop_slot)
|
FI_ Slice_MipsCode ac_gte_sqr_v3s4(AtomBuilder_R ab, Reg r_sx, Reg r_sy, Reg r_sz, MipsCode delay_slot)
|
||||||
atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
||||||
gte_mv_to_data_r(r_sx, C2_IR1),
|
gte_mv_to_data_r(r_sx, C2_IR1),
|
||||||
gte_mv_to_data_r(r_sy, C2_IR2),
|
gte_mv_to_data_r(r_sy, C2_IR2),
|
||||||
gte_mv_to_data_r(r_sz, C2_IR3),
|
gte_mv_to_data_r(r_sz, C2_IR3),
|
||||||
nop_slot, gte_cmdw_sqr,
|
delay_slot, gte_cmdw_sqr,
|
||||||
})
|
})
|
||||||
|
|
||||||
/* ─── STAGE 4 of normalize: mtc2 IR0..3 + GPF + mfc2 MAC + srav finalize ───
|
/* ─── STAGE 4 of normalize: mtc2 IR0..3 + GPF + mfc2 MAC + srav finalize ───
|
||||||
@@ -132,8 +132,7 @@ FI_ Slice_MipsCode ac_trans_mt3s3s4(AtomBuilder_R ab
|
|||||||
FI_ Slice_MipsCode ac_lzcr_round_even_half_shift(AtomBuilder_R ab,
|
FI_ Slice_MipsCode ac_lzcr_round_even_half_shift(AtomBuilder_R ab,
|
||||||
U4 r_shift,
|
U4 r_shift,
|
||||||
U4 r_mag_sq,
|
U4 r_mag_sq,
|
||||||
U4 r_mag_sq_copy
|
U4 r_mag_sq_copy)
|
||||||
)
|
|
||||||
atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
||||||
and_i(r_shift, r_shift, gte_lzcr_even_mask),
|
and_i(r_shift, r_shift, gte_lzcr_even_mask),
|
||||||
or_u(r_mag_sq_copy, r_mag_sq, 0),
|
or_u(r_mag_sq_copy, r_mag_sq, 0),
|
||||||
@@ -142,25 +141,6 @@ atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
|||||||
shift_aright(r_mag_sq, r_mag_sq, 1),
|
shift_aright(r_mag_sq, r_mag_sq, 1),
|
||||||
})
|
})
|
||||||
|
|
||||||
FI_ Slice_MipsCode ac_shift_aright_var_v3(AtomBuilder_R ab
|
|
||||||
, Reg rd_v0, Reg rd_v1, Reg rd_v2
|
|
||||||
, Reg rs_v0, Reg rs_v1, Reg rs_v2
|
|
||||||
, Reg r_shift)
|
|
||||||
MipsAtomComp_Proc_(ab, {
|
|
||||||
shift_aright_var(rd_v0, rs_v0, r_shift),
|
|
||||||
shift_aright_var(rd_v1, rs_v1, r_shift),
|
|
||||||
shift_aright_var(rd_v2, rs_v2, r_shift),
|
|
||||||
})
|
|
||||||
|
|
||||||
FI_ Slice_MipsCode ac_shift_aright_var_v3_self(AtomBuilder_R ab
|
|
||||||
, Reg rds_v0, Reg rds_v1, Reg rds_v2
|
|
||||||
, Reg r_shift)
|
|
||||||
MipsAtomComp_Proc_(ab, {
|
|
||||||
shift_aright_var(rds_v0, rds_v0, r_shift),
|
|
||||||
shift_aright_var(rds_v1, rds_v1, r_shift),
|
|
||||||
shift_aright_var(rds_v2, rds_v2, r_shift),
|
|
||||||
})
|
|
||||||
|
|
||||||
FI_ Slice_MipsCode ac_gte_general_purpose_interopolation(AtomBuilder_R ab
|
FI_ Slice_MipsCode ac_gte_general_purpose_interopolation(AtomBuilder_R ab
|
||||||
, Reg to_ir0, Reg to_ir1, Reg to_ir2, Reg to_ir3
|
, Reg to_ir0, Reg to_ir1, Reg to_ir2, Reg to_ir3
|
||||||
, Reg fr_mac1, Reg fr_mac2, Reg fr_mac3
|
, Reg fr_mac1, Reg fr_mac2, Reg fr_mac3
|
||||||
@@ -170,17 +150,16 @@ 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),
|
||||||
gte_mv_from_data_r(fr_mac3, C2_MAC3),
|
gte_mv_from_data_r(fr_mac3, C2_MAC3),
|
||||||
})
|
})
|
||||||
|
|
||||||
FI_ Slice_MipsCode gte_mv_from_data_r_mac123(AtomBuilder_R ab
|
FI_ Slice_MipsCode ac_gte_mv_from_data_r_mac123(AtomBuilder_R ab
|
||||||
, Reg fr_mac1, Reg fr_mac2, Reg fr_mac3
|
, Reg fr_mac1, Reg fr_mac2, Reg fr_mac3)
|
||||||
)
|
|
||||||
MipsAtomComp_Proc_(ab, {
|
MipsAtomComp_Proc_(ab, {
|
||||||
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),
|
||||||
@@ -257,9 +236,8 @@ internal S2 const gte_normalize_sqr_tbl[192] align_(2) = {
|
|||||||
0x0820, 0x081c, 0x0818, 0x0814, 0x0810, 0x080c, 0x0808, 0x0804,
|
0x0820, 0x081c, 0x0818, 0x0814, 0x0810, 0x080c, 0x0808, 0x0804,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define RegUse_(proc_name) (tmpl(RegUse,proc_name))
|
|
||||||
typedef Struct_(RegUse_normalize_v3s4_proc) {
|
typedef Struct_(RegUse_normalize_v3s4_proc) {
|
||||||
Reg scratch; // Scratch base carrier.
|
Reg const scratch; // Scratch base carrier.
|
||||||
Reg src_ptr;
|
Reg src_ptr;
|
||||||
Reg dst_ptr;
|
Reg dst_ptr;
|
||||||
Reg recip_est; // |v|² sum + shift-input + sqrtbl[index]
|
Reg recip_est; // |v|² sum + shift-input + sqrtbl[index]
|
||||||
@@ -267,7 +245,7 @@ typedef Struct_(RegUse_normalize_v3s4_proc) {
|
|||||||
Reg src_x;
|
Reg src_x;
|
||||||
union { Reg mac1_scratch; } t3;
|
union { Reg mac1_scratch; } t3;
|
||||||
union { Reg mac2_scratch; } t4;
|
union { Reg mac2_scratch; } t4;
|
||||||
union { Reg shift_count, btarget, lookup_addr, src_z; } t5;
|
union { Reg btarget, shift_count, lookup_addr, src_z; } t5;
|
||||||
};
|
};
|
||||||
/* ─── Full normalize (all 4 stages inline) ───
|
/* ─── Full normalize (all 4 stages inline) ───
|
||||||
* Generic 4-stage GTE normalize (SQR → sum+LZCR → align+sqrtbl → GPF+srav).
|
* Generic 4-stage GTE normalize (SQR → sum+LZCR → align+sqrtbl → GPF+srav).
|
||||||
@@ -306,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.lookup_addr, 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),
|
||||||
@@ -315,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.
|
||||||
@@ -350,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()
|
||||||
})
|
})
|
||||||
|
|||||||
+17
-23
@@ -33,7 +33,7 @@
|
|||||||
* gte.h — Geometry Transformation Engine (COP2) for the PS1
|
* gte.h — Geometry Transformation Engine (COP2) for the PS1
|
||||||
* ============================================================================
|
* ============================================================================
|
||||||
*
|
*
|
||||||
* Hand-rolled DSL for emitting GTE/MIPS instruction words as raw `.word` constants from C.
|
* Hand-rolled DSL for emitting GTE/MIPS instruction words from C.
|
||||||
* No GCC inline-assembly string syntax in the code body.
|
* No GCC inline-assembly string syntax in the code body.
|
||||||
*
|
*
|
||||||
* STYLE NOTES
|
* STYLE NOTES
|
||||||
@@ -191,27 +191,22 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* --- GTE Control Register Aliases (Pitfall 1) ---
|
/* --- GTE Control Register Aliases (Pitfall 1) ---
|
||||||
* Three pairs of aliases map to the SAME C2 control-register slot on real silicon:
|
* Three pairs of aliases map to the C2 control-register slot:
|
||||||
* C2[24] = gte_cr_RBK (background R) | gte_cr_OFX (screen offset X)
|
* C2[24] = gte_cr_RBK (background R) | gte_cr_OFX (screen offset X)
|
||||||
* C2[25] = gte_cr_GBK (background G) | gte_cr_OFY (screen offset Y)
|
* C2[25] = gte_cr_GBK (background G) | gte_cr_OFY (screen offset Y)
|
||||||
* C2[26] = gte_cr_BBK (background B) | gte_cr_H (projection plane distance H)
|
* C2[26] = gte_cr_BBK (background B) | gte_cr_H (projection plane distance H)
|
||||||
* Cross-alias writes inside one atom body, or across the wave-context boundary,
|
* Cross-alias writes inside one atom body, or across the wave-context boundary, silently clobber each other.
|
||||||
* silently clobber each other. The metaprogram's check_gte_cr_alias_writes
|
* The metaprogram's check_gte_cr_alias_writes (CHECK_RULES row) warns about each pair per source.
|
||||||
* (CHECK_RULES row) warns about each pair per source. See
|
* See psx-spx docs/gte_reference.md §"Control-register alias table" for the silicon rationale and the libgte outer-product convention.
|
||||||
* docs/gte_reference.md §"Control-register alias table" for the silicon
|
|
||||||
* rationale and the libgte outer-product convention.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* --- RT-matrix packed-slot convention (Pitfall 4) ---
|
/* --- RT-matrix packed-slot convention (Pitfall 4) ---
|
||||||
* The silicon packs two 16-bit RT elements per 32-bit C2 slot:
|
* The silicon packs two 16-bit RT elements per 32-bit C2 slot:
|
||||||
* C2[2] = (RT22 << 16) | RT13 (gte_cr_RT13 writes the low half, gte_cr_RT22 writes the high half)
|
* C2[2] = (RT22 << 16) | RT13 (gte_cr_RT13 writes the low half, gte_cr_RT22 writes the high half)
|
||||||
* C2[4] = (RT33 << 16) | RT22 (gte_cr_RT22 writes the low half — clobbers prior RT22 value if RT13 was also written)
|
* C2[4] = (RT33 << 16) | RT22 (gte_cr_RT22 writes the low half — clobbers prior RT22 value if RT13 was also written)
|
||||||
* OP and MVMVA read D1/D2/D3 from these packed slots. The libgte outer-product
|
* OP and MVMVA read D1/D2/D3 from these packed slots.
|
||||||
* convention (see ac_apply_matrix_lv at gte.atom.c:108-122) writes C2[2] then
|
* The libgte outer-product convention (see ac_apply_matrix_lv at gte.atom.c:108-122) writes C2[2] then C2[4] in sequence;
|
||||||
* C2[4] in sequence; the SECOND write's low half is RT22, not RT13. An agent
|
* the SECOND write's low half is RT22, not RT13.
|
||||||
* who writes gte_cr_RT13 then gte_cr_RT22 to the SAME source GPR clobbers the
|
|
||||||
* RT13 value. See docs/gte_reference.md §"RT-matrix packed-slot convention"
|
|
||||||
* for the canonical write pattern.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* --- GTE Control Register Indices (for ctc2/cfc2) ---
|
/* --- GTE Control Register Indices (for ctc2/cfc2) ---
|
||||||
@@ -300,8 +295,7 @@ enum { _C2_TX_SUBS_ = 0
|
|||||||
// #define gte_mv_from_data_r(rt, rd) enc_gte_tx(cop_mf, (rt), (rd)) /* Move GTE Control Register (rd) to GPR (rt) */
|
// #define gte_mv_from_data_r(rt, rd) enc_gte_tx(cop_mf, (rt), (rd)) /* Move GTE Control Register (rd) to GPR (rt) */
|
||||||
|
|
||||||
/* GTE Data vs Control Register Transfers
|
/* GTE Data vs Control Register Transfers
|
||||||
*
|
* Each macro emits a single instruction for one of MFC2/CFC2/MTC2/CTC2.
|
||||||
* Each macro emits a single .word constant for one of MFC2/CFC2/MTC2/CTC2.
|
|
||||||
*
|
*
|
||||||
* `rd` is the C2 register index in the file the sub-opcode names:
|
* `rd` is the C2 register index in the file the sub-opcode names:
|
||||||
* gte_mv_from_data_r / gte_mv_to_data_r → C2 data register file
|
* gte_mv_from_data_r / gte_mv_to_data_r → C2 data register file
|
||||||
@@ -316,14 +310,14 @@ enum { _C2_TX_SUBS_ = 0
|
|||||||
#define gte_mv_from_ctrl_r(rt, rd) enc_gte_tx(sub_cfc2, (rt), (rd)) /* Copy From ctrl reg */
|
#define gte_mv_from_ctrl_r(rt, rd) enc_gte_tx(sub_cfc2, (rt), (rd)) /* Copy From ctrl reg */
|
||||||
#define gte_mv_to_data_r(rt, rd) enc_gte_tx(sub_mtc2, (rt), (rd)) /* Move To data reg */
|
#define gte_mv_to_data_r(rt, rd) enc_gte_tx(sub_mtc2, (rt), (rd)) /* Move To data reg */
|
||||||
#define gte_mv_to_ctrl_r(rt, rd) enc_gte_tx(sub_ctc2, (rt), (rd)) /* Copy To ctrl reg */
|
#define gte_mv_to_ctrl_r(rt, rd) enc_gte_tx(sub_ctc2, (rt), (rd)) /* Copy To ctrl reg */
|
||||||
|
#define DmaSlot_ // Annotate an instruction as filling a CPU <-> GTE DMA delay slot/s
|
||||||
|
|
||||||
/* COP2 Data Load (lwc2): `lwc2 rt, off(rs)`
|
/* COP2 Data Load (lwc2): `lwc2 rt, off(rs)`
|
||||||
* Layout: [op_lwc2:6][rs:5][rt:5][imm:16]
|
* Layout: [op_lwc2:6][rs:5][rt:5][imm:16]
|
||||||
* - rs: GPR base address
|
* - rs: GPR base address
|
||||||
* - rt: COP2 data register index (0..31)
|
* - rt: COP2 data register index (0..31)
|
||||||
* - imm: signed 16-bit offset
|
* - imm: signed 16-bit offset
|
||||||
* NOTE: When `rs` is a runtime register, the encoding cannot be pre-baked
|
* NOTE: When `rs` is a runtime register, the encoding cannot be pre-baked into a .word — use the string-style `gte_load_v0` macro below instead. */
|
||||||
* into a .word — use the string-style `gte_load_v0` macro below instead. */
|
|
||||||
#define enc_gte_lw(rt, base, off) enc_i(op_lwc2, (base), (rt), (off))
|
#define enc_gte_lw(rt, base, off) enc_i(op_lwc2, (base), (rt), (off))
|
||||||
/* Store Word */
|
/* Store Word */
|
||||||
#define enc_gte_sw(rt, base, off) enc_i(op_swc2, (base), (rt), (off))
|
#define enc_gte_sw(rt, base, off) enc_i(op_swc2, (base), (rt), (off))
|
||||||
@@ -332,8 +326,7 @@ enum { _C2_TX_SUBS_ = 0
|
|||||||
* `swc2` is redundant when we're already inside the `gte_` namespace.
|
* `swc2` is redundant when we're already inside the `gte_` namespace.
|
||||||
* gte_lw rt, base, off → lwc2 rt, off(base)
|
* gte_lw rt, base, off → lwc2 rt, off(base)
|
||||||
* gte_sw rt, base, off → swc2 rt, off(base)
|
* gte_sw rt, base, off → swc2 rt, off(base)
|
||||||
* For the typical user-facing vector-level load (xy + z as two instructions),
|
* For the typical user-facing vector-level load (xy + z as two instructions), use the higher-level `gte_load_vN` macros below. */
|
||||||
* use the higher-level `gte_load_vN` macros below. */
|
|
||||||
#define gte_lw(rt, base, off) enc_gte_lw(rt, base, off)
|
#define gte_lw(rt, base, off) enc_gte_lw(rt, base, off)
|
||||||
#define gte_sw(rt, base, off) enc_gte_sw(rt, base, off)
|
#define gte_sw(rt, base, off) enc_gte_sw(rt, base, off)
|
||||||
|
|
||||||
@@ -408,10 +401,11 @@ enum { _C2_TX_SUBS_ = 0
|
|||||||
#define gte_cmdw_rtpt (gte_cmd_base | enc_gte_cmd(gte_cmd_rtpt ) | gte_cmdw_psyq_compat)
|
#define gte_cmdw_rtpt (gte_cmd_base | enc_gte_cmd(gte_cmd_rtpt ) | gte_cmdw_psyq_compat)
|
||||||
#define gte_cmdw_nclip (gte_cmd_base | enc_gte_cmd(gte_cmd_nclip))
|
#define gte_cmdw_nclip (gte_cmd_base | enc_gte_cmd(gte_cmd_nclip))
|
||||||
#define gte_cmdw_op (gte_cmd_base | enc_gte_cmd(gte_cmd_op ))
|
#define gte_cmdw_op (gte_cmd_base | enc_gte_cmd(gte_cmd_op ))
|
||||||
#define gte_cmdw_outer_product gte_cmdw_op /* "outer product" -- NOCASH/Sdk terminology */
|
#define gte_cmdw_outer_product gte_cmdw_op /* "outer product" -- PSY-Q terminology */
|
||||||
#define gte_cmdw_wedge gte_cmdw_op /* "wedge product" -- geometric-algebra terminology.
|
#define gte_cmdw_wedge gte_cmdw_op /* "wedge product" -- geometric-algebra terminology. */
|
||||||
* RGA(Lengyel): the GTE OP is a 3D signed-16-bit D x IR cross, not a generic RGA exterior product.
|
#define gte_cmdw_cross gte_cmdw_op /* "cross product" -- geometric-algebra terminology.
|
||||||
* The wedge alias is the 3D complement interpretation of the same 3 scalars (MAC1..MAC3). */
|
* RGA(Lengyel): The GTE OP is a 3D signed-16-bit D x IR cross, not a generic RGA exterior product.
|
||||||
|
* The wedge alias is a 3D complement interpretation of the same 3 scalars (MAC1..MAC3). */
|
||||||
#define gte_cmdw_mvmva (gte_cmd_base | enc_gte_cmd(gte_cmd_mvmva))
|
#define gte_cmdw_mvmva (gte_cmd_base | enc_gte_cmd(gte_cmd_mvmva))
|
||||||
|
|
||||||
/* MVMVA with sf=0 (no shift, full-integer), cv=3 (no translation), v=3 (IR vector input).
|
/* MVMVA with sf=0 (no shift, full-integer), cv=3 (no translation), v=3 (IR vector input).
|
||||||
|
|||||||
@@ -101,11 +101,12 @@ 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);
|
||||||
|
|
||||||
typedef U4 const MipsAtom;
|
typedef U4 const MipsAtom; // Underlying type to a mips atom defnition
|
||||||
typedef Slice_(MipsAtom);
|
typedef Slice_(MipsAtom);
|
||||||
// Sometimes a user will define a bundle of atoms that represent a procedure of work as:
|
// Sometimes a user will define a bundle of atoms that represent a procedure of work as:
|
||||||
// MipsAtom* <identifier>[...];
|
// MipsAtom* <identifier>[...];
|
||||||
@@ -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.
|
||||||
@@ -242,6 +245,7 @@ atom_dbg_skip MipsAtomComp_(ac_yield_tail) {
|
|||||||
add_ui_self(R_TapePtr, S_(MipsCode)),
|
add_ui_self(R_TapePtr, S_(MipsCode)),
|
||||||
jump_reg( R_AtomJmp), nop,
|
jump_reg( R_AtomJmp), nop,
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma endregion Macro Atom Components
|
#pragma endregion Macro Atom Components
|
||||||
|
|
||||||
#pragma region Atom Builder
|
#pragma region Atom Builder
|
||||||
@@ -371,13 +375,34 @@ 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);
|
||||||
}
|
}
|
||||||
#pragma endregion RegFileArena (Register File Allocator)
|
#pragma endregion RegFileArena (Register File Allocator)
|
||||||
|
|
||||||
#pragma region Mips Atom Procs
|
#pragma region Mips Atom Procs
|
||||||
|
/* RegUse structs are a convention to organize register allocations for a mips atom procedure.
|
||||||
|
Unlike the usual enum-based declarations, they provide a namespaced scope
|
||||||
|
and have view types via union declarations.
|
||||||
|
*/
|
||||||
|
#define RegUse_(proc_name) (tmpl(RegUse,proc_name))
|
||||||
|
|
||||||
|
typedef Struct_(RegUse_example_atom_proc) {
|
||||||
|
Reg const ro_register; // Scratch base carrier.
|
||||||
|
Reg usual_modifiable;
|
||||||
|
union { Reg view_1, view_2, view_3; } t1;
|
||||||
|
};
|
||||||
|
internal MipsAtom* example_atom_proc(AtomArena_R aa, U2 offset, RegUse_example_atom_proc r)
|
||||||
|
MipsAtom_Proc_(aa, {
|
||||||
|
add_si(r.usual_modifiable, r.ro_register, offset),
|
||||||
|
or_u(r.t1.view_1, r.ro_register, 0),
|
||||||
|
branch_lt_zero(r.t1.view_1, atom_offset(example_atom_proc, skip)), BdSlot_ nop,
|
||||||
|
li_s(r.t1.view_2, 100),
|
||||||
|
atom_label(skip)
|
||||||
|
add_si(r.t1.view_3, r.usual_modifiable, 10),
|
||||||
|
mac_yield(),
|
||||||
|
})
|
||||||
|
|
||||||
#pragma endregion Mips Atom Procs
|
#pragma endregion Mips Atom Procs
|
||||||
|
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
#ifdef INTELLISENSE_DIRECTIVES
|
|
||||||
# include "gen/macs.h"
|
|
||||||
# include "gen/offsets.h"
|
|
||||||
# include "math.h"
|
|
||||||
# include "lottes_tape.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ATOM_FILE_DEBUGGER_LINE_MARKER(math_atom_c);
|
|
||||||
|
|
||||||
#pragma region MACs (Mips Atom Component)
|
|
||||||
|
|
||||||
// FI_ Slice_MipsCode ac_load_imm
|
|
||||||
|
|
||||||
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_y, r_base, offset + O_(V3_S2,y)),
|
|
||||||
})
|
|
||||||
|
|
||||||
FI_ Slice_MipsCode ac_store_v2s2(AtomBuilder_R ab, U4 rt_x, U4 rt_y, U4 base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
|
||||||
store_half(rt_x, base, offset + O_(V2_S2,x)),
|
|
||||||
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, {
|
|
||||||
load_word( rs_x, r_base, offset + O_(V3_S4,x)),
|
|
||||||
load_word( rs_y, r_base, offset + O_(V3_S4,y)),
|
|
||||||
load_word( rs_z, r_base, offset + O_(V3_S4,z)),
|
|
||||||
})
|
|
||||||
// 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, {
|
|
||||||
store_word(rt_x, base, offset + O_(V3_S4,x)),
|
|
||||||
store_word(rt_y, base, offset + O_(V3_S4,y)),
|
|
||||||
store_word(rt_z, base, offset + O_(V3_S4,z)),
|
|
||||||
})
|
|
||||||
// TODO(Ed): we could generate these mappings properly..
|
|
||||||
#define ac_store_p3s4 ac_store_v3s4
|
|
||||||
#define mac_store_p3s4 mac_store_v3s4
|
|
||||||
|
|
||||||
FI_ Slice_MipsCode ac_sub_v3s4(AtomBuilder_R ab, U4 rds_x, U4 rds_y, U4 rds_z, U4 rt_x, U4 rt_y, U4 rt_z) atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
|
||||||
sub_s(rds_x, rds_x, rt_x),
|
|
||||||
sub_s(rds_y, rds_y, rt_y),
|
|
||||||
sub_s(rds_z, rds_z, rt_z),
|
|
||||||
})
|
|
||||||
|
|
||||||
FI_ Slice_MipsCode ac_store_rects2(AtomBuilder_R ab, U4 rt_x, U4 rt_y, U4 rt_width, U4 rt_height, U4 base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
|
||||||
store_half(rt_x, base, offset + O_(Rect_S2,x)),
|
|
||||||
store_half(rt_y, base, offset + O_(Rect_S2,y)),
|
|
||||||
store_half(rt_width, base, offset + O_(Rect_S2,width)),
|
|
||||||
store_half(rt_height, base, offset + O_(Rect_S2,height)),
|
|
||||||
})
|
|
||||||
|
|
||||||
#pragma endregion MACs (Mips Atom Component)
|
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#ifdef INTELLISENSE_DIRECTIVES
|
||||||
|
# include "gen/macs.h"
|
||||||
|
# include "gen/offsets.h"
|
||||||
|
# include "math.h"
|
||||||
|
# include "lottes_tape.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ATOM_FILE_DEBUGGER_LINE_MARKER(math_atom_c);
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
#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, {
|
||||||
|
load_half( rs_x, r_base, offset + O_(V3_S2,x)),
|
||||||
|
load_half( rs_y, r_base, offset + O_(V3_S2,y)),
|
||||||
|
})
|
||||||
|
|
||||||
|
FI_ Slice_MipsCode ac_store_v2s2(AtomBuilder_R ab, U4 rt_x, U4 rt_y, U4 base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
||||||
|
store_half(rt_x, base, offset + O_(V2_S2,x)),
|
||||||
|
store_half(rt_y, base, offset + O_(V2_S2,y)),
|
||||||
|
})
|
||||||
|
|
||||||
|
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(tx, base, offset + OA_(U4,[0])),
|
||||||
|
load_word(ty, base, offset + OA_(U4,[1])),
|
||||||
|
load_word(tz, base, offset + OA_(U4,[2])),
|
||||||
|
})
|
||||||
|
|
||||||
|
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))
|
||||||
|
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))
|
||||||
|
|
||||||
|
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])),
|
||||||
|
})
|
||||||
|
|
||||||
|
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))
|
||||||
|
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)
|
||||||
|
atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
||||||
|
add_si(rt_x, base, O_(V3_S4,x)),
|
||||||
|
add_si(rt_y, base, O_(V3_S4,y)),
|
||||||
|
add_si(rt_z, base, O_(V3_S4,z)),
|
||||||
|
})
|
||||||
|
|
||||||
|
FI_ Slice_MipsCode ac_sub_v3s4(AtomBuilder_R ab, U4 rds_x, U4 rds_y, U4 rds_z, U4 rt_x, U4 rt_y, U4 rt_z) atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
||||||
|
sub_s(rds_x, rds_x, rt_x),
|
||||||
|
sub_s(rds_y, rds_y, rt_y),
|
||||||
|
sub_s(rds_z, rds_z, rt_z),
|
||||||
|
})
|
||||||
|
|
||||||
|
FI_ Slice_MipsCode ac_store_rects2(AtomBuilder_R ab, U4 rt_x, U4 rt_y, U4 rt_width, U4 rt_height, U4 base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
||||||
|
store_half(rt_x, base, offset + O_(Rect_S2,x)),
|
||||||
|
store_half(rt_y, base, offset + O_(Rect_S2,y)),
|
||||||
|
store_half(rt_width, base, offset + O_(Rect_S2,width)),
|
||||||
|
store_half(rt_height, base, offset + O_(Rect_S2,height)),
|
||||||
|
})
|
||||||
|
|
||||||
|
#pragma endregion MACs (Mips Atom Component)
|
||||||
@@ -16,6 +16,32 @@ atom_dbg_skip MipsAtomComp_Proc_(ab, {
|
|||||||
or_i_self( dst, u4_lo(imm)),
|
or_i_self( dst, u4_lo(imm)),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
FI_ Slice_MipsCode ac_shift_aright_v3_self(AtomBuilder_R ab, Reg dt_x, Reg dt_y, Reg dt_z, U2 shift_amount)
|
||||||
|
MipsAtomComp_Proc_( ab, {
|
||||||
|
shift_aright(dt_x, dt_x, shift_amount),
|
||||||
|
shift_aright(dt_y, dt_y, shift_amount),
|
||||||
|
shift_aright(dt_z, dt_z, shift_amount),
|
||||||
|
})
|
||||||
|
|
||||||
|
FI_ Slice_MipsCode ac_shift_aright_var_v3(AtomBuilder_R ab
|
||||||
|
, Reg rd_v0, Reg rd_v1, Reg rd_v2
|
||||||
|
, Reg rs_v0, Reg rs_v1, Reg rs_v2
|
||||||
|
, Reg r_shift)
|
||||||
|
MipsAtomComp_Proc_(ab, {
|
||||||
|
shift_aright_var(rd_v0, rs_v0, r_shift),
|
||||||
|
shift_aright_var(rd_v1, rs_v1, r_shift),
|
||||||
|
shift_aright_var(rd_v2, rs_v2, r_shift),
|
||||||
|
})
|
||||||
|
|
||||||
|
FI_ Slice_MipsCode ac_shift_aright_var_v3_self(AtomBuilder_R ab
|
||||||
|
, Reg rds_v0, Reg rds_v1, Reg rds_v2
|
||||||
|
, Reg r_shift)
|
||||||
|
MipsAtomComp_Proc_(ab, {
|
||||||
|
shift_aright_var(rds_v0, rds_v0, r_shift),
|
||||||
|
shift_aright_var(rds_v1, rds_v1, r_shift),
|
||||||
|
shift_aright_var(rds_v2, rds_v2, r_shift),
|
||||||
|
})
|
||||||
|
|
||||||
#pragma endregion MACs (Mips Atom Components)
|
#pragma endregion MACs (Mips Atom Components)
|
||||||
|
|
||||||
#pragma region Baked Atoms
|
#pragma region Baked Atoms
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ enum {
|
|||||||
atom_offset_end_low_exit_stick = _atom_offset_end_low_exit_stick,
|
atom_offset_end_low_exit_stick = _atom_offset_end_low_exit_stick,
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- atom: pad_input_cam (40 words) ---
|
// --- atom: pad_input_cam (42 words) ---
|
||||||
|
|
||||||
#define _atom_offset_left_x_exit_left_x 3
|
#define _atom_offset_left_x_exit_left_x 3
|
||||||
#define _atom_offset_right_x_exit_right_x 3
|
#define _atom_offset_right_x_exit_right_x 3
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -128,6 +128,11 @@ typedef Struct_(Binds_ResolveLookAtSub) {
|
|||||||
ResolveLookAtScratch* scratchpad;
|
ResolveLookAtScratch* scratchpad;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef Struct_(RegUse_resolve_look_at__input_and_sub_proc) {
|
||||||
|
Reg scratch;
|
||||||
|
Reg target; Reg eye; Reg up_in;
|
||||||
|
Reg t0; Reg t1; Reg t2; Reg t3; Reg t4;
|
||||||
|
};
|
||||||
/* Atom 0 in the bundle: input_and_sub. Stages C-side inputs into the scratchpad and computes fwd = target - eye.
|
/* Atom 0 in the bundle: input_and_sub. Stages C-side inputs into the scratchpad and computes fwd = target - eye.
|
||||||
* Staging work:
|
* Staging work:
|
||||||
* * Stage eye.x/y/z → scratch (for atom 6's translation column)
|
* * Stage eye.x/y/z → scratch (for atom 6's translation column)
|
||||||
@@ -146,66 +151,61 @@ typedef Struct_(Binds_ResolveLookAtSub) {
|
|||||||
* R_V0 : hardcoded (load eye.z / target.z)
|
* R_V0 : hardcoded (load eye.z / target.z)
|
||||||
* 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,
|
internal MipsAtom* resolve_look_at__input_and_sub_proc(AtomArena_R aa, RegUse_resolve_look_at__input_and_sub_proc r)
|
||||||
// TODO(Ed): We can resolve scratch at anytime its fixed to a specific address.
|
atom_info(atom_bind(Binds_ResolveLookAtSub)) MipsAtom_Proc_(aa, {
|
||||||
U4 r_scratch
|
load_word(r.target, R_TapePtr, O_(Binds_ResolveLookAtSub,target)),
|
||||||
, U4 r_target_ptr,U4 r_eye_ptr, U4 r_up_in_ptr
|
load_word(r.eye, R_TapePtr, O_(Binds_ResolveLookAtSub,eye)),
|
||||||
, U4 r_tmp0, U4 r_tmp1, U4 r_tmp2, U4 r_tmp3
|
load_word(r.up_in, R_TapePtr, O_(Binds_ResolveLookAtSub,up_in)),
|
||||||
) MipsAtom_Proc_(aa, {
|
load_word(r.scratch, R_TapePtr, O_(Binds_ResolveLookAtSub,scratchpad)),
|
||||||
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)),
|
|
||||||
load_word(r_scratch, R_TapePtr, O_(Binds_ResolveLookAtSub,scratchpad)),
|
|
||||||
add_ui_self( R_TapePtr, S_(Binds_ResolveLookAtSub)),
|
add_ui_self( R_TapePtr, S_(Binds_ResolveLookAtSub)),
|
||||||
|
|
||||||
// Stage eye.x/y/z into the scratchpad (atom 6 reads these for the translation column).
|
|
||||||
mac_load_p3s4( r_tmp0, r_tmp1, r_tmp2, r_eye_ptr, 0),
|
|
||||||
mac_store_p3s4(r_tmp0, r_tmp1, r_tmp2, r_scratch, O_(ResolveLookAtScratch,eye)),
|
|
||||||
|
|
||||||
/* Stage up_in.x/y/z into the scratchpad. */
|
/* Stage up_in.x/y/z into the scratchpad. */
|
||||||
mac_load_p3s4( r_tmp0, r_tmp1, r_tmp2, r_up_in_ptr, 0),
|
mac_load_word_v3( r.t0, r.t1, r.t2, r.up_in, 0),
|
||||||
mac_store_p3s4(r_tmp0, r_tmp1, r_tmp2, 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).
|
||||||
|
mac_load_word_v3( r.t0, r.t1, r.t2, r.eye, 0),
|
||||||
|
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(r_tmp0, r_tmp1, r_tmp2, r_target_ptr, 0),
|
// mac_load_p3s4(t3, R_AT, t4, r.eye, 0),
|
||||||
mac_load_p3s4(r_tmp3, R_AT, R_V0, r_eye_ptr, 0),
|
mac_load_word_v3(r.t3, R_AT, r.t4, r.target, 0),
|
||||||
mac_sub_v3s4(
|
mac_sub_v3s4(
|
||||||
r_tmp0, r_tmp1, r_tmp2,
|
r.t3, R_AT, r.t4,
|
||||||
r_tmp3, R_AT, R_V0),
|
r.t0, r.t1, r.t2),
|
||||||
mac_store_v3s4(r_tmp0, r_tmp1, r_tmp2, r_scratch, O_(ResolveLookAtScratch,fwd)),
|
mac_store_word_v3(r.t3, R_AT, r.t4, r.scratch, O_(ResolveLookAtScratch,fwd)),
|
||||||
|
|
||||||
mac_yield()
|
mac_yield()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
typedef Struct_(RegUse_resolve_look_at__cross_uz_up_into_right_proc) {
|
||||||
|
Reg scratch;
|
||||||
|
Reg a; Reg b; Reg c; /* load a.x/y/z; result out.x/y/z */
|
||||||
|
Reg d; /* load b.x */
|
||||||
|
Reg f; /* r_f = &right (out ptr), r_g = &uz, r_h = &up_in */
|
||||||
|
|
||||||
|
union { Reg t1, g, target0; };
|
||||||
|
union { Reg t2, h, target1; };
|
||||||
|
Reg t0;
|
||||||
|
};
|
||||||
/* Atom 2: cross uz × up_in → right. */
|
/* Atom 2: cross uz × up_in → right. */
|
||||||
internal MipsAtom* resolve_look_at__cross_uz_up_in_to_right_proc(AtomArena_R aa, U4 r_scratch
|
internal MipsAtom* resolve_look_at__cross_uz_up_into_right_proc(AtomArena_R aa,
|
||||||
, U4 r_a, U4 r_b, U4 r_c /* load a.x/y/z; result out.x/y/z */
|
RegUse_resolve_look_at__cross_uz_up_into_right_proc r
|
||||||
, U4 r_d /* load b.x */
|
|
||||||
, U4 r_f, U4 r_g, U4 r_h /* r_f = &right (out ptr), r_g = &uz, r_h = &up_in */
|
|
||||||
) MipsAtom_Proc_(aa, {
|
) MipsAtom_Proc_(aa, {
|
||||||
/* FIX: build packed RT22+RT33 with proper sign extension. */
|
/* FIX: build packed RT22+RT33 with proper sign extension. */
|
||||||
add_si(r_g, r_scratch, O_(ResolveLookAtScratch,uz)), /* r_g = &uz */
|
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.h, r.scratch, O_(ResolveLookAtScratch,up_in)), /* r_h = &up_in */
|
||||||
add_si(r_f, r_scratch, O_(ResolveLookAtScratch,right)), /* r_f = &right (out) */
|
add_si(r.f, r.scratch, O_(ResolveLookAtScratch,right)), /* r_f = &right (out) */
|
||||||
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. */
|
||||||
load_word(r_a, r_g, O_(V3_S4,x)),
|
mac_load_word_v3(r.a, r.b, r.c, r.g, 0),
|
||||||
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 (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). */
|
||||||
load_word(r_d, r_h, O_(V3_S4,x)),
|
mac_load_word_v3(r.d, R_AT, r.t0, r.h, 0), LdSlot_ // (taken by gte_mv_from_ctrl_r)
|
||||||
load_word(R_AT, r_h, O_(V3_S4,y)),
|
|
||||||
load_word(R_V0, r_h, O_(V3_S4,z)),
|
|
||||||
nop,
|
|
||||||
|
|
||||||
/* 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_g, gte_cr_RT11), /* r_g = C2 r0 (RT11|RT12) */
|
|
||||||
gte_mv_from_ctrl_r(r_h, gte_cr_RT22), /* r_h = C2 r4 (RT22|RT33) */
|
|
||||||
|
|
||||||
|
/* 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.target1, gte_cr_RT22), /* r_h = C2 r4 (RT22|RT33) */
|
||||||
/* Load uz.x/uz.y/uz.z into COP2 control registers.
|
/* Load uz.x/uz.y/uz.z into COP2 control registers.
|
||||||
* OP reads D1 = RT11 from $0.low, D2 = RT22 from $2.high, D3 = RT33 from $4.high.
|
* OP reads D1 = RT11 from $0.low, D2 = RT22 from $2.high, D3 = RT33 from $4.high.
|
||||||
* RT22 is in BOTH $2.high AND $4.low (shared bit position). OP reads from $2.high.
|
* RT22 is in BOTH $2.high AND $4.low (shared bit position). OP reads from $2.high.
|
||||||
@@ -215,20 +215,20 @@ internal MipsAtom* resolve_look_at__cross_uz_up_in_to_right_proc(AtomArena_R aa,
|
|||||||
* The 2nd ctc2 DOES clobber $4.low (becomes a.z.low, NOT a.y.high), but since OP
|
* The 2nd ctc2 DOES clobber $4.low (becomes a.z.low, NOT a.y.high), but since OP
|
||||||
* reads RT22 from $2.high (which the 2nd ctc2 doesn't touch), D2 is still a.y.high.
|
* reads RT22 from $2.high (which the 2nd ctc2 doesn't touch), D2 is still a.y.high.
|
||||||
* This is libpsyx's OuterProduct12 convention EXACTLY. */
|
* This is libpsyx's OuterProduct12 convention EXACTLY. */
|
||||||
gte_mv_to_ctrl_r(r_b, gte_cr_RT13), /* $2 = r_b = a.y. RT13=a.y.low, RT22=a.y.high. */
|
gte_mv_to_ctrl_r(r.b, gte_cr_RT13), /* $2 = r_b = a.y. RT13=a.y.low, RT22=a.y.high. */
|
||||||
gte_mv_to_ctrl_r(r_c, gte_cr_RT22), /* $4 = r_c = a.z. RT22=a.z.low, RT33=a.z.high. */
|
gte_mv_to_ctrl_r(r.c, gte_cr_RT22), /* $4 = r_c = a.z. RT22=a.z.low, RT33=a.z.high. */
|
||||||
|
|
||||||
/* Load uz into the RT diagonal. */
|
/* Load uz into the RT diagonal. */
|
||||||
gte_mv_to_ctrl_r(r_a, gte_cr_RT11), /* D1 = RT11 = uz.x (low 16 of $0, sign-extended by OP). */
|
gte_mv_to_ctrl_r(r.a, gte_cr_RT11), /* D1 = RT11 = uz.x (low 16 of $0, sign-extended by OP). */
|
||||||
nop2, /* CTC2 retirement (CPU→COP2 2-slot delay) */
|
DmaSlot_ nop2, /* CTC2 retirement (CPU→COP2 2-slot delay) */
|
||||||
|
|
||||||
/* Load up_in into IR (the second operand for OP). */
|
/* Load up_in into IR (the second operand for OP). */
|
||||||
gte_mv_to_data_r(r_d, C2_IR1), /* IR1 = up_in.x */
|
gte_mv_to_data_r(r.d, C2_IR1), /* IR1 = up_in.x */
|
||||||
gte_mv_to_data_r(R_AT, C2_IR2), /* IR2 = up_in.y */
|
gte_mv_to_data_r(R_AT, C2_IR2), /* IR2 = up_in.y */
|
||||||
gte_mv_to_data_r(R_V0, C2_IR3), /* IR3 = up_in.z */
|
gte_mv_to_data_r(r.t0, C2_IR3), /* IR3 = up_in.z */
|
||||||
nop2, /* MTC2 retirement (CPU→COP2 2-slot delay) */
|
DmaSlot_ nop2, /* MTC2 retirement (CPU→COP2 2-slot delay) */
|
||||||
|
|
||||||
gte_cmdw_outer_product, /* OP: MAC1/2/3 = uz × up_in
|
gte_cmdw_cross, /* 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
|
||||||
@@ -238,25 +238,18 @@ internal MipsAtom* resolve_look_at__cross_uz_up_in_to_right_proc(AtomArena_R aa,
|
|||||||
* MAC3 = (-fp_one)*uz.x - 0 = -fp_one*uz.x */
|
* MAC3 = (-fp_one)*uz.x - 0 = -fp_one*uz.x */
|
||||||
|
|
||||||
/* Restore the RT slots we clobbered. */
|
/* Restore the RT slots we clobbered. */
|
||||||
gte_mv_to_ctrl_r(r_g, gte_cr_RT11), /* restore C2 r0 (RT11|RT12) */
|
gte_mv_to_ctrl_r(r.target0, gte_cr_RT11), /* restore C2 r0 (RT11|RT12) */
|
||||||
gte_mv_to_ctrl_r(r_h, gte_cr_RT22), /* restore C2 r4 (RT22|RT33) */
|
gte_mv_to_ctrl_r(r.target1, gte_cr_RT22), /* restore C2 r4 (RT22|RT33) */
|
||||||
|
|
||||||
/* mfc2 MAC1/2/3 → r_a/r_b/r_c (out.x/y/z). */
|
/* mfc2 MAC1/2/3 → r_a/r_b/r_c (out.x/y/z). */
|
||||||
gte_mv_from_data_r(r_a, C2_MAC1),
|
mac_gte_mv_from_data_r_mac123(r.a, r.b, r.c),
|
||||||
gte_mv_from_data_r(r_b, C2_MAC2),
|
DmaSlot_ nop, /* MFC2 retirement */
|
||||||
gte_mv_from_data_r(r_c, C2_MAC3),
|
|
||||||
nop, /* MFC2 retirement */
|
|
||||||
|
|
||||||
/* 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. */
|
||||||
shift_aright(r_a, r_a, 12),
|
mac_shift_aright_v3_self(r.a, r.b, r.c, 12),
|
||||||
shift_aright(r_b, r_b, 12),
|
|
||||||
shift_aright(r_c, 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). */
|
||||||
store_word(r_a, r_f, O_(V3_S4,x)),
|
mac_store_word_v3(r.a, r.b, r.c, r.f, 0),
|
||||||
store_word(r_b, r_f, O_(V3_S4,y)),
|
|
||||||
store_word(r_c, r_f, O_(V3_S4,z)),
|
|
||||||
|
|
||||||
mac_yield()
|
mac_yield()
|
||||||
})
|
})
|
||||||
@@ -774,34 +767,34 @@ internal MipsAtom_(pad_input_cam) atom_info(atom_bind(Binds_PadInputCam)
|
|||||||
load_word(R_T1, R_Cam, O_(Camera,pos.x)), // BD-Slot.
|
load_word(R_T1, R_Cam, O_(Camera,pos.x)), // BD-Slot.
|
||||||
|
|
||||||
// D-pad Left → cam.pos.x -= 50. and_i fulfills BD-slot for load on R_Cam.
|
// D-pad Left → cam.pos.x -= 50. and_i fulfills BD-slot for load on R_Cam.
|
||||||
and_i(R_T3, R_T0, Pad_Left), branch_le_zero(R_T3, atom_offset(left_x, exit_left_x)), mac_yield_load(),
|
LdSlot_ and_i(R_T3, R_T0, Pad_Left), branch_le_zero(R_T3, atom_offset(left_x, exit_left_x)), BdSlot_ mac_yield_load(),
|
||||||
add_si(R_T1, R_T1, -50), store_word(R_T1, R_Cam, O_(Camera,pos.x)),
|
add_si(R_T1, R_T1, -50), store_word(R_T1, R_Cam, O_(Camera,pos.x)),
|
||||||
atom_label(exit_left_x)
|
atom_label(exit_left_x)
|
||||||
|
|
||||||
/* D-pad Right → cam.pos.x += 50. Reuses R_T1 from Left. */
|
/* D-pad Right → cam.pos.x += 50. Reuses R_T1 from Left. */
|
||||||
and_i(R_T3, R_T0, Pad_Right), branch_le_zero(R_T3, atom_offset(right_x, exit_right_x)), nop,
|
and_i(R_T3, R_T0, Pad_Right), branch_le_zero(R_T3, atom_offset(right_x, exit_right_x)), BdSlot_ nop,
|
||||||
add_si(R_T1, R_T1, 50), store_word(R_T1, R_Cam, O_(Camera,pos.x)),
|
add_si(R_T1, R_T1, 50), store_word(R_T1, R_Cam, O_(Camera,pos.x)),
|
||||||
atom_label(exit_right_x)
|
atom_label(exit_right_x)
|
||||||
|
|
||||||
/* D-pad Up → cam.pos.y -= 50. Load pos.y BEFORE the andi. */
|
/* D-pad Up → cam.pos.y -= 50. Load pos.y BEFORE the andi. */
|
||||||
load_word(R_T1, R_Cam, O_(Camera,pos.y)),
|
load_word(R_T1, R_Cam, O_(Camera,pos.y)), LdSlot_
|
||||||
and_i(R_T3, R_T0, Pad_Up), branch_le_zero(R_T3, atom_offset(up_y, exit_up_y)), nop,
|
and_i(R_T3, R_T0, Pad_Up), branch_le_zero(R_T3, atom_offset(up_y, exit_up_y)), BdSlot_ nop,
|
||||||
add_si(R_T1, R_T1, -50), store_word(R_T1, R_Cam, O_(Camera,pos.y)),
|
add_si(R_T1, R_T1, -50), store_word(R_T1, R_Cam, O_(Camera,pos.y)),
|
||||||
atom_label(exit_up_y)
|
atom_label(exit_up_y)
|
||||||
|
|
||||||
/* D-pad Down → cam.pos.y += 50. Reuses R_T1 from Up. */
|
/* D-pad Down → cam.pos.y += 50. Reuses R_T1 from Up. */
|
||||||
and_i(R_T3, R_T0, Pad_Down), branch_le_zero(R_T3, atom_offset(down_y, exit_down_y)), nop,
|
and_i(R_T3, R_T0, Pad_Down), branch_le_zero(R_T3, atom_offset(down_y, exit_down_y)), BdSlot_ nop,
|
||||||
add_si(R_T1, R_T1, 50), store_word(R_T1, R_Cam, O_(Camera,pos.y)),
|
add_si(R_T1, R_T1, 50), store_word(R_T1, R_Cam, O_(Camera,pos.y)),
|
||||||
atom_label(exit_down_y)
|
atom_label(exit_down_y)
|
||||||
|
|
||||||
/* D-pad Cross → cam.pos.z -= 50. Load pos.z BEFORE the andi. */
|
/* D-pad Cross → cam.pos.z -= 50. Load pos.z BEFORE the andi. */
|
||||||
load_word(R_T1, R_Cam, O_(Camera,pos.z)),
|
load_word(R_T1, R_Cam, O_(Camera,pos.z)), LdSlot_
|
||||||
and_i(R_T3, R_T0, Pad_Cross), branch_le_zero(R_T3, atom_offset(cross_z, exit_cross_z)), nop,
|
and_i(R_T3, R_T0, Pad_Cross), branch_le_zero(R_T3, atom_offset(cross_z, exit_cross_z)), BdSlot_ nop,
|
||||||
add_si(R_T1, R_T1, -50), store_word(R_T1, R_Cam, O_(Camera,pos.z)),
|
add_si(R_T1, R_T1, -50), store_word(R_T1, R_Cam, O_(Camera,pos.z)),
|
||||||
atom_label(exit_cross_z)
|
atom_label(exit_cross_z)
|
||||||
|
|
||||||
/* D-pad Circle → cam.pos.z += 50. Reuses R_T1 from Cross. */
|
/* D-pad Circle → cam.pos.z += 50. Reuses R_T1 from Cross. */
|
||||||
and_i(R_T3, R_T0, Pad_Circle), branch_le_zero(R_T3, atom_offset(circle_z, exit_circle_z)), nop,
|
and_i(R_T3, R_T0, Pad_Circle), branch_le_zero(R_T3, atom_offset(circle_z, exit_circle_z)), BdSlot_ nop,
|
||||||
add_si(R_T1, R_T1, 50), store_word(R_T1, R_Cam, O_(Camera,pos.z)),
|
add_si(R_T1, R_T1, 50), store_word(R_T1, R_Cam, O_(Camera,pos.z)),
|
||||||
atom_label(exit_circle_z)
|
atom_label(exit_circle_z)
|
||||||
|
|
||||||
|
|||||||
@@ -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,18 +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)
|
||||||
|
|
||||||
U4 r_target_ptr = regfile_alloc(& rf);
|
|
||||||
U4 r_eye_ptr = regfile_alloc(& rf);
|
|
||||||
U4 r_up_in_ptr = regfile_alloc(& rf);
|
|
||||||
U4 r_tmp0 = regfile_alloc(& rf);
|
|
||||||
U4 r_tmp1 = regfile_alloc(& rf);
|
|
||||||
U4 r_tmp2 = regfile_alloc(& rf);
|
|
||||||
U4 r_tmp3 = 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,
|
||||||
R_ResolveScratch,
|
RegUse_(resolve_look_at__input_and_sub_proc) {
|
||||||
r_target_ptr, r_eye_ptr, r_up_in_ptr,
|
.scratch = R_ResolveScratch,
|
||||||
r_tmp0, r_tmp1, r_tmp2, r_tmp3);
|
.target = ralloc(),
|
||||||
|
.eye = ralloc(),
|
||||||
|
.up_in = ralloc(),
|
||||||
|
.t0 = ralloc(),
|
||||||
|
.t1 = ralloc(),
|
||||||
|
.t2 = ralloc(),
|
||||||
|
.t3 = ralloc(),
|
||||||
|
.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);
|
||||||
@@ -168,28 +172,31 @@ 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 === */
|
||||||
U4 r_a_2 = R_T0;
|
smem.resolve_look_at_atom_addrs[2] = resolve_look_at__cross_uz_up_into_right_proc(& ab,
|
||||||
U4 r_b_2 = R_T1;
|
RegUse_(resolve_look_at__cross_uz_up_into_right_proc) {
|
||||||
U4 r_c_2 = R_T2;
|
.scratch = R_ResolveScratch,
|
||||||
U4 r_d_2 = R_T3;
|
.a = ralloc(),
|
||||||
U4 r_f_2 = R_T5; /* out ptr (HARDCODED in body: scratch+32) */
|
.b = ralloc(),
|
||||||
U4 r_g_2 = R_T6; /* a ptr = scratch+16 */
|
.c = ralloc(),
|
||||||
U4 r_h_2 = R_T7; /* b ptr = scratch+128 */
|
.d = ralloc(),
|
||||||
smem.resolve_look_at_atom_addrs[2] = resolve_look_at__cross_uz_up_in_to_right_proc(& ab,
|
.f = ralloc(),
|
||||||
R_ResolveScratch,
|
.t1 = ralloc(),
|
||||||
r_a_2, r_b_2, r_c_2, r_d_2, r_f_2, r_g_2, r_h_2);
|
.t2 = ralloc(),
|
||||||
|
.t0 = ralloc(),
|
||||||
|
});
|
||||||
|
|
||||||
/* === ATOM 3: normalize right→ux === */
|
/* === ATOM 3: normalize right→ux === */
|
||||||
src_offset = O_(ResolveLookAtScratch, right);
|
src_offset = O_(ResolveLookAtScratch, right);
|
||||||
@@ -283,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().
|
||||||
|
|||||||
+96
-7
@@ -2236,6 +2236,43 @@ local function _project_emission_inner(root_body_entry, ctx_table)
|
|||||||
local invocation_stack = {} -- stack of currently-open invocation records
|
local invocation_stack = {} -- stack of currently-open invocation records
|
||||||
local next_inv_id = 0
|
local next_inv_id = 0
|
||||||
|
|
||||||
|
local reg_use_schema = ctx_table.reg_use_schema
|
||||||
|
local reg_use_param = ctx_table.reg_use_param
|
||||||
|
local atom_name = ctx_table.atom_name
|
||||||
|
|
||||||
|
local slot_readonly = {}
|
||||||
|
if reg_use_schema then
|
||||||
|
for _, slot in ipairs(reg_use_schema.slots or {}) do
|
||||||
|
slot_readonly[slot.name] = slot.readonly == true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function apply_sub(sub_map, operand)
|
||||||
|
if not (sub_map and type(operand) == "string") then return operand end
|
||||||
|
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
|
||||||
|
return operand
|
||||||
|
end
|
||||||
|
|
||||||
|
local function resolve_gpr_key(operand)
|
||||||
|
if type(operand) ~= "string" then return nil end
|
||||||
|
if operand:sub(1, 2) == "R_" then return operand end
|
||||||
|
if not (reg_use_schema and reg_use_param) then return nil end
|
||||||
|
local prefix = reg_use_param .. "."
|
||||||
|
if operand:sub(1, #prefix) ~= prefix then return nil end
|
||||||
|
local member_path = operand:sub(#prefix + 1)
|
||||||
|
local slot = reg_use_schema.alias_to_slot[member_path]
|
||||||
|
if not slot then return nil, member_path end
|
||||||
|
return "reguse:" .. atom_name .. ":" .. slot, nil, slot
|
||||||
|
end
|
||||||
|
|
||||||
local function open_invocation_ids_snapshot()
|
local function open_invocation_ids_snapshot()
|
||||||
local ids = {}
|
local ids = {}
|
||||||
for _, inv in ipairs(invocation_stack) do
|
for _, inv in ipairs(invocation_stack) do
|
||||||
@@ -2246,7 +2283,7 @@ local function _project_emission_inner(root_body_entry, ctx_table)
|
|||||||
|
|
||||||
local function emit_word(encoder, args, line, word_call_text,
|
local function emit_word(encoder, args, line, word_call_text,
|
||||||
def_source_now, def_line_now,
|
def_source_now, def_line_now,
|
||||||
immediate_call_text, root_call_text_w)
|
immediate_call_text, root_call_text_w, sub_map)
|
||||||
local inv_ids = open_invocation_ids_snapshot()
|
local inv_ids = open_invocation_ids_snapshot()
|
||||||
local outermost = inv_ids[1] or 0
|
local outermost = inv_ids[1] or 0
|
||||||
-- For words emitted at the root atom body, `immediate_call_text` is nil and the walker's `word_call_text` (the word's own token, e.g. "nop") becomes the effective call_text.
|
-- For words emitted at the root atom body, `immediate_call_text` is nil and the walker's `word_call_text` (the word's own token, e.g. "nop") becomes the effective call_text.
|
||||||
@@ -2254,6 +2291,42 @@ local function _project_emission_inner(root_body_entry, ctx_table)
|
|||||||
-- The call that triggered the body expansion we're currently walking.
|
-- The call that triggered the body expansion we're currently walking.
|
||||||
local eff_call_text = immediate_call_text or word_call_text
|
local eff_call_text = immediate_call_text or word_call_text
|
||||||
local eff_root_call_text = root_call_text_w
|
local eff_root_call_text = root_call_text_w
|
||||||
|
local gpr_keys = nil
|
||||||
|
if reg_use_schema or sub_map then
|
||||||
|
gpr_keys = {}
|
||||||
|
for pos, arg in ipairs(args or {}) do
|
||||||
|
local effective = apply_sub(sub_map, arg)
|
||||||
|
local key, unresolved, slot = resolve_gpr_key(effective)
|
||||||
|
gpr_keys[pos] = key
|
||||||
|
if unresolved then
|
||||||
|
errors[#errors + 1] = {
|
||||||
|
kind = "reguse_unresolved",
|
||||||
|
line = line,
|
||||||
|
msg = string.format("RegUse operand %q does not resolve in schema %q",
|
||||||
|
effective, (reg_use_schema and reg_use_schema.name) or "?"),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
if key and slot and slot_readonly[slot] then
|
||||||
|
local effects = M.INSTRUCTION_GPR_EFFECTS or {}
|
||||||
|
local row = effects[encoder]
|
||||||
|
if row and row.writes then
|
||||||
|
for _, wpos in ipairs(row.writes) do
|
||||||
|
if wpos == pos then
|
||||||
|
errors[#errors + 1] = {
|
||||||
|
kind = "reguse_const_write",
|
||||||
|
line = line,
|
||||||
|
msg = string.format("RegUse slot %q is Reg const; %s writes it",
|
||||||
|
slot, encoder),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not reg_use_schema then
|
||||||
|
gpr_keys = nil
|
||||||
|
end
|
||||||
items[#items + 1] = {
|
items[#items + 1] = {
|
||||||
kind = "word",
|
kind = "word",
|
||||||
encoder = encoder,
|
encoder = encoder,
|
||||||
@@ -2265,6 +2338,7 @@ local function _project_emission_inner(root_body_entry, ctx_table)
|
|||||||
root_call_text = eff_root_call_text,
|
root_call_text = eff_root_call_text,
|
||||||
invocation_ids = inv_ids,
|
invocation_ids = inv_ids,
|
||||||
outermost_invocation_id = outermost,
|
outermost_invocation_id = outermost,
|
||||||
|
gpr_keys = gpr_keys,
|
||||||
}
|
}
|
||||||
word_events[#word_events + 1] = {
|
word_events[#word_events + 1] = {
|
||||||
i = word_idx,
|
i = word_idx,
|
||||||
@@ -2277,6 +2351,7 @@ local function _project_emission_inner(root_body_entry, ctx_table)
|
|||||||
invocation_ids = inv_ids,
|
invocation_ids = inv_ids,
|
||||||
outermost_invocation_id = outermost,
|
outermost_invocation_id = outermost,
|
||||||
word_count = 1,
|
word_count = 1,
|
||||||
|
gpr_keys = gpr_keys,
|
||||||
}
|
}
|
||||||
word_idx = word_idx + 1
|
word_idx = word_idx + 1
|
||||||
end
|
end
|
||||||
@@ -2520,6 +2595,7 @@ local function _project_emission_inner(root_body_entry, ctx_table)
|
|||||||
local line_of = body_entry.line_of or M.LineIndex("")
|
local line_of = body_entry.line_of or M.LineIndex("")
|
||||||
local def_source = body_entry.source or ""
|
local def_source = body_entry.source or ""
|
||||||
local def_line = body_entry.declaration or 0
|
local def_line = body_entry.declaration or 0
|
||||||
|
local sub_map = body_entry.sub_map
|
||||||
-- Per-token dispatch: each matched branch returns; only the fall-through
|
-- Per-token dispatch: each matched branch returns; only the fall-through
|
||||||
-- "opaque word" emit handles direct encoders + mac_X-without-component.
|
-- "opaque word" emit handles direct encoders + mac_X-without-component.
|
||||||
local function process_token(bt)
|
local function process_token(bt)
|
||||||
@@ -2575,12 +2651,22 @@ local function _project_emission_inner(root_body_entry, ctx_table)
|
|||||||
-- Propagate trackers into the recursive walk:
|
-- Propagate trackers into the recursive walk:
|
||||||
-- immediate_call_text = this call's tok (the IMMEDIATE outer call for words emitted in this body)
|
-- immediate_call_text = this call's tok (the IMMEDIATE outer call for words emitted in this body)
|
||||||
-- root_call_text = the OUTERMOST call (immutable across the recursion)
|
-- root_call_text = the OUTERMOST call (immutable across the recursion)
|
||||||
|
local formal_names = ctx_table.component_index[bare]
|
||||||
|
and ctx_table.component_index[bare].arg_names
|
||||||
|
local child_map = nil
|
||||||
|
if formal_names then
|
||||||
|
child_map = {}
|
||||||
|
for i, fname in ipairs(formal_names) do
|
||||||
|
child_map[fname] = apply_sub(sub_map, args[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
walk_body_entry({
|
walk_body_entry({
|
||||||
body_tokens = comp.body_tokens or {},
|
body_tokens = comp.body_tokens or {},
|
||||||
body_off = comp.body_off or 0,
|
body_off = comp.body_off or 0,
|
||||||
line_of = comp.line_of,
|
line_of = comp.line_of,
|
||||||
source = comp.source,
|
source = comp.source,
|
||||||
declaration = comp.declaration,
|
declaration = comp.declaration,
|
||||||
|
sub_map = child_map,
|
||||||
},
|
},
|
||||||
inv.id,
|
inv.id,
|
||||||
invocation_root_call_text,
|
invocation_root_call_text,
|
||||||
@@ -2618,7 +2704,7 @@ local function _project_emission_inner(root_body_entry, ctx_table)
|
|||||||
local n = resolve_count(ident, tok_line)
|
local n = resolve_count(ident, tok_line)
|
||||||
local out_ident = (ident == "nop2") and "nop" or ident
|
local out_ident = (ident == "nop2") and "nop" or ident
|
||||||
for _ = 1, n do
|
for _ = 1, n do
|
||||||
emit_word(out_ident, args, tok_line, tok, def_source, def_line, walk_immediate_call_text, walk_root_call_text)
|
emit_word(out_ident, args, tok_line, tok, def_source, def_line, walk_immediate_call_text, walk_root_call_text, sub_map)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2683,7 +2769,7 @@ end
|
|||||||
--- @param components table -- bare-name → component definition (corpus.components); REQUIRED — consumed at the invocation-construction site to stamp
|
--- @param components table -- bare-name → component definition (corpus.components); REQUIRED — consumed at the invocation-construction site to stamp
|
||||||
--- `invocation.debug_skip`. A missing or non-table `components` raises a fail-loud error rather than silently falling back.
|
--- `invocation.debug_skip`. A missing or non-table `components` raises a fail-loud error rather than silently falling back.
|
||||||
--- @return EmissionProjection
|
--- @return EmissionProjection
|
||||||
function M.project_emission(body_text, component_index, word_counts, components)
|
function M.project_emission(body_text, component_index, word_counts, components, reg_use_ctx)
|
||||||
-- The recursive walk delegates to `_project_emission_inner` so component bodies (which arrive as
|
-- The recursive walk delegates to `_project_emission_inner` so component bodies (which arrive as
|
||||||
-- `{body_tokens, body_off, line_of, source, declaration}` records from `corpus.component_body_index`)
|
-- `{body_tokens, body_off, line_of, source, declaration}` records from `corpus.component_body_index`)
|
||||||
-- re-enter the same walker with the same shared output state.
|
-- re-enter the same walker with the same shared output state.
|
||||||
@@ -2726,6 +2812,10 @@ function M.project_emission(body_text, component_index, word_counts, components)
|
|||||||
component_index = component_index or {},
|
component_index = component_index or {},
|
||||||
word_counts = word_counts or {},
|
word_counts = word_counts or {},
|
||||||
components = components,
|
components = components,
|
||||||
|
reg_use_schema = reg_use_ctx and reg_use_ctx.reg_use_schema,
|
||||||
|
reg_use_param = reg_use_ctx and reg_use_ctx.reg_use_param,
|
||||||
|
atom_name = reg_use_ctx and reg_use_ctx.atom_name,
|
||||||
|
schema_name = reg_use_ctx and reg_use_ctx.schema_name,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2860,13 +2950,12 @@ function M.find_atom_proc_decl_for(source, before_pos, mips_atom_ptr_len)
|
|||||||
if source:sub(next_pos, next_pos) == "(" then
|
if source:sub(next_pos, next_pos) == "(" then
|
||||||
local inner = M.read_parens(source, next_pos)
|
local inner = M.read_parens(source, next_pos)
|
||||||
if inner then
|
if inner then
|
||||||
-- strip the _proc suffix to get the atom name
|
|
||||||
local proc_suffix = "_proc"
|
local proc_suffix = "_proc"
|
||||||
|
local atom_name = ident
|
||||||
if #ident > #proc_suffix and ident:sub(-#proc_suffix) == proc_suffix then
|
if #ident > #proc_suffix and ident:sub(-#proc_suffix) == proc_suffix then
|
||||||
return ident:sub(1, #ident - #proc_suffix), inner
|
atom_name = ident:sub(1, #ident - #proc_suffix)
|
||||||
end
|
end
|
||||||
-- no _proc suffix — return as-is
|
return atom_name, inner, ident
|
||||||
return ident, inner
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- ident not followed by "(" — it's a qualifier; skip it
|
-- ident not followed by "(" — it's a qualifier; skip it
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -159,6 +159,14 @@ local function extract_arg_names(args_str)
|
|||||||
return names
|
return names
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function formal_arg_names(args_str)
|
||||||
|
local names = extract_arg_names(args_str)
|
||||||
|
if not names then return nil end
|
||||||
|
if names[1] == "ab" then table.remove(names, 1) end
|
||||||
|
if #names == 0 then return nil end
|
||||||
|
return names
|
||||||
|
end
|
||||||
|
|
||||||
-- ════════════════════════════════════════════════════════════════════════════
|
-- ════════════════════════════════════════════════════════════════════════════
|
||||||
-- Component projection (read from pre-scanned SourceScan)
|
-- Component projection (read from pre-scanned SourceScan)
|
||||||
-- ════════════════════════════════════════════════════════════════════════════
|
-- ════════════════════════════════════════════════════════════════════════════
|
||||||
@@ -197,6 +205,7 @@ local function project_components(source, scan)
|
|||||||
body_off = a.body_off,
|
body_off = a.body_off,
|
||||||
body_tokens = a.body_tokens,
|
body_tokens = a.body_tokens,
|
||||||
args = args,
|
args = args,
|
||||||
|
arg_names = formal_arg_names(args),
|
||||||
comment = comment,
|
comment = comment,
|
||||||
kind = a.kind, -- "comp_bare" | "comp_proc"; provenance emitter reads this.
|
kind = a.kind, -- "comp_bare" | "comp_proc"; provenance emitter reads this.
|
||||||
debug_skip = a.debug_skip == true,
|
debug_skip = a.debug_skip == true,
|
||||||
@@ -466,18 +475,9 @@ end
|
|||||||
--- @param args_str string|nil
|
--- @param args_str string|nil
|
||||||
--- @return string
|
--- @return string
|
||||||
local function signature_from_args(args_str)
|
local function signature_from_args(args_str)
|
||||||
local arg_names = extract_arg_names(args_str)
|
local names = formal_arg_names(args_str)
|
||||||
if arg_names and #arg_names > 0 then
|
if names then
|
||||||
-- Drop the leading `ab` (atom-builder) first arg if present.
|
return table.concat(names, ", ")
|
||||||
-- Convention: `MipsAtomComp_Proc_` components always declare `ab` as the first function-arg
|
|
||||||
-- (type `MipsAtomBuilder_R`), mirroring the macro signature in `lottes_tape.h`.
|
|
||||||
if arg_names[1] == "ab" then
|
|
||||||
table.remove(arg_names, 1)
|
|
||||||
end
|
|
||||||
if #arg_names > 0 then
|
|
||||||
return table.concat(arg_names, ", ")
|
|
||||||
end
|
|
||||||
return "..." -- `ab` was the only arg; fall through to variadic
|
|
||||||
end
|
end
|
||||||
return "..."
|
return "..."
|
||||||
end
|
end
|
||||||
@@ -710,6 +710,7 @@ local function update_canonical_component_body_index(corpus, src, components, sc
|
|||||||
source = src.path,
|
source = src.path,
|
||||||
declaration = c.line,
|
declaration = c.line,
|
||||||
kind = c.kind,
|
kind = c.kind,
|
||||||
|
arg_names = c.arg_names,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -155,8 +155,28 @@ local function project_atom(atom_record, src, corpus)
|
|||||||
local body = atom_record.body or ""
|
local body = atom_record.body or ""
|
||||||
local wc = corpus.word_counts or {}
|
local wc = corpus.word_counts or {}
|
||||||
local cbi = corpus.component_body_index or {}
|
local cbi = corpus.component_body_index or {}
|
||||||
|
local schema = nil
|
||||||
|
if atom_record.reg_use_schema_name then
|
||||||
|
schema = corpus.reg_use_schemas and corpus.reg_use_schemas[atom_record.reg_use_schema_name]
|
||||||
|
end
|
||||||
-- That construction site stamps `invocation.debug_skip` while appending each record to `proj.invocations`.
|
-- That construction site stamps `invocation.debug_skip` while appending each record to `proj.invocations`.
|
||||||
local proj = duffle.project_emission(body, cbi, wc, corpus.components)
|
local proj = duffle.project_emission(body, cbi, wc, corpus.components, {
|
||||||
|
reg_use_schema = schema,
|
||||||
|
reg_use_param = atom_record.reg_use_param_name,
|
||||||
|
atom_name = atom_record.name,
|
||||||
|
schema_name = atom_record.reg_use_schema_name,
|
||||||
|
})
|
||||||
|
if atom_record.reg_use_schema_name and not schema then
|
||||||
|
proj.errors[#proj.errors + 1] = {
|
||||||
|
kind = "reguse_missing_schema",
|
||||||
|
msg = string.format("RegUse schema %q is missing", atom_record.reg_use_schema_name),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
for _, err in ipairs(corpus.reg_use_errors or {}) do
|
||||||
|
if err.schema_name == atom_record.reg_use_schema_name then
|
||||||
|
proj.errors[#proj.errors + 1] = err
|
||||||
|
end
|
||||||
|
end
|
||||||
local paths = {
|
local paths = {
|
||||||
tokens = atom_record.body_tokens or {},
|
tokens = atom_record.body_tokens or {},
|
||||||
line_in_body = duffle.build_body_line_index(body),
|
line_in_body = duffle.build_body_line_index(body),
|
||||||
|
|||||||
+383
-227
@@ -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 = {}
|
|
||||||
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
|
|
||||||
local n_annot, n_binds, n_macros = 0, 0, 0
|
local n_annot, n_binds, n_macros = 0, 0, 0
|
||||||
for _, r in ipairs(annot_results) do
|
for _, src in ipairs(dir_sources) do
|
||||||
n_annot = n_annot + #r.annots
|
n_annot = n_annot + #((src.scan and src.scan.atom_infos) or {})
|
||||||
n_binds = n_binds + #r.binds
|
n_binds = n_binds + #((src.scan and src.scan.binds) or {})
|
||||||
n_macros = n_macros + #r.macros
|
n_macros = n_macros + #((src.scan and src.scan.macros) or {})
|
||||||
|
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
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -1416,13 +1443,49 @@ local function parse_mips_atom_proc(source, pos, ident_end, line_of, out)
|
|||||||
-- (`internal MipsAtom* X_proc(...)`), not from the first macro arg (which
|
-- (`internal MipsAtom* X_proc(...)`), not from the first macro arg (which
|
||||||
-- is now `aa`). The backward walk finds the function decl before open_paren
|
-- is now `aa`). The backward walk finds the function decl before open_paren
|
||||||
-- and strips the `_proc` suffix.
|
-- and strips the `_proc` suffix.
|
||||||
local raw_name = duffle.find_atom_proc_decl_for(source, open_paren, MIPS_ATOM_PTR_LEN)
|
local raw_name, args_inner, func_ident = duffle.find_atom_proc_decl_for(source, open_paren, MIPS_ATOM_PTR_LEN)
|
||||||
if not raw_name then raw_name = "?" end
|
if not raw_name then raw_name = "?" end
|
||||||
local name = strip_ac_prefix(raw_name)
|
local name = strip_ac_prefix(raw_name)
|
||||||
|
local reg_use_schema_name = nil
|
||||||
|
local reg_use_param_name = nil
|
||||||
|
if args_inner then
|
||||||
|
local arg_tokens = duffle.split_top_level_commas(args_inner)
|
||||||
|
for _, tok in ipairs(arg_tokens) do
|
||||||
|
local trimmed = duffle.trim(tok)
|
||||||
|
local schema_suffix, param = trimmed:match("RegUse_([%w_]+)%s+([%w_]+)$")
|
||||||
|
if schema_suffix then
|
||||||
|
if reg_use_schema_name then
|
||||||
|
out.reg_use_errors[#out.reg_use_errors + 1] = {
|
||||||
|
kind = "reguse_multiple_params",
|
||||||
|
schema_name = "RegUse_" .. schema_suffix,
|
||||||
|
source_line = line_of(pos),
|
||||||
|
}
|
||||||
|
else
|
||||||
|
reg_use_schema_name = "RegUse_" .. schema_suffix
|
||||||
|
reg_use_param_name = param
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
-- Position of body[1] in source = open_paren + 1 (start of inner) + last_brace_pos + 1 (past '{').
|
-- Position of body[1] in source = open_paren + 1 (start of inner) + last_brace_pos + 1 (past '{').
|
||||||
local body_off = open_paren + 2 + last_brace_pos
|
local body_off = open_paren + 2 + last_brace_pos
|
||||||
register_atom(out, "atom_proc", line_of(pos), name, body, body_off, raw_name, pos, after_paren, source)
|
register_atom(out, "atom_proc", line_of(pos), name, body, body_off, raw_name, pos, after_paren, source)
|
||||||
|
|
||||||
|
local entry = out.atoms[#out.atoms]
|
||||||
|
entry.reg_use_schema_name = reg_use_schema_name
|
||||||
|
entry.reg_use_param_name = reg_use_param_name
|
||||||
|
if reg_use_schema_name and func_ident then
|
||||||
|
local expected = "RegUse_" .. func_ident
|
||||||
|
if reg_use_schema_name ~= expected then
|
||||||
|
out.reg_use_errors[#out.reg_use_errors + 1] = {
|
||||||
|
kind = "reguse_name_mismatch",
|
||||||
|
schema_name = reg_use_schema_name,
|
||||||
|
func_ident = func_ident,
|
||||||
|
source_line = line_of(pos),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return after_paren
|
return after_paren
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1530,6 +1593,171 @@ local function register_typedef_alias(underlying, name, pos, line_of, out)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function parse_reg_use_schema_body(body)
|
||||||
|
local slots = {}
|
||||||
|
local alias_to_slot = {}
|
||||||
|
local slot_names = {}
|
||||||
|
local errors = {}
|
||||||
|
|
||||||
|
local function add_alias(path, slot)
|
||||||
|
if alias_to_slot[path] then
|
||||||
|
errors[#errors + 1] = { kind = "reguse_duplicate_alias", path = path }
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
alias_to_slot[path] = slot
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function add_slot(name, aliases, readonly)
|
||||||
|
if slot_names[name] then
|
||||||
|
errors[#errors + 1] = { kind = "reguse_duplicate_slot", name = name }
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
slot_names[name] = true
|
||||||
|
local slot = { name = name, aliases = aliases, readonly = readonly == true }
|
||||||
|
slots[#slots + 1] = slot
|
||||||
|
return slot
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_reg_names(text, pos)
|
||||||
|
local names = {}
|
||||||
|
while pos <= #text do
|
||||||
|
pos = duffle.skip_ws_and_cmt(text, pos)
|
||||||
|
local name, name_end = duffle.read_ident(text, pos)
|
||||||
|
if not name then return nil, pos end
|
||||||
|
names[#names + 1] = name
|
||||||
|
pos = duffle.skip_ws_and_cmt(text, name_end)
|
||||||
|
if text:sub(pos, pos) == "," then
|
||||||
|
pos = pos + 1
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if text:sub(pos, pos) == ";" then pos = pos + 1 end
|
||||||
|
return names, pos
|
||||||
|
end
|
||||||
|
|
||||||
|
local pos = 1
|
||||||
|
while pos <= #body do
|
||||||
|
pos = duffle.skip_ws_and_cmt(body, pos)
|
||||||
|
if pos > #body then break end
|
||||||
|
local first, first_end = duffle.read_ident(body, pos)
|
||||||
|
if not first then
|
||||||
|
pos = pos + 1
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
local after = duffle.skip_ws_and_cmt(body, first_end)
|
||||||
|
if first == "const" then
|
||||||
|
errors[#errors + 1] = { kind = "reguse_const_reg_spelling" }
|
||||||
|
return nil, errors
|
||||||
|
elseif first == "union" then
|
||||||
|
if body:sub(after, after) ~= "{" then
|
||||||
|
errors[#errors + 1] = { kind = "reguse_malformed" }
|
||||||
|
return nil, errors
|
||||||
|
end
|
||||||
|
local inner, after_braces = duffle.read_braces(body, after)
|
||||||
|
if not inner then
|
||||||
|
errors[#errors + 1] = { kind = "reguse_malformed" }
|
||||||
|
return nil, errors
|
||||||
|
end
|
||||||
|
local members = {}
|
||||||
|
local union_readonly = nil
|
||||||
|
local inner_pos = 1
|
||||||
|
while inner_pos <= #inner do
|
||||||
|
inner_pos = duffle.skip_ws_and_cmt(inner, inner_pos)
|
||||||
|
if inner_pos > #inner then break end
|
||||||
|
local m_type, m_type_end = duffle.read_ident(inner, inner_pos)
|
||||||
|
if not m_type then
|
||||||
|
inner_pos = inner_pos + 1
|
||||||
|
goto continue_inner
|
||||||
|
end
|
||||||
|
if m_type == "const" then
|
||||||
|
errors[#errors + 1] = { kind = "reguse_const_reg_spelling" }
|
||||||
|
return nil, errors
|
||||||
|
end
|
||||||
|
if m_type ~= "Reg" then
|
||||||
|
errors[#errors + 1] = { kind = "reguse_malformed" }
|
||||||
|
return nil, errors
|
||||||
|
end
|
||||||
|
local m_after = duffle.skip_ws_and_cmt(inner, m_type_end)
|
||||||
|
local m_readonly = false
|
||||||
|
local maybe_const, maybe_end = duffle.read_ident(inner, m_after)
|
||||||
|
if maybe_const == "const" then
|
||||||
|
m_readonly = true
|
||||||
|
m_after = duffle.skip_ws_and_cmt(inner, maybe_end)
|
||||||
|
end
|
||||||
|
if union_readonly == nil then
|
||||||
|
union_readonly = m_readonly
|
||||||
|
elseif union_readonly ~= m_readonly then
|
||||||
|
errors[#errors + 1] = { kind = "reguse_mixed_const" }
|
||||||
|
return nil, errors
|
||||||
|
end
|
||||||
|
local names, new_inner = parse_reg_names(inner, m_after)
|
||||||
|
if not names or #names == 0 then
|
||||||
|
errors[#errors + 1] = { kind = "reguse_malformed" }
|
||||||
|
return nil, errors
|
||||||
|
end
|
||||||
|
for _, n in ipairs(names) do members[#members + 1] = n end
|
||||||
|
inner_pos = new_inner
|
||||||
|
::continue_inner::
|
||||||
|
end
|
||||||
|
if #members == 0 then
|
||||||
|
errors[#errors + 1] = { kind = "reguse_malformed" }
|
||||||
|
return nil, errors
|
||||||
|
end
|
||||||
|
local after_close = duffle.skip_ws_and_cmt(body, after_braces)
|
||||||
|
local inst_name, inst_end = duffle.read_ident(body, after_close)
|
||||||
|
local aliases = {}
|
||||||
|
local slot_name
|
||||||
|
if inst_name then
|
||||||
|
slot_name = inst_name
|
||||||
|
for _, m in ipairs(members) do
|
||||||
|
local path = inst_name .. "." .. m
|
||||||
|
if not add_alias(path, slot_name) then return nil, errors end
|
||||||
|
aliases[#aliases + 1] = path
|
||||||
|
end
|
||||||
|
after_close = inst_end
|
||||||
|
else
|
||||||
|
slot_name = members[1]
|
||||||
|
for _, m in ipairs(members) do
|
||||||
|
if not add_alias(m, slot_name) then return nil, errors end
|
||||||
|
aliases[#aliases + 1] = m
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not add_slot(slot_name, aliases, union_readonly) then return nil, errors end
|
||||||
|
after_close = duffle.skip_ws_and_cmt(body, after_close)
|
||||||
|
if body:sub(after_close, after_close) == ";" then after_close = after_close + 1 end
|
||||||
|
pos = after_close
|
||||||
|
elseif first == "Reg" then
|
||||||
|
local readonly = false
|
||||||
|
local maybe_const, maybe_end = duffle.read_ident(body, after)
|
||||||
|
if maybe_const == "const" then
|
||||||
|
readonly = true
|
||||||
|
after = duffle.skip_ws_and_cmt(body, maybe_end)
|
||||||
|
end
|
||||||
|
local names, new_pos = parse_reg_names(body, after)
|
||||||
|
if not names or #names == 0 then
|
||||||
|
errors[#errors + 1] = { kind = "reguse_malformed" }
|
||||||
|
return nil, errors
|
||||||
|
end
|
||||||
|
for _, n in ipairs(names) do
|
||||||
|
if not add_alias(n, n) then return nil, errors end
|
||||||
|
if not add_slot(n, { n }, readonly) then return nil, errors end
|
||||||
|
end
|
||||||
|
pos = new_pos
|
||||||
|
else
|
||||||
|
errors[#errors + 1] = { kind = "reguse_malformed" }
|
||||||
|
return nil, errors
|
||||||
|
end
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
if #slots == 0 then
|
||||||
|
errors[#errors + 1] = { kind = "reguse_malformed" }
|
||||||
|
return nil, errors
|
||||||
|
end
|
||||||
|
return { slots = slots, alias_to_slot = alias_to_slot }, errors
|
||||||
|
end
|
||||||
|
|
||||||
--- Parse: `typedef` declarations.
|
--- Parse: `typedef` declarations.
|
||||||
---
|
---
|
||||||
--- Recognizes four shapes:
|
--- Recognizes four shapes:
|
||||||
@@ -1563,6 +1791,21 @@ local function parse_typedef_binds(source, pos, ident_end, line_of, out)
|
|||||||
local body, after_brace = find_body_braces(source, after_paren, open_paren + 1)
|
local body, after_brace = find_body_braces(source, after_paren, open_paren + 1)
|
||||||
if not body then return after_brace end
|
if not body then return after_brace end
|
||||||
register_struct_type(body, name, pos, line_of, out)
|
register_struct_type(body, name, pos, line_of, out)
|
||||||
|
if name:sub(1, 7) == "RegUse_" then
|
||||||
|
local schema, schema_errors = parse_reg_use_schema_body(body)
|
||||||
|
if schema then
|
||||||
|
schema.name = name
|
||||||
|
schema.source_file = out._source_file
|
||||||
|
schema.source_line = line_of(pos)
|
||||||
|
out.reg_use_schemas[name] = schema
|
||||||
|
end
|
||||||
|
for _, err in ipairs(schema_errors or {}) do
|
||||||
|
err.schema_name = name
|
||||||
|
err.source_file = out._source_file
|
||||||
|
err.source_line = line_of(pos)
|
||||||
|
out.reg_use_errors[#out.reg_use_errors + 1] = err
|
||||||
|
end
|
||||||
|
end
|
||||||
attach_debug_skip_marker(out, "unrelated")
|
attach_debug_skip_marker(out, "unrelated")
|
||||||
return after_brace
|
return after_brace
|
||||||
|
|
||||||
@@ -1893,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,
|
||||||
@@ -1954,6 +2198,8 @@ local function scan_source(source, source_file, code_macros, code_macro_bodies)
|
|||||||
-- typedef chain walking (cycle-guarded, depth <= 8), and struct field sums.
|
-- typedef chain walking (cycle-guarded, depth <= 8), and struct field sums.
|
||||||
-- See `propagate_type_sizes()` below.
|
-- See `propagate_type_sizes()` below.
|
||||||
type_name_registry = {},
|
type_name_registry = {},
|
||||||
|
reg_use_schemas = {},
|
||||||
|
reg_use_errors = {},
|
||||||
-- Shared `R_*_Code -> integer code` registry
|
-- Shared `R_*_Code -> integer code` registry
|
||||||
-- (passed in from M.run pass 1; same reference so preprocessor intercept writes are visible to the enum-value resolver).
|
-- (passed in from M.run pass 1; same reference so preprocessor intercept writes are visible to the enum-value resolver).
|
||||||
-- Stripped from `src.scan` before return.
|
-- Stripped from `src.scan` before return.
|
||||||
@@ -2186,13 +2432,15 @@ local function merge_corpus_registries(corpus)
|
|||||||
corpus.atom_auto_regs = corpus.atom_auto_regs or {}
|
corpus.atom_auto_regs = corpus.atom_auto_regs or {}
|
||||||
corpus.phase_auto_regs = corpus.phase_auto_regs or {}
|
corpus.phase_auto_regs = corpus.phase_auto_regs or {}
|
||||||
corpus.collisions = corpus.collisions or {}
|
corpus.collisions = corpus.collisions or {}
|
||||||
|
corpus.reg_use_schemas = corpus.reg_use_schemas or {}
|
||||||
|
corpus.reg_use_errors = corpus.reg_use_errors or {}
|
||||||
|
|
||||||
-- Replace the existing corpus collections with empty tables so a re-run on the same corpus produces identical state (deterministic merge).
|
-- Replace the existing corpus collections with empty tables so a re-run on the same corpus produces identical state (deterministic merge).
|
||||||
-- This is safe because M.run is the only writer to these tables within a single orchestrator invocation.
|
-- This is safe because M.run is the only writer to these tables within a single orchestrator invocation.
|
||||||
for _, key in ipairs({
|
for _, key in ipairs({
|
||||||
"register_alias_registry", "type_name_registry", "binds_by_name",
|
"register_alias_registry", "type_name_registry", "binds_by_name",
|
||||||
"atoms_by_name", "atom_views", "atom_ctxs", "atom_phases",
|
"atoms_by_name", "atom_views", "atom_ctxs", "atom_phases",
|
||||||
"atom_infos", "collisions",
|
"atom_infos", "collisions", "reg_use_schemas", "reg_use_errors",
|
||||||
}) do
|
}) do
|
||||||
corpus[key] = {}
|
corpus[key] = {}
|
||||||
end
|
end
|
||||||
@@ -2285,6 +2533,15 @@ local function merge_corpus_registries(corpus)
|
|||||||
for _, info in ipairs(scan.atom_infos or {}) do
|
for _, info in ipairs(scan.atom_infos or {}) do
|
||||||
corpus.atom_infos[#corpus.atom_infos + 1] = info
|
corpus.atom_infos[#corpus.atom_infos + 1] = info
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for name, schema in pairs(scan.reg_use_schemas or {}) do
|
||||||
|
if corpus.reg_use_schemas[name] == nil then
|
||||||
|
corpus.reg_use_schemas[name] = schema
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _, err in ipairs(scan.reg_use_errors or {}) do
|
||||||
|
corpus.reg_use_errors[#corpus.reg_use_errors + 1] = err
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -451,6 +451,14 @@ local function is_cop2_consumer_of(consumer_event, destination, producer_rel)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function gpr_identity(event, pos)
|
||||||
|
local keys = event and event.gpr_keys
|
||||||
|
if keys and keys[pos] then return keys[pos] end
|
||||||
|
local arg = event and event.args and event.args[pos]
|
||||||
|
if type(arg) == "string" and arg:sub(1, 2) == "R_" then return arg end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
-- True iff `consumer_event` reads the GPR operand at any position the destination register occupies.
|
-- True iff `consumer_event` reads the GPR operand at any position the destination register occupies.
|
||||||
-- read_pos 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 function is_gpr_consumer_of(consumer_event, destination)
|
||||||
@@ -458,9 +466,8 @@ local function is_gpr_consumer_of(consumer_event, destination)
|
|||||||
local read_pos = duffle.OPERAND_READ_POSITIONS or {}
|
local read_pos = duffle.OPERAND_READ_POSITIONS or {}
|
||||||
local positions = read_pos[consumer_token]
|
local positions = read_pos[consumer_token]
|
||||||
if not positions then return false end
|
if not positions then return false end
|
||||||
local args = consumer_event.args or {}
|
|
||||||
for _, pos in ipairs(positions) do
|
for _, pos in ipairs(positions) do
|
||||||
if args[pos] == destination then return true end
|
if gpr_identity(consumer_event, pos) == destination then return true end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
@@ -552,6 +559,11 @@ local function is_gpr_operand(operand)
|
|||||||
return type(operand) == "string" and operand:sub(1, 2) == "R_"
|
return type(operand) == "string" and operand:sub(1, 2) == "R_"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function is_tracked_gpr(operand)
|
||||||
|
return is_gpr_operand(operand)
|
||||||
|
or (type(operand) == "string" and operand:sub(1, 7) == "reguse:")
|
||||||
|
end
|
||||||
|
|
||||||
local function constant_for_operand(gpr_values, operand)
|
local function constant_for_operand(gpr_values, operand)
|
||||||
if operand == "R_0" then return 0 end
|
if operand == "R_0" then return 0 end
|
||||||
local slot = is_gpr_operand(operand) and gpr_values[operand] or nil
|
local slot = is_gpr_operand(operand) and gpr_values[operand] or nil
|
||||||
@@ -560,13 +572,13 @@ local function constant_for_operand(gpr_values, operand)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function invalidate_gpr(gpr_values, operand)
|
local function invalidate_gpr(gpr_values, operand)
|
||||||
if is_gpr_operand(operand) and operand ~= "R_0" then
|
if is_tracked_gpr(operand) and operand ~= "R_0" then
|
||||||
gpr_values[operand] = { kind = "unknown" }
|
gpr_values[operand] = { kind = "unknown" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function store_gpr_constant(gpr_values, operand, value)
|
local function store_gpr_constant(gpr_values, operand, value)
|
||||||
if not is_gpr_operand(operand) or operand == "R_0" then return end
|
if not is_tracked_gpr(operand) or operand == "R_0" then return end
|
||||||
if value == nil then gpr_values[operand] = { kind = "unknown" }
|
if value == nil then gpr_values[operand] = { kind = "unknown" }
|
||||||
else gpr_values[operand] = { kind = "constant", value = wrap_u4(value) }
|
else gpr_values[operand] = { kind = "constant", value = wrap_u4(value) }
|
||||||
end
|
end
|
||||||
@@ -629,26 +641,31 @@ end
|
|||||||
-- Encoders without an explicit effect row conservatively invalidate every R_-prefixed operand.
|
-- Encoders without an explicit effect row conservatively invalidate every R_-prefixed operand.
|
||||||
-- Recognized value rules are evaluated before their destination is invalidated.
|
-- Recognized value rules are evaluated before their destination is invalidated.
|
||||||
-- A failed/unknown evaluation writes `{kind = "unknown"}` instead.
|
-- A failed/unknown evaluation writes `{kind = "unknown"}` instead.
|
||||||
local function apply_gpr_effects(ev_ident, ev_args, forward_state)
|
local function apply_gpr_effects(ev, forward_state)
|
||||||
|
local ev_ident = ev.encoder or ev.ident
|
||||||
|
local ev_args = ev.args or {}
|
||||||
local gpr_values = forward_state.gpr_values
|
local gpr_values = forward_state.gpr_values
|
||||||
local effects = duffle.INSTRUCTION_GPR_EFFECTS or {}
|
local effects = duffle.INSTRUCTION_GPR_EFFECTS or {}
|
||||||
local row = effects[ev_ident]
|
local row = effects[ev_ident]
|
||||||
if row == nil then
|
if row == nil then
|
||||||
for _, operand in ipairs(ev_args or {}) do
|
for pos, operand in ipairs(ev_args) do
|
||||||
invalidate_gpr(gpr_values, operand)
|
local key = gpr_identity(ev, pos) or operand
|
||||||
|
if type(key) == "string" and (key:sub(1, 2) == "R_" or key:sub(1, 7) == "reguse:") then
|
||||||
|
if key ~= "R_0" then gpr_values[key] = { kind = "unknown" } end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local value_rule = (duffle.GPR_VALUE_RULES or {})[ev_ident]
|
local value_rule = (duffle.GPR_VALUE_RULES or {})[ev_ident]
|
||||||
local value = value_rule and evaluate_gpr_value_rule(value_rule, ev_args or {}, gpr_values) or nil
|
local value = value_rule and evaluate_gpr_value_rule(value_rule, ev_args, gpr_values) or nil
|
||||||
for _, position in ipairs(row.writes or {}) do
|
for _, position in ipairs(row.writes or {}) do
|
||||||
local destination = ev_args and ev_args[position]
|
local destination = gpr_identity(ev, position)
|
||||||
if is_gpr_operand(destination) then
|
if destination then
|
||||||
if value_rule and position == value_rule.dest and value ~= nil then
|
if value_rule and position == value_rule.dest and value ~= nil then
|
||||||
store_gpr_constant(gpr_values, destination, value)
|
store_gpr_constant(gpr_values, destination, value)
|
||||||
else
|
else
|
||||||
invalidate_gpr(gpr_values, destination)
|
if destination ~= "R_0" then gpr_values[destination] = { kind = "unknown" } end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -953,7 +970,7 @@ local function analyze_hardware_relations(atom)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- ── 2. Apply GPR value effects. ──
|
-- ── 2. Apply GPR value effects. ──
|
||||||
apply_gpr_effects(ev_ident, ev_args, forward)
|
apply_gpr_effects(ev, forward)
|
||||||
|
|
||||||
-- ── 3. Stage producers created by this event. ──
|
-- ── 3. Stage producers created by this event. ──
|
||||||
local rows = rows_by_token[ev_ident]
|
local rows = rows_by_token[ev_ident]
|
||||||
@@ -962,7 +979,7 @@ local function analyze_hardware_relations(atom)
|
|||||||
-- `stage = false` rows document a direction but do not create a later command-input producer (SWC2 and ordinary MTC0).
|
-- `stage = false` rows document a direction but do not create a later command-input producer (SWC2 and ordinary MTC0).
|
||||||
if row.stage ~= false then
|
if row.stage ~= false then
|
||||||
local dest_arg = row.writes and row.writes.arg
|
local dest_arg = row.writes and row.writes.arg
|
||||||
local destination = dest_arg and ev_args[dest_arg] or nil
|
local destination = dest_arg and (gpr_identity(ev, dest_arg) or ev_args[dest_arg]) or nil
|
||||||
if destination then
|
if destination then
|
||||||
-- Apply the destination_match filter when present.
|
-- Apply the destination_match filter when present.
|
||||||
if row.destination_match and row.destination_match ~= destination then
|
if row.destination_match and row.destination_match ~= destination then
|
||||||
@@ -1305,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",
|
||||||
@@ -1319,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
|
||||||
@@ -1352,7 +1375,7 @@ local function check_hazard_nop_use(atom, _pipe_ctx, findings)
|
|||||||
for _, row in ipairs(relations_table) do
|
for _, row in ipairs(relations_table) do
|
||||||
if row.token == ev_ident and row.stage ~= false then
|
if row.token == ev_ident and row.stage ~= false then
|
||||||
local dest_arg = row.writes and row.writes.arg
|
local dest_arg = row.writes and row.writes.arg
|
||||||
local destination = dest_arg and ev_args[dest_arg] or nil
|
local destination = dest_arg and (gpr_identity(ev, dest_arg) or ev_args[dest_arg]) or nil
|
||||||
if destination and (not row.destination_match or row.destination_match == destination) then
|
if destination and (not row.destination_match or row.destination_match == destination) then
|
||||||
local required = row.visibility and row.visibility.required
|
local required = row.visibility and row.visibility.required
|
||||||
if required == nil and not (row.visibility and row.visibility.kind == "unknown_consumer") then
|
if required == nil and not (row.visibility and row.visibility.kind == "unknown_consumer") then
|
||||||
@@ -1517,11 +1540,12 @@ local function check_load_delay_slots(atom, pipe_ctx, findings)
|
|||||||
-- Use `net_reads` to ignore RMW positions (write shadows read within the same instruction).
|
-- Use `net_reads` to ignore RMW positions (write shadows read within the same instruction).
|
||||||
if not is_load then
|
if not is_load then
|
||||||
for _, pos in ipairs(net_reads(event_ident, args)) do
|
for _, pos in ipairs(net_reads(event_ident, args)) do
|
||||||
local reg = args[pos]
|
local reg = gpr_identity(event, pos)
|
||||||
if type(reg) == "string" and reg:sub(1, 2) == "R_" then
|
if reg then
|
||||||
local until_idx = volatile_until[reg]
|
local until_idx = volatile_until[reg]
|
||||||
if until_idx and event_idx <= until_idx then
|
if until_idx and event_idx <= until_idx then
|
||||||
local ev_line = line_for_word_event(event)
|
local ev_line = line_for_word_event(event)
|
||||||
|
local authored = args[pos] or reg
|
||||||
findings[#findings + 1] = {
|
findings[#findings + 1] = {
|
||||||
atom = atom.name,
|
atom = atom.name,
|
||||||
line = ev_line,
|
line = ev_line,
|
||||||
@@ -1530,7 +1554,7 @@ local function check_load_delay_slots(atom, pipe_ctx, findings)
|
|||||||
msg = string.format("%s at line %d reads %s at word %d, but a prior load's "
|
msg = string.format("%s at line %d reads %s at word %d, but a prior load's "
|
||||||
.. "delay slot is not over until word %d; insert a `nop` between the "
|
.. "delay slot is not over until word %d; insert a `nop` between the "
|
||||||
.. "load and this instruction.",
|
.. "load and this instruction.",
|
||||||
atom.name, ev_line, reg, event_idx, until_idx),
|
atom.name, ev_line, authored, event_idx, until_idx),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1541,8 +1565,8 @@ local function check_load_delay_slots(atom, pipe_ctx, findings)
|
|||||||
local effect = gpr_effects[event_ident]
|
local effect = gpr_effects[event_ident]
|
||||||
if effect and effect.writes then
|
if effect and effect.writes then
|
||||||
for _, pos in ipairs(effect.writes) do
|
for _, pos in ipairs(effect.writes) do
|
||||||
local reg = args[pos]
|
local reg = gpr_identity(event, pos)
|
||||||
if type(reg) == "string" and reg:sub(1, 2) == "R_" then
|
if reg then
|
||||||
if is_load then
|
if is_load then
|
||||||
-- Load: destination volatile for exactly 1 slot (the delay slot).
|
-- Load: destination volatile for exactly 1 slot (the delay slot).
|
||||||
volatile_until[reg] = event_idx + 1
|
volatile_until[reg] = event_idx + 1
|
||||||
|
|||||||
Reference in New Issue
Block a user