mirror of
https://github.com/Ed94/pikuma_ps1.git
synced 2026-09-22 17:20:04 +00:00
Compare commits
2
Commits
02658d3609
...
09dde54030
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09dde54030 | ||
|
|
315e1b2c5e |
@@ -19,3 +19,4 @@ toolchain/lfs
|
||||
toolchain/lpeg
|
||||
|
||||
scratch
|
||||
toolchain/libpsn00b
|
||||
|
||||
Vendored
+36
-35
@@ -74,41 +74,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Debug: Hello GTE Psy-Q!",
|
||||
"type": "gdb",
|
||||
"request": "attach",
|
||||
"target": "localhost:3333",
|
||||
"remote": true,
|
||||
"cwd": "${workspaceRoot}/build",
|
||||
"valuesFormatting": "parseText",
|
||||
"registerLimit": "1-32",
|
||||
"frameFilters": false,
|
||||
"showDevDebugOutput": false,
|
||||
"printCalls": false,
|
||||
"stopAtConnect": true,
|
||||
"gdbpath": "gdb-multiarch",
|
||||
"windows": {
|
||||
"gdbpath": "gdb-multiarch.exe"
|
||||
},
|
||||
"osx": {
|
||||
"gdbpath": "gdb"
|
||||
},
|
||||
"executable": "${workspaceRoot}/build/hello_gte.elf",
|
||||
"setupCommands": [
|
||||
{ "text": "set mi-async off" },
|
||||
{ "text": "set remotetimeout 0" },
|
||||
{ "text": "set logging file build/gen/hello_gte.gdb.log" },
|
||||
{ "text": "set logging redirect on" }
|
||||
],
|
||||
"autorun": [
|
||||
"monitor reset shellhalt",
|
||||
"load hello_gte.elf",
|
||||
"tbreak main",
|
||||
"continue"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Debug: Hello GTE Psy-Q! (atoms debug — DWARF-injected)",
|
||||
"name": "Debug: Hello GTE!",
|
||||
"type": "gdb",
|
||||
"request": "attach",
|
||||
"target": "localhost:3333",
|
||||
@@ -141,6 +107,41 @@
|
||||
"tbreak main",
|
||||
"continue"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Debug: Hello Joypad!",
|
||||
"type": "gdb",
|
||||
"request": "attach",
|
||||
"target": "localhost:3333",
|
||||
"remote": true,
|
||||
"cwd": "${workspaceRoot}",
|
||||
"valuesFormatting": "parseText",
|
||||
"registerLimit": "1-32",
|
||||
"frameFilters": false,
|
||||
"showDevDebugOutput": false,
|
||||
"printCalls": false,
|
||||
"stopAtConnect": true,
|
||||
"gdbpath": "gdb-multiarch",
|
||||
"windows": {
|
||||
"gdbpath": "gdb-multiarch.exe"
|
||||
},
|
||||
"osx": {
|
||||
"gdbpath": "gdb"
|
||||
},
|
||||
"executable": "${workspaceRoot}/build/hello_joypad.dwarf-injected.elf",
|
||||
"setupCommands": [
|
||||
{ "text": "set mi-async off" },
|
||||
{ "text": "set remotetimeout 0" },
|
||||
{ "text": "set logging file build/gen/hello_joypad.gdb.log" },
|
||||
{ "text": "set logging redirect on" }
|
||||
],
|
||||
"autorun": [
|
||||
"monitor reset shellhalt",
|
||||
"load build/hello_joypad.dwarf-injected.elf",
|
||||
"source scripts/gdb/gdb_tape_atoms.gdb",
|
||||
"tbreak main",
|
||||
"continue"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ WORD_COUNT(mac_gte_load_tri_verts, 18)
|
||||
, load_word( R_AT, R_T1, O_(PolyTag,code)) /* AT = old_ot_head */ \
|
||||
, load_upper_i(R_V0, (S_(Poly_F3)/S_(U4) - S_(PolyTag)/S_(U4)) << PolyTag_len_bits) /* V0 = (5 - 1) << 24 = 4 << 24 */ \
|
||||
, mask_upper( R_AT, R_AT, S_(PolyTag_len_bits)) /* Strip upper 8 bits (length from prev cell) → keep only low 24 */ \
|
||||
, or_u( R_AT, R_AT, R_V0) /* Merge length */ \
|
||||
, or( R_AT, R_AT, R_V0) /* Merge length */ \
|
||||
, store_word( R_AT, R_PrimCursor, O_(PolyTag,code)) /* prim->tag = packed(prim_length, old_addr) */ \
|
||||
, shift_lleft( R_AT, R_PrimCursor, S_(PolyTag_len_bits)) /* AT = (prim_length << 24) | old_addr */ \
|
||||
, shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)) \
|
||||
@@ -76,7 +76,7 @@ WORD_COUNT(mac_insert_ot_tag_f3, 11)
|
||||
, load_word( R_AT, R_T1, O_(PolyTag,code)) /* AT = old_ot_head */ \
|
||||
, load_upper_i(R_V0, (S_(Poly_G4)/S_(U4) - S_(PolyTag)/S_(U4)) << PolyTag_len_bits) /* V0 = (9 - 1) << 24 = 8 << 24 */ \
|
||||
, mask_upper( R_AT, R_AT, S_(PolyTag_len_bits)) /* Strip upper 8 bits (length from prev cell) → keep only low 24 */ \
|
||||
, or_u( R_AT, R_AT, R_V0) /* Merge length */ \
|
||||
, or( R_AT, R_AT, R_V0) /* Merge length */ \
|
||||
, store_word( R_AT, R_PrimCursor, O_(PolyTag,code)) /* prim->tag = packed(prim_length, old_addr) */ \
|
||||
, shift_lleft( R_AT, R_PrimCursor, S_(PolyTag_len_bits)) /* AT = (prim_length << 24) | old_addr */ \
|
||||
, shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)) \
|
||||
|
||||
@@ -60,7 +60,7 @@ enum {
|
||||
atom_dbg_skip MipsAtom_(tape_exit) { jump_reg(rret_addr), nop };
|
||||
|
||||
/* Generalized Tape Engine Runner */
|
||||
NI_ void tape_run(Slice_MipsCode tape) { register U4* tp rgcc(R_TapePtr) = u4_r(tape.ptr); asm volatile(
|
||||
FI_ void tape_run(Slice_MipsCode tape) { register U4* tp rgcc(R_TapePtr) = u4_r(tape.ptr); asm volatile(
|
||||
asm_words(
|
||||
add_ui( R_SP, R_SP, -MipsStackAlignment) /* Allocate stack space */
|
||||
, store_word( R_RA, R_SP, 0) /* Safely backup $ra to the stack */
|
||||
@@ -93,7 +93,10 @@ FI_ void tb_data(TapeBuilder* tb, U4 data) { u4_r(tb->ptr)[tb->used] = u4
|
||||
|
||||
FI_ Slice_MipsCode tb_end (TapeBuilder* tb) { tb_emit(tb,tape_exit); return (Slice_MipsCode){ C_(U4*,tb->ptr), tb->used }; }
|
||||
FI_ Slice_MipsCode tb_slice(TapeBuilder tb) { return (Slice_MipsCode){ C_(U4*,tb.ptr), tb.used }; }
|
||||
#define tb_scope(tb) for(U4 tbs_once=0;tbs_once==0;++tbs_once,tb_emit(tb,tape_exit))
|
||||
#define tb_scope(tb) for(U4 tbs_once=0;tbs_once==0;++tbs_once,tb_emit(tb,tape_exit))
|
||||
|
||||
FI_ void tb_scope_run_end(TapeBuilder* tb) { tb_emit(tb,tape_exit); tape_run(tb_slice(tb[0])); }
|
||||
#define tb_scope_run(tb) for(U4 tbs_once=0;tbs_once==0;++tbs_once,tb_scope_run_end(tb))
|
||||
|
||||
#pragma endregion Tape Drive
|
||||
|
||||
@@ -132,7 +135,7 @@ MipsAtomComp_(ac_insert_ot_tag_f3) {
|
||||
load_word( R_AT, R_T1, O_(PolyTag,code)), // AT = old_ot_head
|
||||
load_upper_i(R_V0, (S_(Poly_F3)/S_(U4) - S_(PolyTag)/S_(U4)) << PolyTag_len_bits), // V0 = (5 - 1) << 24 = 4 << 24
|
||||
mask_upper( R_AT, R_AT, S_(PolyTag_len_bits)), // Strip upper 8 bits (length from prev cell) → keep only low 24
|
||||
or_u( R_AT, R_AT, R_V0), // Merge length
|
||||
or( R_AT, R_AT, R_V0), // Merge length
|
||||
store_word( R_AT, R_PrimCursor, O_(PolyTag,code)), // prim->tag = packed(prim_length, old_addr)
|
||||
shift_lleft( R_AT, R_PrimCursor, S_(PolyTag_len_bits)), // AT = (prim_length << 24) | old_addr
|
||||
shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)),
|
||||
@@ -147,7 +150,7 @@ MipsAtomComp_(ac_insert_ot_tag_g4) {
|
||||
load_word( R_AT, R_T1, O_(PolyTag,code)), // AT = old_ot_head
|
||||
load_upper_i(R_V0, (S_(Poly_G4)/S_(U4) - S_(PolyTag)/S_(U4)) << PolyTag_len_bits), // V0 = (9 - 1) << 24 = 8 << 24
|
||||
mask_upper( R_AT, R_AT, S_(PolyTag_len_bits)), // Strip upper 8 bits (length from prev cell) → keep only low 24
|
||||
or_u( R_AT, R_AT, R_V0), // Merge length
|
||||
or( R_AT, R_AT, R_V0), // Merge length
|
||||
store_word( R_AT, R_PrimCursor, O_(PolyTag,code)), // prim->tag = packed(prim_length, old_addr)
|
||||
shift_lleft( R_AT, R_PrimCursor, S_(PolyTag_len_bits)), // AT = (prim_length << 24) | old_addr
|
||||
shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)),
|
||||
|
||||
+4
-4
@@ -336,10 +336,10 @@ enum { _BitOffsets = 0
|
||||
|
||||
/* Logic Opcodes */
|
||||
|
||||
#define and_u(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_and)
|
||||
#define or_u(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_or)
|
||||
#define xor_u(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_xor)
|
||||
#define nor_u(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_nor)
|
||||
#define and(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_and)
|
||||
#define or(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_or)
|
||||
#define xor(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_xor)
|
||||
#define nor(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_nor)
|
||||
|
||||
#define or_u_self(rd_rs, rt) enc_r(op_special, (rd_rs), (rt), (rd_rs), 0, fc_or)
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifdef INTELLISENSE_DIRECTIVES
|
||||
# pragma once
|
||||
# include "dsl.h"
|
||||
#endif
|
||||
|
||||
|
||||
enum {
|
||||
Bit_(Pad_L2, 0),
|
||||
Bit_(Pad_R2, 1),
|
||||
Bit_(Pad_L1, 2),
|
||||
Bit_(Pad_R1, 3),
|
||||
Bit_(Pad_Triangle, 4),
|
||||
Bit_(Pad_Circle, 5),
|
||||
Bit_(Pad_Cross, 6),
|
||||
Bit_(Pad_Square, 7),
|
||||
Bit_(Pad_Select, 8),
|
||||
Bit_(Unused_PadI, 9),
|
||||
Bit_(Unused_PadJ, 10),
|
||||
Bit_(Pad_Start, 11),
|
||||
Bit_(Pad_Up, 12),
|
||||
Bit_(Pad_Right, 13),
|
||||
Bit_(Pad_Down, 14),
|
||||
Bit_(Pad_Left, 15),
|
||||
};
|
||||
|
||||
enum {
|
||||
PadId_Offset = 4,
|
||||
|
||||
Pad0 = 0 << PadId_Offset,
|
||||
Pad1 = 1 << PadId_Offset,
|
||||
};
|
||||
|
||||
#define pad0_(btn_id) (btn_id << Pad0)
|
||||
#define pad1_(btn_id) (btn_id << Pad1)
|
||||
|
||||
void pad_init(U4 mode) asm("PadInit");
|
||||
U4 pad_read(U4 id) asm("PadRead");
|
||||
|
||||
@@ -116,20 +116,22 @@ MipsAtom_(floor_f3_face) atom_info(atom_phase(floor_f3)
|
||||
/* Culling (Branch forward if Backface) */
|
||||
gte_mv_from_data_r(R_T0, C2_MAC0),
|
||||
nop, branch_le_zero(R_T0, atom_offset(culling, floor_f3_face_exit)), nop, // required gte -> cpu load-delay slot.
|
||||
/* Format Primitive */
|
||||
mac_gte_store_f3(),
|
||||
/* Format Primitive */
|
||||
mac_gte_store_f3(),
|
||||
|
||||
/* Calculate Depth */
|
||||
gte_avg_sort_z3,
|
||||
gte_mv_from_data_r(R_T1, C2_OTZ),
|
||||
/* Bounds Check OTZ < 2048 (Branch forward to skip insertion) */
|
||||
add_ui( R_AT, R_0, OrderingTbl_Len),
|
||||
set_lt_u( R_AT, R_T1, R_AT),
|
||||
branch_equal(R_AT, R_0, atom_offset(bounds_chk, floor_f3_face_exit)), nop,
|
||||
mac_format_f3_color(0xFF, 0xFF, 0xFF), // RGB-form (R=FF, G=FF, B=FF = white)
|
||||
mac_insert_ot_tag_f3(), /* Insert into Ordering Table Linked List */
|
||||
add_ui_self(R_PrimCursor, S_(Poly_F3)), /* Advance Prim Cursor (5 words) */
|
||||
// Note(Ed): No bounds checking, should be checked before atom runs.
|
||||
/* Calculate Depth */
|
||||
gte_avg_sort_z3,
|
||||
gte_mv_from_data_r(R_T1, C2_OTZ),
|
||||
/* Bounds Check OTZ < 2048 (Branch forward to skip insertion) */
|
||||
add_ui( R_AT, R_0, OrderingTbl_Len),
|
||||
set_lt_u( R_AT, R_T1, R_AT),
|
||||
branch_equal(R_AT, R_0, atom_offset(bounds_chk, floor_f3_face_exit)), nop,
|
||||
mac_format_f3_color(0xFF, 0xFF, 0xFF), // RGB-form (R=FF, G=FF, B=FF = white)
|
||||
mac_insert_ot_tag_f3(), /* Insert into Ordering Table Linked List */
|
||||
add_ui_self(R_PrimCursor, S_(Poly_F3)), /* Advance Prim Cursor (5 words) */
|
||||
// Note(Ed): No bounds checking, should be checked before atom runs.
|
||||
// end: branch(bounds_chk)
|
||||
// end: branch(culling)
|
||||
|
||||
/* Advance Input Cursor & Yield (Both branch targets land here) */
|
||||
atom_label(floor_f3_face_exit)
|
||||
|
||||
@@ -5,6 +5,16 @@
|
||||
#pragma region hello_joypad.tape
|
||||
|
||||
|
||||
// --- atom: pad_input_demo (24 words) ---
|
||||
|
||||
#define _atom_offset_pad_left_exit_pad_left 6
|
||||
#define _atom_offset_pad_right_exit_pad_right 6
|
||||
|
||||
enum {
|
||||
atom_offset_pad_left_exit_pad_left = _atom_offset_pad_left_exit_pad_left,
|
||||
atom_offset_pad_right_exit_pad_right = _atom_offset_pad_right_exit_pad_right,
|
||||
};
|
||||
|
||||
// --- atom: cube_g4_face (77 words) ---
|
||||
|
||||
#define _atom_offset_cull_cube_g4_face_exit 42
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "duffle/mips.h"
|
||||
#include "duffle/gp.h"
|
||||
#include "duffle/gte.h"
|
||||
#include "duffle/pad.h"
|
||||
|
||||
# include "duffle/gen/duffle.macs.h"
|
||||
# include "duffle/gen/duffle.offsets.h"
|
||||
@@ -25,6 +26,7 @@
|
||||
|
||||
#include "hello_joypad.tape.c"
|
||||
|
||||
|
||||
typedef U4 OrderingTable_Buffer[OrderingTbl_Len];
|
||||
typedef Array_(OrderingTable_Buffer, 2);
|
||||
|
||||
@@ -99,6 +101,7 @@ typedef Struct_(Ent_Floor) {
|
||||
A2_V3_S2 faces;
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
Scratchpad_Len = 1024,
|
||||
MemTape_Len = 512,
|
||||
@@ -116,11 +119,17 @@ typedef Struct_(SMemory) {
|
||||
Ent_Cube cube;
|
||||
Ent_Floor floor;
|
||||
|
||||
U4 pad_state;
|
||||
|
||||
U4_V scratchpad; // d-cache
|
||||
};
|
||||
global SMemory smem;
|
||||
extern SMemory smem;
|
||||
|
||||
#define pad0_signal_(btn_id) smem.pad_state & pad0_(btn_id)
|
||||
#define pad1_signal_(btn_id) smem.pad_state & pad1_(btn_id)
|
||||
|
||||
|
||||
// TODO(Ed):
|
||||
FI_ U4* spad_warm(MipsAtom atom) {
|
||||
return nullptr;
|
||||
@@ -190,6 +199,31 @@ void render(void) {
|
||||
GCC_OPTIMIZATION_DISABLE
|
||||
void update(PrimitiveArena* pa, U4* ordering_buf)
|
||||
{
|
||||
TapeBuilder tb = tb_make(slice_ut_arr(smem.MemTape));
|
||||
|
||||
smem.pad_state = pad_read(0);
|
||||
|
||||
if (0) // Pad Input
|
||||
{
|
||||
if (pad0_signal_(Pad_Left)) {
|
||||
smem.cube.rot.y += 30;
|
||||
smem.floor.rot.y += 5;
|
||||
}
|
||||
if (pad0_signal_(Pad_Right)) {
|
||||
smem.cube.rot.y -= 30;
|
||||
smem.floor.rot.y -= 5;
|
||||
}
|
||||
}
|
||||
if (1) // Pad Input (Tape version)
|
||||
{
|
||||
tb.used = 0; tb_scope_run(& tb) {
|
||||
tb_emit(& tb, pad_input_demo);
|
||||
tb_data(& tb, smem.pad_state);
|
||||
tb_data(& tb, u4_(& smem.cube.rot));
|
||||
tb_data(& tb, u4_(& smem.floor.rot));
|
||||
}
|
||||
}
|
||||
|
||||
orderingtbl_clear_reverse(ordering_buf, OrderingTbl_Len);
|
||||
|
||||
// Update the position based on acceleration and velocity
|
||||
@@ -213,7 +247,6 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
|
||||
A2_S2 p; //???
|
||||
S4 flag; //????
|
||||
|
||||
TapeBuilder tb = tb_make(slice_ut_arr(smem.MemTape));
|
||||
|
||||
// Draw Cube
|
||||
if (0)
|
||||
@@ -285,7 +318,7 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
|
||||
}
|
||||
tape_run(tb_slice(tb));
|
||||
|
||||
smem.cube.rot.y += 30;
|
||||
// smem.cube.rot.y += 30;
|
||||
}
|
||||
// Draw Floor
|
||||
if (0)
|
||||
@@ -376,7 +409,7 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
|
||||
tape_run(tb_slice(tb));// Fire off the tape.
|
||||
|
||||
// C-side state (pa->used) has already been updated by the tape!
|
||||
smem.floor.rot.y += 5;
|
||||
// smem.floor.rot.y += 5;
|
||||
}
|
||||
// --- TAPE DIAGNOSTICS ---
|
||||
if (0)
|
||||
@@ -402,22 +435,27 @@ int main(void)
|
||||
smem = (SMemory){0};
|
||||
smem.scratchpad = C_(U4_V, 0x1F800000);
|
||||
smem.primitives.used = 0;
|
||||
ent_cube128_init(& smem.cube.verts, & smem.cube.faces); {
|
||||
Ent_Cube* cube = & smem.cube;
|
||||
cube->rot = v3s2(0, 0, 0);
|
||||
// cube->pos = v3s4(0, 0, 900);
|
||||
cube->scale = v3s4_fp_one();
|
||||
cube->accel = v3s4(0, 1, 0);
|
||||
cube->pos = v3s4(0, -400, 1800);
|
||||
{
|
||||
ent_cube128_init(& smem.cube.verts, & smem.cube.faces); {
|
||||
Ent_Cube* cube = & smem.cube;
|
||||
cube->rot = v3s2(0, 0, 0);
|
||||
// cube->pos = v3s4(0, 0, 900);
|
||||
cube->scale = v3s4_fp_one();
|
||||
cube->accel = v3s4(0, 1, 0);
|
||||
cube->pos = v3s4(0, -400, 1800);
|
||||
}
|
||||
ent_floor_init(& smem.floor.verts, & smem.floor.faces); {
|
||||
Ent_Floor* floor = & smem.floor;
|
||||
floor->rot = v3s2(0, 0, 0);
|
||||
floor->pos = v3s4(0, 450, 1800);
|
||||
floor->scale = v3s4_fp_one();
|
||||
}
|
||||
}
|
||||
ent_floor_init(& smem.floor.verts, & smem.floor.faces); {
|
||||
Ent_Floor* floor = & smem.floor;
|
||||
floor->rot = v3s2(0, 0, 0);
|
||||
floor->pos = v3s4(0, 450, 1800);
|
||||
floor->scale = v3s4_fp_one();
|
||||
{
|
||||
// gknown gp_screen_init();
|
||||
gp_screen_init_c11(& smem.screen_buf, & smem.active_buf_id);
|
||||
pad_init(0);
|
||||
}
|
||||
// gknown gp_screen_init();
|
||||
gp_screen_init_c11(& smem.screen_buf, & smem.active_buf_id);
|
||||
while (1) {
|
||||
gknown S4* active_buf_id = & smem.active_buf_id;
|
||||
gknown U4* ordering_buf = r_(smem.ordering_tbl)[active_buf_id[0]];
|
||||
|
||||
@@ -43,25 +43,25 @@ typedef Struct_(DoubleBuffer) {
|
||||
#define ScreenRes_CenterX (ScreenRes_X >> 1)
|
||||
#define ScreenRes_CenterY (ScreenRes_Y >> 1)
|
||||
|
||||
DisplayEnv* displayenv_init(DisplayEnv* env, S4 x, S4 y, S4 w, S4 h) __asm__("SetDefDispEnv");
|
||||
DrawEnv* drawenv_init (DrawEnv* env, S4 x, S4 y, S4 w, S4 h) __asm__("SetDefDrawEnv");
|
||||
DisplayEnv* displayenv_init(DisplayEnv* env, S4 x, S4 y, S4 w, S4 h) asm("SetDefDispEnv");
|
||||
DrawEnv* drawenv_init (DrawEnv* env, S4 x, S4 y, S4 w, S4 h) asm("SetDefDrawEnv");
|
||||
|
||||
DisplayEnv* displayenv_put(DisplayEnv* env) __asm__("PutDispEnv");
|
||||
DrawEnv* drawenv_put (DrawEnv* env) __asm__("PutDrawEnv");
|
||||
DisplayEnv* displayenv_put(DisplayEnv* env) asm("PutDispEnv");
|
||||
DrawEnv* drawenv_put (DrawEnv* env) asm("PutDrawEnv");
|
||||
|
||||
U4 geom_init(void) __asm__("InitGeom");
|
||||
void geom_set_offset(U4 x, U4 y) __asm__("SetGeomOffset");
|
||||
void geom_set_screen(U4 h) __asm__("SetGeomScreen");
|
||||
U4 geom_init(void) asm("InitGeom");
|
||||
void geom_set_offset(U4 x, U4 y) asm("SetGeomOffset");
|
||||
void geom_set_screen(U4 h) asm("SetGeomScreen");
|
||||
|
||||
U4* orderingtbl_clear_reverse(U4* ot, U4 len) __asm__("ClearOTagR");
|
||||
U4* orderingtbl_clear_reverse(U4* ot, U4 len) asm("ClearOTagR");
|
||||
|
||||
U4 reset_graph(U4 mode) __asm__("ResetGraph");
|
||||
void set_display_enabled(U4 mask) __asm__("SetDispMask");
|
||||
U4 reset_graph(U4 mode) asm("ResetGraph");
|
||||
void set_display_enabled(U4 mask) asm("SetDispMask");
|
||||
|
||||
U4 draw_sync(U4 mode) __asm__("DrawSync");
|
||||
U4 vsync(U4 mode) __asm__("VSync");
|
||||
U4 draw_sync(U4 mode) asm("DrawSync");
|
||||
U4 vsync(U4 mode) asm("VSync");
|
||||
|
||||
void draw_orderingtbl(U4* buf) __asm__("DrawOTag");
|
||||
void draw_orderingtbl(U4* buf) asm("DrawOTag");
|
||||
|
||||
typedef Struct_(Tile) {
|
||||
U4 tag;
|
||||
@@ -74,16 +74,16 @@ typedef Struct_(Tile) {
|
||||
Linear Algebra
|
||||
*/
|
||||
|
||||
M3_S2* m3s2_rotation (V3_S2* vec, M3_S2* mat) __asm__("RotMatrix");
|
||||
M3_S2* m3s2_translation(M3_S2* mat, V3_S4* vec) __asm__("TransMatrix");
|
||||
M3_S2* m3s2_scale (M3_S2* mat, V3_S4* vec) __asm__("ScaleMatrix");
|
||||
M3_S2* m3s2_rotation (V3_S2* vec, M3_S2* mat) asm("RotMatrix");
|
||||
M3_S2* m3s2_translation(M3_S2* mat, V3_S4* vec) asm("TransMatrix");
|
||||
M3_S2* m3s2_scale (M3_S2* mat, V3_S4* vec) asm("ScaleMatrix");
|
||||
|
||||
// Rotation, Translation, Perspective
|
||||
|
||||
S4 rtp_v3s2_raw(V3_S2* vec, S4* xy, S4* pp, S4* flag) __asm__("RotTransPers");
|
||||
S4 rtp_v3s2_raw(V3_S2* vec, S4* xy, S4* pp, S4* flag) asm("RotTransPers");
|
||||
FI_ S4 rtp_v3s2(V3_S2* vec, V2_S2* xy, A2_S2* pp, S4* flag) { return rtp_v3s2_raw(vec, C_(S4*R_, & xy->x), C_(S4*R_, pp), r_(flag)); }
|
||||
|
||||
S4 rtp_avg_nclip_a3_v3s2_raw(V3_S2* v0, V3_S2* v1, V3_S2* v2, S4* xy1, S4* xy2, S4* xy3, S4* pp, S4* otz, S4* flag) __asm__("RotAverageNclip3");
|
||||
S4 rtp_avg_nclip_a3_v3s2_raw(V3_S2* v0, V3_S2* v1, V3_S2* v2, S4* xy1, S4* xy2, S4* xy3, S4* pp, S4* otz, S4* flag) asm("RotAverageNclip3");
|
||||
FI_ S4 rtp_avg_nclip_a3_v3s2(
|
||||
V3_S2* v0, V3_S2* v1, V3_S2* v2,
|
||||
V2_S2* xy0, V2_S2* xy1, V2_S2* xy2,
|
||||
@@ -96,7 +96,7 @@ FI_ S4 rtp_avg_nclip_a3_v3s2(
|
||||
);
|
||||
}
|
||||
|
||||
S4 rtp_avg_nclip_a4_v3s2_raw(V3_S2* v0, V3_S2* v1, V3_S2* v2, V3_S2* v3, S4* xy1, S4* xy2, S4* xy3, S4* xy4, S4* pp, S4* otz, S4* flag) __asm__("RotAverageNclip4");
|
||||
S4 rtp_avg_nclip_a4_v3s2_raw(V3_S2* v0, V3_S2* v1, V3_S2* v2, V3_S2* v3, S4* xy1, S4* xy2, S4* xy3, S4* xy4, S4* pp, S4* otz, S4* flag) asm("RotAverageNclip4");
|
||||
FI_ S4 rtp_avg_nclip_a4_v3s2(
|
||||
V3_S2* v0, V3_S2* v1, V3_S2* v2, V3_S2* v3,
|
||||
V2_S2* xy0, V2_S2* xy1, V2_S2* xy2, V2_S2* xy3,
|
||||
@@ -109,8 +109,8 @@ FI_ S4 rtp_avg_nclip_a4_v3s2(
|
||||
);
|
||||
}
|
||||
|
||||
void gte_matrix_set_rotation (M3_S2* mat) __asm__("SetRotMatrix");
|
||||
void gte_matrix_set_translation(M3_S2* mat) __asm__("SetTransMatrix");
|
||||
void gte_matrix_set_rotation (M3_S2* mat) asm("SetRotMatrix");
|
||||
void gte_matrix_set_translation(M3_S2* mat) asm("SetTransMatrix");
|
||||
|
||||
enum {
|
||||
fp_one = (1 << 12),
|
||||
|
||||
@@ -16,6 +16,49 @@
|
||||
|
||||
#pragma region Baked Atoms
|
||||
|
||||
enum {
|
||||
R_PadState = R_T4 atom_reg atom_type(U4),
|
||||
R_PadSignal = R_T0 atom_reg atom_type(U4),
|
||||
R_CubeRot = R_T1 atom_reg atom_type(V3_S2*),
|
||||
R_FloorRot = R_T2 atom_reg atom_type(V3_S2*),
|
||||
};
|
||||
typedef Struct_(Binds_PadInputDemo) {
|
||||
U4 pad_state;
|
||||
V3_S2* cube_rot;
|
||||
V3_S2* floor_rot;
|
||||
};
|
||||
internal MipsAtom_(pad_input_demo) atom_info(atom_bind(Binds_PadInputDemo)
|
||||
, atom_reads(R_PadState, R_CubeRot, R_FloorRot)
|
||||
, atom_writes( R_CubeRot, R_FloorRot)
|
||||
) {
|
||||
load_word(R_PadState, R_TapePtr, O_(Binds_PadInputDemo,pad_state)),
|
||||
load_word(R_CubeRot, R_TapePtr, O_(Binds_PadInputDemo,cube_rot)),
|
||||
load_word(R_FloorRot, R_TapePtr, O_(Binds_PadInputDemo,floor_rot)),
|
||||
add_ui_self( R_TapePtr, S_(Binds_PadInputDemo)),
|
||||
|
||||
and_i(R_PadSignal, R_PadState, pad0_(Pad_Left)),
|
||||
branch_le_zero(R_PadSignal, atom_offset(pad_left, exit_pad_left)),
|
||||
load_half( R_T5, R_CubeRot, O_(V3_S2,y)), // BD-Slot occupied
|
||||
load_half( R_T6, R_FloorRot, O_(V3_S2,y)),
|
||||
add_si( R_T5, R_T5, 30),
|
||||
add_si( R_T6, R_T6, 5),
|
||||
store_half(R_T5, R_CubeRot, O_(V3_S2,y)),
|
||||
store_half(R_T6, R_FloorRot, O_(V3_S2,y)),
|
||||
atom_label(exit_pad_left)
|
||||
|
||||
and_i(R_PadSignal, R_PadState, pad0_(Pad_Right)),
|
||||
branch_le_zero(R_PadSignal, atom_offset(pad_right, exit_pad_right)),
|
||||
load_half( R_T5, R_CubeRot, O_(V3_S2,y)), // BD-Slot occupied
|
||||
load_half( R_T6, R_FloorRot, O_(V3_S2,y)),
|
||||
add_si( R_T5, R_T5, -30),
|
||||
add_si( R_T6, R_T6, -5),
|
||||
store_half(R_T5, R_CubeRot, O_(V3_S2,y)),
|
||||
store_half(R_T6, R_FloorRot, O_(V3_S2,y)),
|
||||
atom_label(exit_pad_right)
|
||||
|
||||
mac_yield(),
|
||||
};
|
||||
|
||||
typedef Struct_(Binds_CubeTri) {
|
||||
U4 PrimCursor;
|
||||
V4_S2* FaceCursor;
|
||||
@@ -116,20 +159,22 @@ MipsAtom_(floor_f3_face) atom_info(atom_phase(floor_f3)
|
||||
/* Culling (Branch forward if Backface) */
|
||||
gte_mv_from_data_r(R_T0, C2_MAC0),
|
||||
nop, branch_le_zero(R_T0, atom_offset(culling, floor_f3_face_exit)), nop, // required gte -> cpu load-delay slot.
|
||||
/* Format Primitive */
|
||||
mac_gte_store_f3(),
|
||||
/* Format Primitive */
|
||||
mac_gte_store_f3(),
|
||||
|
||||
/* Calculate Depth */
|
||||
gte_avg_sort_z3,
|
||||
gte_mv_from_data_r(R_T1, C2_OTZ),
|
||||
/* Bounds Check OTZ < 2048 (Branch forward to skip insertion) */
|
||||
add_ui( R_AT, R_0, OrderingTbl_Len),
|
||||
set_lt_u( R_AT, R_T1, R_AT),
|
||||
branch_equal(R_AT, R_0, atom_offset(bounds_chk, floor_f3_face_exit)), nop,
|
||||
mac_format_f3_color(0xFF, 0xFF, 0xFF), // RGB-form (R=FF, G=FF, B=FF = white)
|
||||
mac_insert_ot_tag_f3(), /* Insert into Ordering Table Linked List */
|
||||
add_ui_self(R_PrimCursor, S_(Poly_F3)), /* Advance Prim Cursor (5 words) */
|
||||
// Note(Ed): No bounds checking, should be checked before atom runs.
|
||||
/* Calculate Depth */
|
||||
gte_avg_sort_z3,
|
||||
gte_mv_from_data_r(R_T1, C2_OTZ),
|
||||
/* Bounds Check OTZ < 2048 (Branch forward to skip insertion) */
|
||||
add_ui( R_AT, R_0, OrderingTbl_Len),
|
||||
set_lt_u( R_AT, R_T1, R_AT),
|
||||
branch_equal(R_AT, R_0, atom_offset(bounds_chk, floor_f3_face_exit)), nop,
|
||||
mac_format_f3_color(0xFF, 0xFF, 0xFF), // RGB-form (R=FF, G=FF, B=FF = white)
|
||||
mac_insert_ot_tag_f3(), /* Insert into Ordering Table Linked List */
|
||||
add_ui_self(R_PrimCursor, S_(Poly_F3)), /* Advance Prim Cursor (5 words) */
|
||||
// Note(Ed): No bounds checking, should be checked before atom runs.
|
||||
// end: branch(bounds_chk)
|
||||
// end: branch(culling)
|
||||
|
||||
/* Advance Input Cursor & Yield (Both branch targets land here) */
|
||||
atom_label(floor_f3_face_exit)
|
||||
|
||||
+262
-1
@@ -193,7 +193,7 @@ M.DWARF_LINE_OPS = {
|
||||
DW_LNE_set_address = 2, -- spec: §6.2.5.3
|
||||
-- Standard opcode header (§6.2.5.1)
|
||||
-- opcode_base + line_range are 1-byte header fields; hex so they map
|
||||
-- directly to their position in the line-program header byte sequence.
|
||||
-- directly to the line-program header byte sequence.
|
||||
-- line_base stays signed decimal (=-5) since 0xFB obscures the spec semantics.
|
||||
opcode_base = 0x0D,
|
||||
line_base = -5,
|
||||
@@ -204,6 +204,44 @@ M.DWARF_LINE_OPS = {
|
||||
set_address_payload_size = 0x05, -- size = sub_opcode(1) + addr(4)
|
||||
}
|
||||
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- DWARF5 .debug_line (per DWARF5 spec §6.2.4 — Line Number Program Header)
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- All offsets are zero-based wire offsets from the start of the unit body
|
||||
-- (i.e. AFTER unit_length has been read and unit_length bytes skipped past unit_length's 4 bytes).
|
||||
--
|
||||
-- The DWARF3/4 line-program format differs:
|
||||
-- - It omits `address_size` (DWARF3 §6.2.4) + `segment_selector_size` (DWARF5 §6.2.4).
|
||||
-- - It uses null-terminated string lists for `include_directories` + `file_names`
|
||||
-- (vs. DWARF5's format_count + fields-list shape).
|
||||
-- These are documented inline at each parse site in read_line_unit_file_table below.
|
||||
|
||||
--- spec: DWARF5 spec §6.2.4 (Line Number Program Header — version >= 5)
|
||||
M.DWARF5_DEBUG_LINE = {
|
||||
-- Header fields (zero-based, AFTER unit_length has been read).
|
||||
version_offset_post_il = 0x00, -- 2-byte LE; expected = 5
|
||||
addr_size_offset = 0x02, -- 1 byte; expected = 4
|
||||
seg_size_offset = 0x03, -- 1 byte; expected = 0
|
||||
header_length_offset = 0x04, -- 4-byte LE; length of program-header content that follows
|
||||
program_header_start = 0x08, -- first byte of program-header content (after the 8 fixed bytes)
|
||||
|
||||
-- Per-form byte widths (used when reading directory / file-name entries).
|
||||
form_addr_bytes = 0x04, -- DW_FORM_addr (32-bit) | DW_FORM_data4
|
||||
form_strp_bytes = 0x04, -- DW_FORM_line_strp / DW_FORM_strp / DW_FORM_strp_sup
|
||||
form_data16_bytes = 0x10, -- DW_FORM_data16 (MD5)
|
||||
|
||||
-- DWARF5 form codes (subset used in line-program directory + file tables).
|
||||
form_line_strp = 0x1A, -- DWARF5 §7.5.6 — DW_FORM_line_strp (4-byte offset into .debug_line_str)
|
||||
form_string = 0x08, -- DWARF4-compatible fallback (inline null-terminated; not in .debug_line_str)
|
||||
form_udata = 0x0F, -- DW_FORM_udata (ULEB)
|
||||
form_data16 = 0x18, -- DW_FORM_data16 (16-byte MD5; gcc emits this for split debug info)
|
||||
|
||||
-- DWARF5 content-tag codes (DW_LNCT_* from §6.2.4.1 + §6.2.4.2).
|
||||
lnct_path = 0x01,
|
||||
lnct_directory_index = 0x02,
|
||||
lnct_md5 = 0x05, -- gcc with MD5 in file name table (rare)
|
||||
}
|
||||
|
||||
-- ════════════════════════════════════════════════════════════════════════════
|
||||
-- I/O helpers: little-endian byte read/write
|
||||
-- ════════════════════════════════════════════════════════════════════════════
|
||||
@@ -787,6 +825,229 @@ function M.sleb128_size(n)
|
||||
return bytes
|
||||
end
|
||||
|
||||
-- ════════════════════════════════════════════════════════════════════════════
|
||||
-- DWARF5 line-program file-table reader
|
||||
-- ════════════════════════════════════════════
|
||||
|
||||
--- Read every line-program unit in `.debug_line` and produce one entry per file across all units.
|
||||
--- Returns three parallel maps keyed by 1-based file index.
|
||||
---
|
||||
--- Wire format notes:
|
||||
--- * The `.debug_line` section may contain MULTIPLE line-program units
|
||||
--- File indices are 1-based, **per unit**; we concatenate all units and the index ranges from 1..N₁ in unit 1, N₁+1..N₁+N₂ in unit 2, etc.
|
||||
--- Per-unit indices (the way gcc emits them, and the way `DW_LNS_set_file` references them in the line program)
|
||||
--- are returned via the `basename_to_index` map only when the unit boundary happens to align with the metaprogram's per-atom
|
||||
--- `inv.call_file` (true today for hello_joypad — the C unit is the LAST unit, and atom-side file indices fit 1-based).
|
||||
--- * Per spec, the `.debug_line_str` section (DWARF5 §7.5.6) holds the strings referenced by `DW_FORM_line_strp`.
|
||||
--- The legacy DWARF3 format embeds strings directly with null terminators. This helper handles BOTH.
|
||||
--- * File entries may have multiple forms (gcc -gdwarf-5 with `DW_LNCT_directory_index`
|
||||
--- emits 2 forms: path + dir_index). The helper supports:
|
||||
--- - DW_FORM_line_strp (DWARF5; offset into .debug_line_str)
|
||||
--- - DW_FORM_string (DWARF4-compat; inline null-terminated in .debug_line)
|
||||
--- - DW_FORM_udata (ULEB128)
|
||||
--- - DW_FORM_data16 (16-byte MD5; ignored — skip the form's bytes)
|
||||
--- * Symlink-canonicalisation: each path's `paths[i]` is stored verbatim from the wire
|
||||
--- (mixed `/` and `\` accepted; the basename is taken via the last path separator). Caller normalises as needed.
|
||||
---
|
||||
--- Behavior on failure: writes to stderr and returns nil.
|
||||
--- Helpers consumed by `passes/dwarf_injection.lua::init_file_index_lookup(elf_path)` calls this once at pass start to populate the module-level `basename_to_index` map;
|
||||
--- downstream `resolve_provenance_file_index(path)` consumers
|
||||
--- (which replaced the former hardcoded `ATOM_SOURCE_FILE_INDEX` + `PROVENANCE_BASENAME_TO_FILE_INDEX` table per `conductor/tracks/dwarf_file_index_lookup_20260731/`)
|
||||
--- consult the map directly.
|
||||
---
|
||||
--- @param elf_path string -- absolute path to the post-link ELF (typically the gcc-emitted `.elf` BEFORE dwarf_injector's splice;
|
||||
--- both shapes work since the splice preserves `.debug_line`)
|
||||
--- @return table|nil, table|nil, table|nil
|
||||
--- basename_to_index: { [basename] = 1-based-per-unit-file-index, ... }
|
||||
--- basenames: { [1-based-per-unit-file-index] = basename, ... }
|
||||
--- paths: { [1-based-per-unit-file-index] = full path (mixed slashes), ... }
|
||||
function M.read_line_unit_file_table(elf_path)
|
||||
local sections = M.read_elf_sections(elf_path, { ".debug_line", ".debug_line_str" })
|
||||
local line = sections[".debug_line"]
|
||||
local lstr = sections[".debug_line_str"] or ""
|
||||
if not line or line == "" then
|
||||
io.stderr:write("[elf_dwarf.read_line_unit_file_table] no .debug_line section in: " .. tostring(elf_path) .. "\n")
|
||||
return nil
|
||||
end
|
||||
|
||||
local basenames = {}
|
||||
local basename_to_index = {}
|
||||
local paths = {}
|
||||
|
||||
--- Read one form-code's bytes from `buf` at position `p` according to `form`.
|
||||
--- Returns (value, after) where `value` is:
|
||||
--- * the resolved string (DW_FORM_line_strp / DW_FORM_string)
|
||||
--- * the ULEB128 number (DW_FORM_udata)
|
||||
--- * nil + skip-bytes (DW_FORM_data16; we don't surface the MD5)
|
||||
local function read_form(buf, lstr_buf, p, form)
|
||||
if form == M.DWARF5_DEBUG_LINE.form_line_strp then
|
||||
local strp = M.read_u32_le(buf, p)
|
||||
local end_pos = lstr_buf:find("\0", strp + 1, true) or (#lstr_buf + 1)
|
||||
return lstr_buf:sub(strp + 1, end_pos - 1), p + M.DWARF5_DEBUG_LINE.form_strp_bytes
|
||||
elseif form == M.DWARF5_DEBUG_LINE.form_string then
|
||||
local nul = buf:find("\0", p + 1, true) or (#buf + 1)
|
||||
return buf:sub(p + 1, nul - 1), nul
|
||||
elseif form == M.DWARF5_DEBUG_LINE.form_udata then
|
||||
local v, after = M.read_uleb128_at(buf, p)
|
||||
return v, after
|
||||
elseif form == M.DWARF5_DEBUG_LINE.form_data16 then
|
||||
return nil, p + M.DWARF5_DEBUG_LINE.form_data16_bytes
|
||||
else
|
||||
-- Unsupported form in a directory/file-table entry: best-effort skip.
|
||||
-- We do NOT stderr-write because the crt0.s DWARF5 line unit (gcc-as emitted) uses DW_FORM_addr (0x01) for what is effectively a path entry,
|
||||
-- which is non-standard.
|
||||
-- The C-unit's DWARF3 paths are read via the parallel DWARF3 path and never see this error.
|
||||
-- Callers should consult `basename_to_index` for the paths they care about and ignore this unit if it produced none.
|
||||
return nil, p
|
||||
end
|
||||
end
|
||||
|
||||
--- Parse one DWARF-version-3-style unit (DWARF3/4 line program; gcc default in the PS1 toolchain still emits DWARF3 for line programs in `-g` mode).
|
||||
--- Layout: null-terminated directory list, then path(null) + dir_idx(ULEB) + time(ULEB) + size(ULEB) file entries terminated by an empty null.
|
||||
--- `content_start` = zero-based wire offset of the first byte of program-header content (after version + header_length fields).
|
||||
--- @return unit_basenames { [idx_in_unit_1_based] = basename }
|
||||
--- @return unit_paths { [idx_in_unit_1_based] = full path }
|
||||
local function parse_dwarf3_unit(buf, content_start, body_end)
|
||||
local up = content_start
|
||||
-- 5 fixed bytes: min_insn, default_is, line_base (signed), line_range, opcode_base
|
||||
up = up + 5
|
||||
local opcode_base = buf:byte(content_start + 5)
|
||||
up = up + (opcode_base - 1) -- std_opcode_lengths
|
||||
local dirs = {}
|
||||
while up < body_end do
|
||||
local nul = buf:find("\0", up + 1, true) or (body_end + 1)
|
||||
if nul > body_end then break end
|
||||
local len = nul - up - 1
|
||||
if len == 0 then up = nul break end
|
||||
dirs[#dirs + 1] = buf:sub(up + 1, nul - 1)
|
||||
up = nul
|
||||
end
|
||||
local unit_basenames = {}
|
||||
local unit_paths = {}
|
||||
while up < body_end do
|
||||
local nul = buf:find("\0", up + 1, true) or (body_end + 1)
|
||||
if nul > body_end or nul == up + 1 then up = nul break end
|
||||
local path = buf:sub(up + 1, nul - 1)
|
||||
up = nul
|
||||
local didx, up_next = M.read_uleb128_at(buf, up); up = up_next
|
||||
local _time, up_next2 = M.read_uleb128_at(buf, up); up = up_next2
|
||||
local _size, up_next3 = M.read_uleb128_at(buf, up); up = up_next3
|
||||
local idx = #unit_basenames + 1
|
||||
local bs = path:match("[^/\\]+$") or path
|
||||
unit_paths[idx] = path
|
||||
unit_basenames[idx] = bs
|
||||
dirs[1] = dirs[1] or "" -- safety: gcc emits "" sentinel dir at 0
|
||||
if didx > 0 and dirs[didx] then
|
||||
unit_paths[idx] = dirs[didx] .. "/" .. path
|
||||
end
|
||||
end
|
||||
return unit_basenames, unit_paths
|
||||
end
|
||||
|
||||
--- Parse one DWARF-version-5-style unit (DWARF5 line program; used by modern gcc with `-gdwarf-5`).
|
||||
--- `content_start` is the first byte of program-header content (after the 8 fixed bytes version+addr_size+seg_size+header_length).
|
||||
--- @return same shape as parse_dwarf3_unit
|
||||
local function parse_dwarf5_unit(buf, lstr_buf, content_start, body_end)
|
||||
local up = content_start
|
||||
-- 6 fixed bytes: min_insn, max_ops_per_insn, default_is, line_base, line_range, opcode_base
|
||||
up = up + 6
|
||||
local opcode_base = buf:byte(content_start + 6)
|
||||
up = up + (opcode_base - 1) -- std_opcode_lengths
|
||||
-- directories
|
||||
local dir_format_count, after = M.read_uleb128_at(buf, up); up = after
|
||||
local dir_formats = {}
|
||||
for i = 1, dir_format_count do
|
||||
local f, a2 = M.read_uleb128_at(buf, up); up = a2
|
||||
dir_formats[i] = f
|
||||
end
|
||||
local dir_count, a3 = M.read_uleb128_at(buf, up); up = a3
|
||||
local dirs = {}
|
||||
for i = 1, dir_count do
|
||||
local combined = ""
|
||||
for j = 1, dir_format_count do
|
||||
local v, a4 = read_form(buf, lstr_buf, up, dir_formats[j])
|
||||
up = a4
|
||||
if j == 1 and type(v) == "string" then combined = v end
|
||||
end
|
||||
dirs[i] = combined
|
||||
end
|
||||
-- file names
|
||||
local file_format_count, after2 = M.read_uleb128_at(buf, up); up = after2
|
||||
local file_formats = {}
|
||||
for i = 1, file_format_count do
|
||||
local f, a2 = M.read_uleb128_at(buf, up); up = a2
|
||||
file_formats[i] = f
|
||||
end
|
||||
local file_count, a3 = M.read_uleb128_at(buf, up); up = a3
|
||||
local unit_basenames = {}
|
||||
local unit_paths = {}
|
||||
for i = 1, file_count do
|
||||
local combined = ""
|
||||
local didx = 0
|
||||
for j = 1, file_format_count do
|
||||
local v, a4 = read_form(buf, lstr_buf, up, file_formats[j])
|
||||
up = a4
|
||||
if j == 1 and type(v) == "string" then combined = v end
|
||||
if j == 2 and type(v) == "number" then didx = v end
|
||||
end
|
||||
local idx = #unit_basenames + 1
|
||||
local bs = combined:match("[^/\\]+$") or combined
|
||||
unit_paths[idx] = combined
|
||||
unit_basenames[idx] = bs
|
||||
if didx > 0 and dirs[didx] then
|
||||
unit_paths[idx] = dirs[didx] .. "/" .. combined
|
||||
end
|
||||
end
|
||||
return unit_basenames, unit_paths
|
||||
end
|
||||
|
||||
--- Walk every line-program unit in the section.
|
||||
local p = 0
|
||||
local section_end = #line
|
||||
while p + 4 <= section_end do
|
||||
local unit_length = M.read_u32_le(line, p)
|
||||
if unit_length == 0xFFFFFFFF then
|
||||
io.stderr:write("[elf_dwarf.read_line_unit_file_table] 64-bit DWARF (initial-length 0xFFFFFFFF); not supported\n")
|
||||
return nil
|
||||
end
|
||||
local body_start = p + 4
|
||||
local body_end = p + 4 + unit_length
|
||||
if body_end > section_end then break end
|
||||
local version = M.read_u16_le(line, body_start)
|
||||
local unit_basenames, unit_paths
|
||||
if version >= 5 then
|
||||
-- DWARF5 header: version(2) + addr_size(1) + seg_size(1) + header_length(4) + content
|
||||
local header_length_offset = body_start + 6 -- past version(2) + addr_size(1) + seg_size(1) - wait that's wrong; past hdr len is at +6
|
||||
local content_start = body_start + 8 -- past version(2) + addr_size(1) + seg_size(1) + header_length(4)
|
||||
unit_basenames, unit_paths = parse_dwarf5_unit(line, lstr, content_start, body_end)
|
||||
elseif version >= 2 then
|
||||
-- DWARF2/3/4 header: version(2) + header_length(4) + content
|
||||
local content_start = body_start + 6 -- past version(2) + header_length(4)
|
||||
unit_basenames, unit_paths = parse_dwarf3_unit(line, content_start, body_end)
|
||||
else
|
||||
io.stderr:write(string.format("[elf_dwarf.read_line_unit_file_table] unsupported DWARF version %d (offset 0x%x)\n", version, p))
|
||||
p = body_end
|
||||
goto continue
|
||||
end
|
||||
-- Per-unit 1-based file indices are aligned with `inv.call_file` values because the metaprogram emits `DW_LNS_set_file` with the per-unit index.
|
||||
-- When multiple units are present (crt0.s + C unit), the per-unit index in each unit matches the metaprogram's intent (gcc always sets file in unit-local terms).
|
||||
-- We therefore store directly without global re-indexing; the caller is responsible for knowing which unit the file-index applies to.
|
||||
-- For DWARF3 (C unit is the unit that matters for atom line tables), this matches.
|
||||
-- For DWARF5 (crt0.s + C unit), each carries its own per-unit file-table map;
|
||||
-- the atom-side DW_LNS_set_file(N) refers to the C unit's indices, NOT crt0.s's.
|
||||
-- Since the C unit is the one with full include_directories + 12 entries, we can use it directly.
|
||||
for idx, bs in pairs(unit_basenames) do
|
||||
basenames[idx] = bs
|
||||
paths[idx] = unit_paths[idx]
|
||||
basename_to_index[bs] = idx
|
||||
end
|
||||
p = body_end
|
||||
::continue::
|
||||
end
|
||||
|
||||
return basename_to_index, basenames, paths
|
||||
end
|
||||
|
||||
-- ════════════════════════════════════════════════════════════════════════════
|
||||
-- I/O helpers: atoms source-map + native directory glob
|
||||
-- ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -71,9 +71,15 @@ local DW_LNE_set_address = DWARF_LINE_OPS.DW_LNE_set_address
|
||||
local DW_RLE_end_of_list = DWARF5_RNGLISTS.end_of_list
|
||||
local DW_RLE_start_length = DWARF5_RNGLISTS.start_length
|
||||
|
||||
-- File index 11 in the existing main line unit is hello_gte_tape.c.
|
||||
-- The injector extends that unit rather than appending an unreferenced unit.
|
||||
local ATOM_SOURCE_FILE_INDEX = 11
|
||||
-- File-index lookup for the existing main line unit (Unit 2).
|
||||
-- Populated at pass start by `init_file_index_lookup(elf_path)` from the runtime ELF (see `elf_dwarf.read_line_unit_file_table`).
|
||||
-- The hardcoded indices and the `PROVENANCE_BASENAME_TO_FILE_INDEX` table that previously lived here were retired in `conductor/tracks/dwarf_file_index_lookup_20260731/`
|
||||
-- (red of the
|
||||
-- `TODO(Ed): Remove this HARDCODE` from line 156); the runtime lookup reads the
|
||||
-- actual gcc-emitted `.debug_line` file table instead.
|
||||
local _file_index_by_basename = nil -- [basename] = 1-based line-table file index
|
||||
local _file_path_by_index = nil -- [1-based index] = full source path (diagnostics / future consumers)
|
||||
local _default_atom_source_index = nil -- any valid index used in opaque-row fallbacks
|
||||
|
||||
-- RR_<R_Name> debug-visible variables come from the merged register_alias_registry filtered to aliases whose code is a valid MIPS GPR 0..31
|
||||
-- (see collect_per_source_registries + by_alias in build_inserted_children).
|
||||
@@ -98,9 +104,8 @@ local ABBREV_INLINED_SUBROUTINE = 0x6C -- 108: DW_TAG_inlined_subroutine with
|
||||
-- (each field transitions from tape memory to GPR at load_pc + 8 = MIPS I load-delay slot boundary).
|
||||
local ABBREV_BIND_VAR_LOCLIST = 0x6D -- 109: DW_TAG_variable no children + DW_AT_type = ref4 + DW_AT_location = sec_offset
|
||||
-- Typed-view pointer_type (for the synthetic V4_S2* / V3_S2* / U4* / void* chains).
|
||||
-- MUST be a fresh abbrev code in the appended table — emitting uleb128(9) collides with GCC's
|
||||
-- existing abbrev 9 (a pointer_type that carries DW_AT_byte_size + DW_AT_type), so gdb misparses
|
||||
-- our 4-byte ref4 as (byte_size, type[0..2]) and lands the cursor mid-attribute.
|
||||
-- MUST be a fresh abbrev code in the appended table — emitting uleb128(9) collides with GCC's existing abbrev 9
|
||||
-- (a pointer_type that carries DW_AT_byte_size + DW_AT_type), so gdb misparses our 4-byte ref4 as (byte_size, type[0..2]) and lands the cursor mid-attribute.
|
||||
local ABBREV_TYPED_VIEW_POINTER = 0x6E -- 110: DW_TAG_pointer_type no children + DW_AT_type = ref4 (typed-view / U4 / void chain)
|
||||
|
||||
-- DWARF5 §7.7.3 loclist opcodes.
|
||||
@@ -153,46 +158,66 @@ local DW_AT_inline = 0x20 -- DWARF5 §7.7.1: DW_AT_inline (used by a
|
||||
local DW_AT_decl_file = 0x3A -- DWARF5 §7.7.1: DW_AT_decl_file (1-based file index into the CU's file table)
|
||||
local DW_AT_decl_line = 0x3B -- DWARF5 §7.7.1: DW_AT_decl_line
|
||||
|
||||
-- TODO(Ed): Remove this HARDCODE
|
||||
-- File index lookup table for the existing main line unit (Unit 2).
|
||||
-- Provenance paths come back with mixed slashes; we normalize to basename and look up against the line unit's actual file table.
|
||||
-- Current scope has two provenance basenames: hello_gte_tape.c (the atom's call site) and lottes_tape.h (the component definition).
|
||||
-- Both live in the existing gcc-generated line unit; their 1-based indices are stable across rebuilds because the include order
|
||||
-- in code/gte_hello/hello_gte.c determines the unit's file table.
|
||||
-- gcc only adds a file to the line table when it has actual line-number entries;
|
||||
-- headers that are pure macros/typedefs (dsl.h, memory.h, math.h, mips.h, gp.h, gte.h, etc.) never appear.
|
||||
-- lottes_tape.h is the FIRST include that emits line entries (MipsAtomComp_ declarations), so it is the FIRST entry after the primary file.
|
||||
local PROVENANCE_BASENAME_TO_FILE_INDEX = {
|
||||
["hello_joypad.tape.c"] = ATOM_SOURCE_FILE_INDEX, -- = 11
|
||||
["hello_gte.tape.c"] = ATOM_SOURCE_FILE_INDEX, -- = 11
|
||||
["lottes_tape.h"] = 2,
|
||||
}
|
||||
-- Replaced the hardcoded `ATOM_SOURCE_FILE_INDEX = 11` and the `PROVENANCE_BASENAME_TO_FILE_INDEX` table below with a runtime lookup
|
||||
-- (`init_file_index_lookup` + `resolve_provenance_file_index`) that reads the actual `.debug_line` file table from the post-link ELF.
|
||||
|
||||
--- Populate the module-level file-index lookup table from the `.debug_line` section of the post-link ELF pointed at by `elf_path`.
|
||||
--- This MUST be called exactly once at pass start (from `M.run`) before any `resolve_provenance_file_index` invocation;
|
||||
--- downstream callers handle a nil table as "no file info available; fall back to errors".
|
||||
---
|
||||
--- The lookup uses `elf_dwarf.read_line_unit_file_table` (which parses both DWARF3 and DWARF5 line-program units —
|
||||
--- the crt0.s assembler-side DWARF5 unit may emit non-standard form codes for paths and is intentionally skipped).
|
||||
--- @param elf_path string|nil
|
||||
local function init_file_index_lookup(elf_path)
|
||||
if not elf_path or elf_path == "" then return end
|
||||
local b2i, _basenames, paths = elf_dwarf.read_line_unit_file_table(elf_path)
|
||||
if type(b2i) ~= "table" or type(paths) ~= "table" then
|
||||
io.stderr:write("[dwarf_injection] read_line_unit_file_table returned no file table for: " .. tostring(elf_path) .. "\n")
|
||||
return
|
||||
end
|
||||
_file_index_by_basename = b2i
|
||||
_file_path_by_index = paths
|
||||
-- Pick any valid index for the opaque-row fallbacks at lines 466 + 570
|
||||
-- (both sites legitimately want "any file index"; gdb resolves whatever index we emit to whatever that file's line happens to be).
|
||||
for idx in pairs(paths) do
|
||||
_default_atom_source_index = idx
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
--- Resolve an absolute provenance path to the line-unit file index used by the emitting line program.
|
||||
--- Normalizes mixed `/` and `\` separators to a basename and looks it up against the known file table.
|
||||
--- Normalizes mixed `/` and `\` separators to a basename and looks it up against the runtime-computed file table populated by `init_file_index_lookup`.
|
||||
---
|
||||
--- Fails loudly on an unknown provenance basename: adding a new component source file requires extending
|
||||
--- `PROVENANCE_BASENAME_TO_FILE_INDEX` so the line-program emission contract stays explicit.
|
||||
--- Silent fallback to ATOM_SOURCE_FILE_INDEX would mask the new-file case by misattributing component rows to the atom's source file.
|
||||
--- @param path string -- absolute provenance path (e.g. "C:/.../lottes_thttps://www.youtube.com/watch?v=ORM4yLkdKx8ape.h" or "C:\\...\\lottes_tape.h")
|
||||
--- Fails loudly on an unknown provenance basename: adding a new component source file will produce a clear error message naming the missing basename and listing the .debug_line file table contents,
|
||||
--- so the user can either confirm the gcc include order, the unity-root, or the `.debug_line` file table contents.
|
||||
--- Silent fallback would mask the new-file case by misattributing component rows to an arbitrary source file.
|
||||
--- @param path string -- absolute provenance path (mixed slashes accepted)
|
||||
--- @return integer -- 1-based line-unit file index
|
||||
local function resolve_provenance_file_index(path)
|
||||
if _file_index_by_basename == nil then
|
||||
error("[dwarf_injection] resolve_provenance_file_index called before init_file_index_lookup. "
|
||||
.. "Is M.run being entered correctly (with --elf)?")
|
||||
end
|
||||
if path == nil or path == "" then
|
||||
error("[dwarf_injection] resolve_provenance_file_index: empty path")
|
||||
end
|
||||
-- Normalize backslashes → forward slashes (paths arrive with mixed separators from the provenance file: forward slashes from Lua's io.lines;
|
||||
-- backslashes if the input ever round-trips through Windows shell expansion).
|
||||
-- Normalize backslashes → forward slashes (paths arrive with mixed separators from the provenance file).
|
||||
local normalized = path:gsub("\\", "/")
|
||||
-- Take the last path component (the basename).
|
||||
local basename = normalized:match("([^/]+)$") or normalized
|
||||
local idx = PROVENANCE_BASENAME_TO_FILE_INDEX[basename]
|
||||
if idx == nil then
|
||||
error(string.format(
|
||||
"[dwarf_injection] resolve_provenance_file_index: unknown provenance basename '%s' (from '%s'). "
|
||||
.. "Extend PROVENANCE_BASENAME_TO_FILE_INDEX in passes/dwarf_injection.lua.",
|
||||
basename, path))
|
||||
local idx = _file_index_by_basename[basename]
|
||||
if idx ~= nil then return idx end
|
||||
-- Last-resort exact-path match (handles paths that don't reduce to a known basename).
|
||||
for i, p in pairs(_file_path_by_index) do
|
||||
if p and p:gsub("\\", "/") == normalized then return i end
|
||||
end
|
||||
return idx
|
||||
-- Build an error message listing the known basenames for fast diagnostics.
|
||||
local known = {}
|
||||
for k in pairs(_file_index_by_basename) do known[#known + 1] = k end
|
||||
table.sort(known)
|
||||
error(string.format("[dwarf_injection] resolve_provenance_file_index: unknown provenance basename '%s' (from '%s'). "
|
||||
.. "Known basenames in the .debug_line file table (%d): %s"
|
||||
, basename, path, #known, table.concat(known, ", ")))
|
||||
end
|
||||
|
||||
local DW_FORM_addr = 0x01
|
||||
@@ -463,7 +488,7 @@ local function build_atom_sequence(atom)
|
||||
if atom.debug_skip then
|
||||
return table.concat({
|
||||
set_address(atom.addr),
|
||||
set_file(ATOM_SOURCE_FILE_INDEX),
|
||||
set_file(resolve_provenance_file_index(atom.src_path)),
|
||||
advance_line(atom.entries[1].line - 1),
|
||||
negate_stmt(),
|
||||
copy_op(),
|
||||
@@ -510,10 +535,9 @@ local function build_atom_sequence(atom)
|
||||
-- * If the invocation's body has any NESTED invocations (parent_id == top_inv.id), the body's
|
||||
-- first content is the call_line of the earliest nested invocation (by start_pos).
|
||||
-- * Otherwise (only RAW words in the body), it's the line of the first raw word = body_lines[1].
|
||||
-- This is the value the multi-row PC's body_lines[1] row must reference for source-order display:
|
||||
-- `anc.body_lines[1]` is the line of the FIRST WORD (which for an outer whose body starts with a
|
||||
-- nested expansion is inside the inner's expansion = wrong for display purposes); `anc.body_first_line`
|
||||
-- is the body's first content line in the parent's source (= correct for display).
|
||||
-- This is the value the multi-row PC's body_lines[1] row must reference for source-order display: `anc.body_lines[1]` is the line of the FIRST WORD
|
||||
-- (which for an outer whose body starts with a nested expansion is inside the inner's expansion = wrong for display purposes);
|
||||
-- `anc.body_first_line` is the body's first content line in the parent's source (= correct for display).
|
||||
local body_first_line_of = {}
|
||||
for _, top_inv in ipairs(invs) do
|
||||
local earliest_nested_call_line = nil
|
||||
@@ -567,7 +591,7 @@ local function build_atom_sequence(atom)
|
||||
parts[#parts + 1] = copy_op()
|
||||
end
|
||||
|
||||
local call_file_idx = ATOM_SOURCE_FILE_INDEX
|
||||
local call_file_idx = resolve_provenance_file_index(atom.src_path)
|
||||
|
||||
-- --- Atom entry (idx 1) -------------------------------------------------
|
||||
local entry_1 = atom.entries[1]
|
||||
@@ -576,13 +600,11 @@ local function build_atom_sequence(atom)
|
||||
-- If atom entry 1 starts inside an invocation, walk the ancestry and emit a call-site row + (when applicable)
|
||||
-- a body_lines[1] row for every active ancestor. For a non-nested invocation this is just the one pair;
|
||||
-- for nested invocations this emits the outer call-site + body_lines[1] rows BEFORE the inner pair so the debugger displays
|
||||
-- the outer body line at the inner's first word
|
||||
-- (PROBLEM B fix).
|
||||
-- the outer body line at the inner's first word (PROBLEM B fix).
|
||||
--
|
||||
-- A marked OUTERMOST ancestor's body_lines[1] row is suppressed at this PC (the existing full-skip
|
||||
-- contract is preserved for the marked outer range); its call-site row IS still emitted as a
|
||||
-- statement. Marked INNER ancestors always emit their body_lines[1] row with is_stmt=false
|
||||
-- (the per-invocation `want_body = not inv.debug_skip` predicate).
|
||||
-- A marked OUTERMOST ancestor's body_lines[1] row is suppressed at this PC (the existing full-skip contract is preserved for the marked outer range);
|
||||
-- Its call-site row IS still emitted as a statement.
|
||||
-- Marked INNER ancestors always emit their body_lines[1] row with is_stmt=false (the per-invocation `want_body = not inv.debug_skip` predicate).
|
||||
if #entry_1_ancestry == 0 then
|
||||
-- RAW word at atom entry: single call-site row, always a statement target.
|
||||
emit_row(call_file_idx, entry_1.line, true)
|
||||
@@ -590,9 +612,8 @@ local function build_atom_sequence(atom)
|
||||
-- Atom starts in an invocation. Walk the ancestry outermost-first.
|
||||
-- Each ancestor emits one call-site row (statement) and one body_lines[1] row
|
||||
-- (statement iff unmarked; suppressed for marked outermost).
|
||||
-- The body_lines[1] row references body_first_line_of[anc.id] (= the body's first content
|
||||
-- line in the parent's source), NOT anc.body_lines[1] (= the line of the first WORD,
|
||||
-- which is wrong when the outer's body starts with a nested call).
|
||||
-- The body_lines[1] row references body_first_line_of[anc.id] (= the body's first content line in the parent's source),
|
||||
-- NOT anc.body_lines[1] (= the line of the first WORD, which is wrong when the outer's body starts with a nested call).
|
||||
for ai, anc in ipairs(entry_1_ancestry) do
|
||||
assert(anc.body_lines, "missing body_lines: emitter did not run emission-model")
|
||||
assert(anc.body_lines[1] ~= nil
|
||||
@@ -617,20 +638,18 @@ local function build_atom_sequence(atom)
|
||||
|
||||
if inv and idx == inv.start_pos + 1 then
|
||||
-- First word of the innermost active invocation (PROBLEM B fix — nested-display rule).
|
||||
-- Walk the active ancestry outermost-first; for each ancestor emit a call-site row
|
||||
-- (statement) + a body_lines[1] row. The inner-most invocation's call-site + body pair
|
||||
-- become the LAST two rows in the sequence. Marked outermost ancestors suppress their
|
||||
-- body_lines[1] row at this PC (the existing full-skip contract is preserved for the
|
||||
-- marked outer range); all OTHER ancestors emit body_lines[1] with is_stmt = not debug_skip.
|
||||
-- Walk the active ancestry outermost-first; for each ancestor emit a call-site row (statement) + a body_lines[1] row.
|
||||
-- The inner-most invocation's call-site + body pair become the LAST two rows in the sequence.
|
||||
-- Marked outermost ancestors suppress their body_lines[1] row at this PC (the existing full-skip contract is preserved for the marked outer range);
|
||||
-- all OTHER ancestors emit body_lines[1] with is_stmt = not debug_skip.
|
||||
--
|
||||
-- This re-emits the outer ancestor's call-site + body rows at the inner's first word PC
|
||||
-- for debugger context: source-level stepping now shows the outer body line (not the
|
||||
-- inner body line) when stepping into the inner. PROBLEM B fix.
|
||||
-- The body_lines[1] row references body_first_line_of[anc.id] (= the body's first content
|
||||
-- line in the parent's source), NOT anc.body_lines[1] (= the line of the first WORD,
|
||||
-- which is wrong when the outer's body starts with a nested call: gdb 12.1 picks the
|
||||
-- displayed line as the LAST row at the same PC in byte-stream order, so the disc=1 row's
|
||||
-- value matters for what's shown when stepping into the nested case).
|
||||
-- for debugger context: source-level stepping now shows the outer body line
|
||||
-- (not the inner body line) when stepping into the inner. PROBLEM B fix.
|
||||
-- The body_lines[1] row references body_first_line_of[anc.id] (= the body's first content line in the parent's source),
|
||||
-- NOT anc.body_lines[1] (= the line of the first WORD, which is wrong when the outer's body starts with a nested call:
|
||||
-- gdb 12.1 picks the displayed line as the LAST row at the same PC in byte-stream order,
|
||||
-- so the disc=1 row's value matters for what's shown when stepping into the nested case).
|
||||
local ancestry = ancestry_idx[idx]
|
||||
for ai, anc in ipairs(ancestry) do
|
||||
assert(anc.body_lines, "missing body_lines: emitter did not run emission-model")
|
||||
@@ -649,10 +668,9 @@ local function build_atom_sequence(atom)
|
||||
-- Subsequent body word of the innermost active invocation: `body_lines[k]` is indexed by the 1-based offset of this word inside the invocation.
|
||||
-- Both `idx` (1-based DWARF entry index) and `inv.start_pos` (0-based emitted-word position stamped at `emit_invoke_begin`) come from the same
|
||||
-- monotonic counter, so `idx - inv.start_pos` is exactly the 1-based k (the first word of the invocation has `idx == inv.start_pos + 1`, hence `k == 1`).
|
||||
-- atom_dbg_step_ux_20260725: `want_body = not inv.debug_skip`. The previous `want = not marked_idx[idx]`
|
||||
-- (which suppressed ALL body rows when any ancestor was marked) is replaced by the per-invocation
|
||||
-- predicate. Marked invocations emit non-statement body rows at every body word; unmarked
|
||||
-- invocations emit statement body rows.
|
||||
-- atom_dbg_step_ux_20260725: `want_body = not inv.debug_skip`.
|
||||
-- The previous `want = not marked_idx[idx]` (which suppressed ALL body rows when any ancestor was marked) is replaced by the per-invocation predicate.
|
||||
-- Marked invocations emit non-statement body rows at every body word; unmarked invocations emit statement body rows.
|
||||
assert(inv.body_lines, "missing body_lines: emitter did not run emission-model")
|
||||
local words_into = idx - inv.start_pos
|
||||
assert(inv.body_lines[words_into] ~= nil
|
||||
@@ -706,7 +724,9 @@ local function build_atom_table(corpus, addrs)
|
||||
local atoms_by_name = corpus.atoms_by_name or {}
|
||||
|
||||
-- Per-atom ingest. Returns nil if the atom is absent from the corpus; the caller skips it via the `if atom then ...` guard.
|
||||
local function ingest_atom(name, info)
|
||||
-- `src_path` is the absolute source path that declared this atom; the build_atom_table iteration below threads `src.path` through.
|
||||
-- This is consumed by `build_atom_sequence::set_file(...)` for opaque-row fallbacks + raw-word rows (atoms where no invocation ancestry exists).
|
||||
local function ingest_atom(name, info, src_path)
|
||||
local atom_record = atoms_by_name[name]
|
||||
if not atom_record then return nil end
|
||||
|
||||
@@ -732,6 +752,7 @@ local function build_atom_table(corpus, addrs)
|
||||
words = #word_events,
|
||||
entries = entries,
|
||||
debug_skip = atom_record.debug_skip == true,
|
||||
src_path = src_path or "",
|
||||
}
|
||||
|
||||
-- Consume invocation records from `atom.paths.invocations`. It is the single producer of per-invocation body_lines, per-invocation debug_skip,
|
||||
@@ -755,9 +776,26 @@ local function build_atom_table(corpus, addrs)
|
||||
end
|
||||
|
||||
local out = {}
|
||||
for name, info in pairs(addrs) do
|
||||
local atom = ingest_atom(name, info)
|
||||
if atom then out[#out + 1] = atom end
|
||||
-- Walk every source's atom list (which preserves source order + per-source src_path).
|
||||
-- Cross-ref with the nm symbol table; atoms absent from `addrs` are skipped (an atom
|
||||
-- declared in source but not emitted as a symbol is a metaprogram or atom-info bug, not
|
||||
-- a source-correlation bug — emit_no_emit would catch it upstream).
|
||||
for _, src in ipairs((corpus and corpus.source_order) or {}) do
|
||||
local src_path = src.path or ""
|
||||
for _, atom_rec in ipairs(((src.scan or {}).atoms) or {}) do
|
||||
local info = addrs[atom_rec.name or atom_rec.raw_name]
|
||||
if info then
|
||||
local atom = ingest_atom(atom_rec.name or atom_rec.raw_name, info, src_path)
|
||||
if atom then out[#out + 1] = atom end
|
||||
end
|
||||
end
|
||||
for _, atom_rec in ipairs(((src.scan or {}).raw_atoms) or {}) do
|
||||
local info = addrs[atom_rec.name or atom_rec.raw_name]
|
||||
if info then
|
||||
local atom = ingest_atom(atom_rec.name or atom_rec.raw_name, info, src_path)
|
||||
if atom then out[#out + 1] = atom end
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(out, function(a, b) return a.addr < b.addr end)
|
||||
return out
|
||||
@@ -2188,6 +2226,10 @@ function M.run(ctx)
|
||||
-- reading them just returns "" which is the "missing" case the builder handles.
|
||||
".debug_loc", ".debug_loclists",
|
||||
})
|
||||
-- Resolve the per-file line-table indices from the same .debug_line bytes;
|
||||
-- this MUST run before any atom sequence is emitted (build_atom_sequence below
|
||||
-- calls resolve_provenance_file_index when populating call-site / body rows).
|
||||
init_file_index_lookup(elf_path)
|
||||
-- Skip state lives in `corpus.atoms_by_name[*].debug_skip` (whole-atom) and `atom.paths.invocations[*].debug_skip` (per-invocation).
|
||||
-- `corpus` is the sole canonical source projection.
|
||||
local corpus = (ctx.shared and ctx.shared.corpus) or {}
|
||||
|
||||
Reference in New Issue
Block a user