aboutsummaryrefslogtreecommitdiff
path: root/src/tools
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 15:54:04 +0330
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 15:54:04 +0330
commit0879d0a42d137054057cd2a1e37437e6be78ed0b (patch)
treefcb16d3185c1f80fae8d54cd9062bd68d6c0c88d /src/tools
parentmerge spec conformance suite (diff)
parentmerge main into work/line-tables (diff)
merge line tables
Diffstat (limited to 'src/tools')
-rwxr-xr-xsrc/tools/automap.sh14
-rw-r--r--src/tools/build.zc3
-rw-r--r--src/tools/genmap.c2
-rwxr-xr-xsrc/tools/parity.sh4
-rw-r--r--src/tools/zc0.c65
5 files changed, 82 insertions, 6 deletions
diff --git a/src/tools/automap.sh b/src/tools/automap.sh
index ff559ac..b4aceb7 100755
--- a/src/tools/automap.sh
+++ b/src/tools/automap.sh
@@ -35,8 +35,18 @@ lay() { # $1 = genome slot, or empty for the preset's own
$CC -O1 -w -fwrapv src/tools/zc0.c -I"$O/map" -Isrc/core -o "$ZC0"
$CC -O1 -w -fwrapv src/tools/mkgenome.c -I"$O/map" -o "$O/.mkgenome"
}
+# the files after the map's own. a run-only image gets line tables for its program (so a fault
+# prints a backtrace): zc0 reads the comment //:lines NAME as "from here on, file NAME, count
+# its lines from the next one". the files under src/ are the runtime's and have none.
+feed() {
+ local f
+ for f in "$@"; do
+ if [ "$P" = run ]; then case "$f" in src/*) ;; *) printf '\n//:lines %s\n' "$f" ;; esac; fi
+ cat "$f"
+ done
+}
build() {
- if [ -f "$O/map/compiler.zc" ]; then cat "$O/map/rt.zc" src/core/core.zc "$O/map/compiler.zc" "$@"; else cat "$O/map/rt.zc" src/core/core.zc "$@"; fi | "$ZC0" > "$O/image.bin"
+ if [ -f "$O/map/compiler.zc" ]; then { cat "$O/map/rt.zc" src/core/core.zc "$O/map/compiler.zc"; feed "$@"; }; else { cat "$O/map/rt.zc" src/core/core.zc; feed "$@"; }; fi | "$ZC0" > "$O/image.bin"
"$O/.mkgenome" < "$O/image.bin" > "$O/genome.bin"
}
# with DOCS=1 the genome is made again with zc0 built for docs (ZC0DOCS), so the
@@ -44,7 +54,7 @@ build() {
# docofs(). the image is the same code either way; the docs are stored after it.
docs() {
$CC -O1 -w -fwrapv -DZC0DOCS src/tools/zc0.c -I"$O/map" -Isrc/core -o "$O/.zc0docs"
- if [ -f "$O/map/compiler.zc" ]; then cat "$O/map/rt.zc" src/core/core.zc "$O/map/compiler.zc" "$@"; else cat "$O/map/rt.zc" src/core/core.zc "$@"; fi | "$O/.zc0docs" > "$O/image.bin"
+ if [ -f "$O/map/compiler.zc" ]; then { cat "$O/map/rt.zc" src/core/core.zc "$O/map/compiler.zc"; feed "$@"; }; else { cat "$O/map/rt.zc" src/core/core.zc; feed "$@"; }; fi | "$O/.zc0docs" > "$O/image.bin"
"$O/.mkgenome" < "$O/image.bin" > "$O/genome.bin"
}
len() { echo $(( $(wc -c < "$1") / 8 )); }
diff --git a/src/tools/build.zc b/src/tools/build.zc
index b0e3da0..b628a33 100644
--- a/src/tools/build.zc
+++ b/src/tools/build.zc
@@ -6,6 +6,9 @@ include rt, run;
docs on;
+// the genome has no line tables: its code is the runtime's and the compiler's, and a program
+// built into an image turns them on for itself (lines on;)
+lines off;
// writes the compiled genome image to stdout as little-endian 64-bit words.
int main() {
int n = run.build_image_grow(rt.GORG, 4096), i = 0, w;
diff --git a/src/tools/genmap.c b/src/tools/genmap.c
index d515778..6d8db55 100644
--- a/src/tools/genmap.c
+++ b/src/tools/genmap.c
@@ -242,7 +242,7 @@ static void preset_default(Spec *s)
s->compiler = 1;
s->heap_top = 1; s->code_end = 0x80000; s->types = 65536;
s->heap = 0x60000; s->total = 0x8000000;
- s->genome = 0x60000; s->frame = 0x40000; s->ret = 0x10000; s->mark = 0x8000;
+ s->genome = 0x68000; s->frame = 0x40000; s->ret = 0x10000; s->mark = 0x8000;
s->data = 0x7FF0;
tables_big(s);
}
diff --git a/src/tools/parity.sh b/src/tools/parity.sh
index 00245a8..2b9b870 100755
--- a/src/tools/parity.sh
+++ b/src/tools/parity.sh
@@ -63,7 +63,9 @@ KNOWN=tests/parity.known
mkdir -p "$W"
# what every image starts with, and how many lines that is (zc0 counts from the top)
SRCS="src/runtime/rt.zc src/core/core.zc src/runtime/run.zc"
-{ cat $SRCS; echo 'namespace;'; } > "$W/prefix.new"
+# the marker turns zc0's line tables on for the program, named <stdin> as under zy, so faults
+# and rt.trace() print the same backtrace on both sides (src/tools/automap.sh does the same)
+{ cat $SRCS; echo 'namespace;'; echo '//:lines <stdin>'; } > "$W/prefix.new"
if cmp -s "$W/prefix.new" "$W/prefix.zc"; then rm "$W/prefix.new"; else mv "$W/prefix.new" "$W/prefix.zc"; fi
nprefix=$(wc -l < "$W/prefix.zc")
# what the compiled image depends on besides the program
diff --git a/src/tools/zc0.c b/src/tools/zc0.c
index 040a4ea..5846e22 100644
--- a/src/tools/zc0.c
+++ b/src/tools/zc0.c
@@ -73,6 +73,13 @@ static int esc(void)
switch (c) { case 'n': return 10; case 't': return 9; case 'r': return 13; case '0': return 0; case 'e': return 27; }
return c;
}
+/* line tables, as the self-hosted compiler makes them with lines on (see rt.line_blocks in rt.zc).
+ they are off until a program says lines on; or a marker comment //:lines NAME comes by, which
+ src/tools/automap.sh puts in front of the program of a run-only image: it turns them on, names
+ the file, and counts the lines from the next one. */
+static int zl_on, zl_base, zl_head, zl_entry, zl_open, zl_fline, zl_file_at, zl_n, zl_cap;
+static char zl_file[256] = "<stdin>", zl_file_done[256], zl_name[64];
+static int64_t *zl_pairs;
/* doc comments, as the self-hosted compiler reads them with docs on: the //
lines directly above a declaration, up to a blank line or any other text */
static char dbuf[2100], dsnap[2100], dnm[64]; static int dn, dlast, dline, dsn, docpending;
@@ -82,7 +89,15 @@ static void doccomment(void) /* pos is on the first slash */
while (k >= 0 && (src[k] == ' ' || src[k] == '\t')) k--;
bol = k < 0 || src[k] == '\n';
pos += 2;
- if (src[pos] == ':') { dn = 0; dlast = cl; docpending = 0; while (src[pos] && src[pos] != '\n') pos++; return; }
+ if (src[pos] == ':') {
+ if (!strncmp(src + pos, ":lines ", 7)) {
+ int n = 0;
+ pos += 7;
+ while (src[pos] && src[pos] != '\n' && src[pos] != '\r' && n < 255) zl_file[n++] = src[pos++];
+ zl_file[n] = 0; zl_on = 1; zl_base = cl;
+ }
+ dn = 0; dlast = cl; docpending = 0; while (src[pos] && src[pos] != '\n') pos++; return;
+ }
if (!bol) { while (src[pos] && src[pos] != '\n') pos++; return; }
if (dn && dlast != cl - 1) dn = 0;
if (src[pos] == ' ') pos++;
@@ -500,6 +515,42 @@ static int bstrwords(const unsigned char *b, long n)
if ((n & 7) == 0) emit(0);
return a;
}
+/* line tables. zl_begin starts recording the function whose code begins at entry, zl_stmt notes
+ that a statement starts at the current line, and zl_end puts the function's line block among
+ the code, as lt_end does in the resident compiler: [next, entry, end, name, file, count, then
+ count words of offset << 32 | line]. */
+static void zl_begin(int entry, const char *name, int fline)
+{
+ if (!zl_on) { zl_open = 0; return; }
+ zl_open = 1; zl_entry = entry; zl_n = 0; zl_fline = fline - zl_base;
+ strncpy(zl_name, name, 63); zl_name[63] = 0;
+ if (zl_n == zl_cap) { zl_cap = zl_cap ? zl_cap * 2 : 1024; zl_pairs = realloc(zl_pairs, (size_t)zl_cap * sizeof *zl_pairs); }
+ zl_pairs[zl_n++] = zl_fline;
+}
+static void zl_stmt(void)
+{
+ int off = here - zl_entry;
+ if (!zl_open) return;
+ if (zl_n == zl_cap) { zl_cap *= 2; zl_pairs = realloc(zl_pairs, (size_t)zl_cap * sizeof *zl_pairs); }
+ if (zl_n && (zl_pairs[zl_n - 1] >> 32) == off) zl_n--;
+ if (zl_n && (zl_pairs[zl_n - 1] & 0xffffffffLL) == line - zl_base) return;
+ zl_pairs[zl_n++] = (int64_t)off * 4294967296LL + (line - zl_base);
+}
+static void zl_end(void)
+{
+ int end = here, name, file, blk, i;
+ if (!zl_open) return;
+ zl_open = 0;
+ name = bstrwords((const unsigned char *)zl_name, (long)strlen(zl_name));
+ if (!zl_file_at || strcmp(zl_file, zl_file_done)) {
+ zl_file_at = bstrwords((const unsigned char *)zl_file, (long)strlen(zl_file)); strcpy(zl_file_done, zl_file);
+ }
+ file = zl_file_at;
+ blk = here;
+ emit(zl_head); emit(zl_entry); emit(end); emit(name); emit(file); emit(zl_n);
+ for (i = 0; i < zl_n; i++) emit(zl_pairs[i]);
+ zl_head = blk;
+}
/* b"...": the words inline, jumped over, then the address pushed */
static void bstrlit(void)
{
@@ -691,6 +742,7 @@ static void patchbreaks(void) { int i; for (i = 0; i < nbrk[nloop]; i++) setlit(
static void stmt(void)
{
int a, b, c, d;
+ zl_stmt();
if (accept(";")) return;
if (is("{")) { block(); return; }
{
@@ -759,7 +811,7 @@ static void stmt(void)
/* ---- top level ---- */
static void fndef(int ret, int rstr) /* rstr: the return type is bstr */
{
- char *n = ident(); Sym *s; int np = 0, tags = 0, t, frame, i, helper, busy, done, check, node;
+ char *n = ident(); int zfl = line; Sym *s; int np = 0, tags = 0, t, frame, i, helper, busy, done, check, node;
if (dsn && !dnm[0]) strcpy(dnm, n);
char pn[16][64]; int pstr[16];
char full_name[256]; qualify(full_name, sizeof full_name, n); s = find_raw(full_name);
@@ -794,6 +846,7 @@ static void fndef(int ret, int rstr) /* rstr: the return type is bstr */
}
#endif
nlo = 0; nslot = 0; nepi = 0; infn = 1;
+ zl_begin(here, s->name, zfl);
for (i = 0; i < np; i++) { strcpy(lo[nlo].name, pn[i]); lo[nlo].arr = 0; lo[nlo].flt = 0; lo[nlo].dstr = lo[nlo].str = pstr[i & 15]; lo[nlo++].slot = nslot++; }
/* retain the caller's pointer until the frame has its final segment. */
helper = !find("frame_busy") || !strcmp(s->name, "rt.frame_grow") || !strcmp(s->name, "rt.frame_leave");
@@ -823,6 +876,7 @@ static void fndef(int ret, int rstr) /* rstr: the return type is bstr */
op(O_RET);
setlit(frame, nslot);
infn = 0;
+ zl_end();
}
/* enhance name(params) [-> n] micro "recipe"; or ... vm { asm };
emits the install descriptor; the booted genome installs it */
@@ -953,6 +1007,11 @@ static void top1(void)
expect(";"); return;
}
if (accept("include")) { do { ident(); } while (accept(",")); expect(";"); return; }
+ if (is("lines")) { /* lines on; and lines off; (anything else is a name) */
+ Lex L; lexsave(&L); next();
+ if (is("on") || is("off")) { zl_on = is("on"); next(); expect(";"); return; }
+ lexrest(&L);
+ }
if (accept("embed")) { embeddecl(0); return; }
{
if (accept("unchecked")) { top1(); return; }
@@ -1088,6 +1147,8 @@ int main(int argc, char **argv)
setlit(fx[i].at, s->val);
}
if (!(st = find("start")) || !st->defd) { fprintf(stderr, "zc0: no start\n"); return 1; }
+ /* the line tables are reached through rt.line_blocks, which starts out as the newest block */
+ if (zl_head && (st = find_raw("rt.line_blocks"))) img[st->val - GORG] = zl_head;
/* symbol blob: kind, value, sig, name chars, 0 ... then a 0 kind */
img[5] = here;
for (i = 0; i < nsy; i++) {