Skip to content

Commit 0b70355

Browse files
committed
added source-span plumbing
1 parent 5f81605 commit 0b70355

6 files changed

Lines changed: 408 additions & 20 deletions

File tree

CHANGELOG.txt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,40 @@
11
CHANGELOG
22
=========
33

4+
2026-05-17 Source-span plumbing
5+
- rt_net_t and rt_cell_t gain line and column fields,
6+
self-contained so opt and xform passes can read source
7+
locations after the AST has long been freed. Eight extra
8+
bytes per net and per cell, well within the existing
9+
pool budget.
10+
- New rt_anet_at and rt_acell_at variants accept source
11+
line and column; the untagged rt_anet and rt_acell are
12+
now thin wrappers that pass zero, so every existing
13+
caller compiles unchanged and gradually migrates as each
14+
pass learns to carry provenance forward.
15+
- Lowerer updated at six high-value callsites: named
16+
identifiers via lw_fnet, port declarations, integer
17+
literals, binary operations, unary operations, and
18+
ternary expressions. Every cell or net produced by
19+
these now remembers where in the source it came from.
20+
- tests/tspans.c lands five regression tests under the
21+
"spans" category, taking the total to 87. Covers port
22+
line numbers, literal line numbers, binary-op line
23+
numbers, ternary line numbers, and the strongest
24+
property of all: two CONST cells from literals on
25+
different source lines must carry different line
26+
numbers, so anybody who accidentally hardcodes zero
27+
in the future gets caught immediately.
28+
- Hash digest unchanged because line and column are
29+
deliberately not part of the canonical form. They
30+
are provenance, not structure, so cosmetic source
31+
reformatting still produces the same fingerprint.
32+
- Remaining lowerer callsites still use the untagged
33+
variants for now and will migrate as the surrounding
34+
code is touched, after which the new fields can feed
35+
--annot, span-aware opt-pass propagation, and source
36+
pointers in ABEND dumps.
37+
438
2026-05-16 Cell budget (--budget) plus hash regression suite
539
- --budget <n> refuses to emit if the live cell count
640
exceeds the limit. Prints a status line either way and

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ $(TARGET): $(OBJS)
8383
# Tests
8484
TEST_SRCS = tests/tmain.c tests/tlex.c tests/tparse.c tests/telab.c \
8585
tests/trtl.c tests/topt.c tests/tmap.c tests/tvhdl.c \
86-
tests/tabel.c tests/thash.c
86+
tests/tabel.c tests/thash.c tests/tspans.c
8787
TEST_TARGET = trunner
8888

8989
ifeq ($(OS),Windows_NT)

include/takahe.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,13 @@ typedef struct {
524524
uint8_t is_reg; /* 1=registered (has DFF driver) */
525525
uint16_t gen; /* generation counter */
526526
uint32_t driver; /* cell index that drives this net */
527+
/* Source location of the AST node that produced this net.
528+
* Zero means "no provenance" (synthesised by a later pass
529+
* or simply not populated yet). Self-contained on purpose:
530+
* the AST is freed long before opt/xform/tech run. */
531+
uint32_t line;
532+
uint16_t col;
533+
uint16_t pad;
527534
} rt_net_t;
528535

529536
/* ---- RTL Cell ---- */
@@ -536,6 +543,13 @@ typedef struct {
536543
uint32_t width; /* operation width */
537544
int64_t param; /* cell parameter (const value, etc.) */
538545
uint16_t gen; /* generation counter */
546+
/* Source location, same contract as rt_net_t. Zero means
547+
* the cell was synthesised by a pass that did not preserve
548+
* provenance, which is fine for now and will be tightened
549+
* pass by pass over time. */
550+
uint32_t line;
551+
uint16_t col;
552+
uint16_t pad;
539553
} rt_cell_t;
540554

541555
/* ---- RTL Module ---- */
@@ -580,6 +594,15 @@ uint32_t rt_anet (rt_mod_t *M, const char *name, uint16_t nlen,
580594
uint32_t width, uint8_t port, uint8_t radix);
581595
uint32_t rt_acell(rt_mod_t *M, rt_ctype_t type, uint32_t out,
582596
const uint32_t *ins, uint8_t n_in, uint32_t width);
597+
/* Source-tagged variants. Same semantics, plus the cell or
598+
* net is marked with the AST line/col it came from. The
599+
* untagged forms above are thin wrappers that pass zero. */
600+
uint32_t rt_anet_at (rt_mod_t *M, const char *name, uint16_t nlen,
601+
uint32_t width, uint8_t port, uint8_t radix,
602+
uint32_t line, uint16_t col);
603+
uint32_t rt_acell_at(rt_mod_t *M, rt_ctype_t type, uint32_t out,
604+
const uint32_t *ins, uint8_t n_in, uint32_t width,
605+
uint32_t line, uint16_t col);
583606
void rt_dump (const rt_mod_t *M);
584607
const char *rt_cname(rt_ctype_t t);
585608

src/rtl/tk_lower.c

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,26 @@ lw_width(const lw_ctx_t *C, uint32_t nidx)
109109
return 1; /* default 1-bit */
110110
}
111111

112+
/* ---- Source location pull from an AST node ----
113+
* Used at the rt_*_at callsites so newly created nets and
114+
* cells remember where in the user's source they came from.
115+
* Out-of-range or sentinel nidx yields zero, which downstream
116+
* code treats as "no provenance," same as the untagged path. */
117+
118+
static uint32_t
119+
lw_line(const lw_ctx_t *C, uint32_t nidx)
120+
{
121+
if (nidx == 0 || nidx >= C->P->n_node) return 0;
122+
return C->P->nodes[nidx].line;
123+
}
124+
125+
static uint16_t
126+
lw_col(const lw_ctx_t *C, uint32_t nidx)
127+
{
128+
if (nidx == 0 || nidx >= C->P->n_node) return 0;
129+
return (uint16_t)C->P->nodes[nidx].col;
130+
}
131+
112132
/* ---- ceil(log2(n)) — address bits for n elements ---- */
113133

114134
static uint32_t
@@ -240,7 +260,8 @@ lw_fnet(lw_ctx_t *C, uint32_t ast_id)
240260
}
241261

242262
/* Create new net */
243-
ni = rt_anet(C->M, nm, nlen, w, 0, C->radix);
263+
ni = rt_anet_at(C->M, nm, nlen, w, 0, C->radix,
264+
lw_line(C, ast_id), lw_col(C, ast_id));
244265
C->nmap[ast_id] = ni;
245266
return ni;
246267
}
@@ -271,8 +292,10 @@ lw_expr(lw_ctx_t *C, uint32_t nidx)
271292
val = C->cv[nidx].val;
272293
if (C->cv[nidx].width > 0) cw = C->cv[nidx].width;
273294
}
274-
onet = rt_anet(C->M, "const", 5, cw, 0, C->radix);
275-
ci = rt_acell(C->M, RT_CONST, onet, NULL, 0, cw);
295+
onet = rt_anet_at(C->M, "const", 5, cw, 0, C->radix,
296+
lw_line(C, nidx), lw_col(C, nidx));
297+
ci = rt_acell_at(C->M, RT_CONST, onet, NULL, 0, cw,
298+
lw_line(C, nidx), lw_col(C, nidx));
276299
if (ci > 0 && ci < C->M->n_cell)
277300
C->M->cells[ci].param = val;
278301
return onet;
@@ -329,9 +352,11 @@ lw_expr(lw_ctx_t *C, uint32_t nidx)
329352
ow = lw > rw ? lw : rw;
330353
}
331354

332-
onet = rt_anet(C->M, "tmp", 3, ow, 0, C->radix);
355+
onet = rt_anet_at(C->M, "tmp", 3, ow, 0, C->radix,
356+
lw_line(C, nidx), lw_col(C, nidx));
333357
ins[0] = l; ins[1] = r;
334-
rt_acell(C->M, ct, onet, ins, 2, ow);
358+
rt_acell_at(C->M, ct, onet, ins, 2, ow,
359+
lw_line(C, nidx), lw_col(C, nidx));
335360
return onet;
336361
}
337362

@@ -352,16 +377,20 @@ lw_expr(lw_ctx_t *C, uint32_t nidx)
352377
/* Bitwise NOT: same width as operand */
353378
uint32_t ow = operand < C->M->n_net ?
354379
C->M->nets[operand].width : 1;
355-
uint32_t onet = rt_anet(C->M, "tmp", 3, ow, 0, C->radix);
380+
uint32_t onet = rt_anet_at(C->M, "tmp", 3, ow, 0, C->radix,
381+
lw_line(C, nidx), lw_col(C, nidx));
356382
uint32_t ins[1] = { operand };
357-
rt_acell(C->M, RT_NOT, onet, ins, 1, ow);
383+
rt_acell_at(C->M, RT_NOT, onet, ins, 1, ow,
384+
lw_line(C, nidx), lw_col(C, nidx));
358385
return onet;
359386
}
360387
if (strcmp(op, "!") == 0) {
361388
/* Logical NOT: always 1-bit */
362-
uint32_t onet = rt_anet(C->M, "tmp", 3, 1, 0, C->radix);
389+
uint32_t onet = rt_anet_at(C->M, "tmp", 3, 1, 0, C->radix,
390+
lw_line(C, nidx), lw_col(C, nidx));
363391
uint32_t ins[1] = { operand };
364-
rt_acell(C->M, RT_NOT, onet, ins, 1, 1);
392+
rt_acell_at(C->M, RT_NOT, onet, ins, 1, 1,
393+
lw_line(C, nidx), lw_col(C, nidx));
365394
return onet;
366395
}
367396
return operand; /* +a is just a */
@@ -535,9 +564,11 @@ lw_expr(lw_ctx_t *C, uint32_t nidx)
535564
dw0 = d0 < C->M->n_net ? C->M->nets[d0].width : 1;
536565
ow = dw1 > dw0 ? dw1 : dw0;
537566

538-
onet = rt_anet(C->M, "tmp", 3, ow, 0, C->radix);
567+
onet = rt_anet_at(C->M, "tmp", 3, ow, 0, C->radix,
568+
lw_line(C, nidx), lw_col(C, nidx));
539569
ins[0] = sel; ins[1] = d0; ins[2] = d1;
540-
rt_acell(C->M, RT_MUX, onet, ins, 3, ow);
570+
rt_acell_at(C->M, RT_MUX, onet, ins, 3, ow,
571+
lw_line(C, nidx), lw_col(C, nidx));
541572
return onet;
542573
}
543574

@@ -1455,9 +1486,10 @@ lw_mod(lw_ctx_t *C, uint32_t mod_node)
14551486
}
14561487

14571488
if (name_n) {
1458-
uint32_t ni = rt_anet(C->M,
1489+
uint32_t ni = rt_anet_at(C->M,
14591490
lw_text(C, name_n), lw_tlen(C, name_n),
1460-
w, dir, C->radix);
1491+
w, dir, C->radix,
1492+
lw_line(C, c), lw_col(C, c));
14611493
C->nmap[name_n] = ni;
14621494
}
14631495
break;

src/rtl/tk_rtl.c

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,15 @@ rt_sint(rt_mod_t *M, const char *s, uint16_t len)
9898
return off;
9999
}
100100

101-
/* ---- Add net ---- */
101+
/* ---- Add net (source-tagged) ----
102+
* The real function. Untagged rt_anet below is the thin
103+
* wrapper that passes zero, which is fine for callers that
104+
* have nothing to say about provenance. */
102105

103106
uint32_t
104-
rt_anet(rt_mod_t *M, const char *name, uint16_t nlen,
105-
uint32_t width, uint8_t port, uint8_t radix)
107+
rt_anet_at(rt_mod_t *M, const char *name, uint16_t nlen,
108+
uint32_t width, uint8_t port, uint8_t radix,
109+
uint32_t line, uint16_t col)
106110
{
107111
uint32_t idx;
108112
rt_net_t *n;
@@ -118,15 +122,25 @@ rt_anet(rt_mod_t *M, const char *name, uint16_t nlen,
118122
n->is_port = port;
119123
n->radix = radix ? radix : TK_RADIX_BIN;
120124
n->gen = 1;
125+
n->line = line;
126+
n->col = col;
121127

122128
return idx;
123129
}
124130

125-
/* ---- Add cell ---- */
131+
uint32_t
132+
rt_anet(rt_mod_t *M, const char *name, uint16_t nlen,
133+
uint32_t width, uint8_t port, uint8_t radix)
134+
{
135+
return rt_anet_at(M, name, nlen, width, port, radix, 0, 0);
136+
}
137+
138+
/* ---- Add cell (source-tagged) ---- */
126139

127140
uint32_t
128-
rt_acell(rt_mod_t *M, rt_ctype_t type, uint32_t out,
129-
const uint32_t *ins, uint8_t n_in, uint32_t width)
141+
rt_acell_at(rt_mod_t *M, rt_ctype_t type, uint32_t out,
142+
const uint32_t *ins, uint8_t n_in, uint32_t width,
143+
uint32_t line, uint16_t col)
130144
{
131145
uint32_t idx;
132146
rt_cell_t *c;
@@ -142,6 +156,8 @@ rt_acell(rt_mod_t *M, rt_ctype_t type, uint32_t out,
142156
c->n_in = n_in > RT_MAX_PIN ? RT_MAX_PIN : n_in;
143157
c->width = width;
144158
c->gen = 1;
159+
c->line = line;
160+
c->col = col;
145161

146162
for (i = 0; i < c->n_in; i++)
147163
c->ins[i] = ins[i];
@@ -153,6 +169,13 @@ rt_acell(rt_mod_t *M, rt_ctype_t type, uint32_t out,
153169
return idx;
154170
}
155171

172+
uint32_t
173+
rt_acell(rt_mod_t *M, rt_ctype_t type, uint32_t out,
174+
const uint32_t *ins, uint8_t n_in, uint32_t width)
175+
{
176+
return rt_acell_at(M, type, out, ins, n_in, width, 0, 0);
177+
}
178+
156179
/* ---- Dump RTL for debugging ---- */
157180

158181
void

0 commit comments

Comments
 (0)