From 24a21f7520ce91681e573833b7f33156e308964a Mon Sep 17 00:00:00 2001 From: zinc Date: Thu, 15 Oct 2026 14:36:10 +0330 Subject: docs: hold blocks in LEARN.md (36-hold), spec 5.16, ACTOR.md; TODO: m:n scheduler and follow-ups --- docs/ACTOR.md | 2 ++ docs/LEARN.md | 41 +++++++++++++++++++++++++++++++++++++++++ docs/SPEC.md | 26 +++++++++++++++++++++++++- 3 files changed, 68 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/ACTOR.md b/docs/ACTOR.md index 2dc6158..629de20 100644 --- a/docs/ACTOR.md +++ b/docs/ACTOR.md @@ -4,6 +4,8 @@ actors run asynchronously in separate operating-system processes on the native and portable micro interpreters on posix hosts. each worker has its own vm stack, heap, globals and captured handler state. workers can execute in parallel. these are isolated worker processes, rather than shared-memory threads. +a worker starts from a copy-on-write snapshot of its parent's vm: the code, the compiler and every page the parent hasn't changed since the snapshot are one set of physical pages for all workers, and an idle worker costs well under a megabyte. globals are still each worker's own. state every actor sees is declared `shared` in a top-level `hold` block, whose functions use it under the block's lock (spec 5.16, LEARN.md "hold blocks"). + ```zc namespace; include rt, actor; diff --git a/docs/LEARN.md b/docs/LEARN.md index 35ee914..eae9ad4 100644 --- a/docs/LEARN.md +++ b/docs/LEARN.md @@ -1003,6 +1003,47 @@ output: 16640 ``` +## hold blocks + +actors share nothing by default: each one runs on its own copy of the program's globals and its own heap, so a global an actor changes is changed only for that actor. state that every actor sees is declared `shared` inside a top-level `hold` block, a monitor. only the functions written in the block can use its shared variables, and each call to one of them holds the block's lock for the whole call, so everything such a function does is atomic. a call from one of the block's functions to another doesn't lock again. + +using a shared variable anywhere else is an error, and so is using it in a closure, which could run after the lock is released. shared variables hold numbers or arrays of numbers; a pointer would point into one actor's heap. when a program has several hold blocks, a function of one block may call into a block declared after it, but not back into an earlier one while it holds a later one: that order rules out deadlocks between blocks, and breaking it stops the program. + +[example source](../examples/learn/36-hold.zc): + +```zc +namespace; +include rt, actor; +typecheck on; + +// one counter for every actor. only the functions written in the block can touch it, +// and each call holds the block's lock, so a read and the write after it can't interleave. +hold { + shared let hits = 0; + fn hit() { let seen = hits; hits = seen + 1; return seen; } + fn total() { return hits; } +} + +int work(int n) { let i = 0; while (i < n) { hit(); i++; } return n; } + +fn main() { + actor.Actor* a = rt.new(actor.Actor), b = rt.new(actor.Actor); + a.spawn(work); b.spawn(work); + a.send(1000); b.send(1000); + a.recv(fn(int n) { return 0; }); b.recv(fn(int n) { return 0; }); + a.await(); b.await(); + rt.put_n(total()); rt.put_c(10); + a.close(); b.close(); + return 0; +} +``` + +output: + +```text +2000 +``` + ## the optional typechecker `typecheck on;` enables type checking for the declarations that follow; `typecheck off;` disables it. errors report the expected and actual types. the lifetime analyzer runs independently of this switch. a literal 0 can be a null pointer, `word` matches any type, and arrays can decay to pointers. diff --git a/docs/SPEC.md b/docs/SPEC.md index b2aa5ea..a4f8397 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -675,7 +675,7 @@ int y = 7; asm { y @ 1 + y ! } // y is 8 ### 5.1 items -5.1.1 a unit is a sequence of items. an item is: a function definition or prototype, a global variable, `const`, `struct`, `type`, `enum`, `include`, `embed`, `namespace`, `typecheck`, `docs`, `lines`, `memdebug`, `hints`, `enhance`, `extern`, `unchecked;`, `unbounded;`, or a use of a top-level parse word (7.2). +5.1.1 a unit is a sequence of items. an item is: a function definition or prototype, a global variable, `const`, `struct`, `type`, `enum`, `include`, `embed`, `namespace`, `typecheck`, `docs`, `lines`, `memdebug`, `hints`, `enhance`, `extern`, `unchecked;`, `unbounded;`, a `hold` block (5.16), or a use of a top-level parse word (7.2). 5.1.2 `struct`, `type`, `typecheck`, `docs`, `lines`, `namespace`, `enhance` and `extern` are valid only at the top level. `enum`, `embed`, `memdebug`, `hints` and `const` also appear as statements in functions. `include` is top-level only (5.12.7). @@ -811,6 +811,30 @@ rt.put_n(util.twice(1)); // error: module access requires `include util;` 5.15.3 the comment `//:lines NAME` names the source after it NAME, counts its lines from the next line, and turns lines on from the next top-level item. it starts a new file: from the same item, `unbounded;` and `unchecked;` of the source before it no longer apply. +### 5.16 hold blocks + +5.16.1 form: +``` +hold { + shared let c = 0; // shared T name [= constant], shared T[n] name, shared let a, b; + fn a() { ... } // any function definitions or prototypes + int b(int k) { ... } +} +``` +`hold` followed by `{` at the top level starts a hold block; elsewhere `hold` and `shared` are ordinary names. a hold block contains shared variable declarations, functions, and any other top-level items. hold blocks don't nest (error: hold blocks don't nest). each block has a rank, its position among the unit's hold blocks, from 1. + +5.16.2 a shared variable is a global with one value for the whole program, seen by every actor (docs/ACTOR.md); every other global is each actor's own copy. its type is a number type or a fixed array of one. a pointer, string, struct, growable array or function type is error: x: is shared, so it holds numbers: no pointers, strings or structs (each actor has its own heap). an initializer is a constant, as for any global. + +5.16.3 only the functions written in the block can name its shared variables. a use anywhere else, including in another hold block, is error: x: is shared in a hold block; only the functions written inside that block can use it (they hold its lock). a use in a closure, even one written inside the block, is error: x: is shared; a closure can't use it, because it may run after the hold block's lock is released. a function of a hold block can't be `o3` (error: f: is in a hold block and can't be o3). + +5.16.4 a call to a function of a hold block holds the block's lock from entry to return. the lock is reentrant: a call from a function of the block to another of the same block (directly or through other functions) doesn't take it again. inside the call the shared variables behave as ordinary globals; the values another actor wrote are in them on entry, and what the call writes is seen by the next holder after it returns. a read followed by a write (`c = c + 1`) is therefore atomic. + +5.16.5 blocks are taken in rank order: while holding block j, a thread may enter a block k > j, but entering a block k < j stops the program with `zy: hold block k was entered while block j was held; hold blocks are entered in the order they are declared` (exit status 2, a trap). this rules out lock-order deadlocks between blocks. + +5.16.6 an actor that ends (or is closed) while inside a block gives the lock up without writing its changes back. the first entry to a block anywhere in the program takes the shared variables' initial values from the image. + +5.16.7 the implementation keeps the shared values in the host, outside every vm's memory: entering a block from outside (ops 57 and 58 of the syscall gate, at the function's entry and exit) copies them in and back, so every backend runs the block's functions unchanged. a vm without threads (the browser, bare metal) has one machine and needs no lock. + ## 6. ownership and lifetimes this section describes the analyzer: a pass that runs inside the compiler on every function (including code compiled at run time by `run.eval` and friends), tracks heap objects through local variables, inserts frees, and reports violations. it can be switched off (6.12). what it proves is limited to what is stated here. -- cgit v1.2.3 From b6af0e942dcde433db96a298a25490aa44e88173 Mon Sep 17 00:00:00 2001 From: zinc Date: Thu, 15 Oct 2026 15:25:23 +0330 Subject: hold: copy hold.h with micro.c everywhere; xref baseline for hold globals; lsp keyword completion yields to a same-named symbol; compiler message avoids the word actor --- Makefile | 2 +- docs/SPEC.md | 2 +- src/baremetal/run.sh | 4 ++-- src/compiler/decl.zc | 2 +- src/lsp/server.zc | 16 ++++++++++++++-- src/tools/parity.sh | 2 +- src/tools/zcc | 2 +- tests/bench/bench_bytes.sh | 2 +- tests/bench/bench_share.sh | 2 +- tests/bench/bench_strings.sh | 2 +- tests/drivers/imagekit.py | 2 +- tests/test.sh | 10 +++++----- tests/xref.baseline | 8 +++++++- 13 files changed, 37 insertions(+), 19 deletions(-) (limited to 'docs') diff --git a/Makefile b/Makefile index ceed9be..ff9e9d0 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,7 @@ WEB := $(B)/web MAP := $(B)/core/map # hand-written core headers, the generated map, and where #embed finds the genome image COREINC := -Isrc/core -I$(MAP) --embed-dir=$(B)/core -CORE_H := src/core/genome.h src/core/fops.h src/core/bops.h src/core/stack.h $(MAP)/memmap.h +CORE_H := src/core/genome.h src/core/fops.h src/core/bops.h src/core/stack.h src/core/hold.h $(MAP)/memmap.h # the sources the compiler genome is built from, and the files it embeds # the compiler is the files named in src/compiler/parts, in that order ZC_PARTS := $(addprefix src/compiler/,$(shell cat src/compiler/parts)) diff --git a/docs/SPEC.md b/docs/SPEC.md index a4f8397..7dd1247 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -823,7 +823,7 @@ hold { ``` `hold` followed by `{` at the top level starts a hold block; elsewhere `hold` and `shared` are ordinary names. a hold block contains shared variable declarations, functions, and any other top-level items. hold blocks don't nest (error: hold blocks don't nest). each block has a rank, its position among the unit's hold blocks, from 1. -5.16.2 a shared variable is a global with one value for the whole program, seen by every actor (docs/ACTOR.md); every other global is each actor's own copy. its type is a number type or a fixed array of one. a pointer, string, struct, growable array or function type is error: x: is shared, so it holds numbers: no pointers, strings or structs (each actor has its own heap). an initializer is a constant, as for any global. +5.16.2 a shared variable is a global with one value for the whole program, seen by every actor (docs/ACTOR.md); every other global is each actor's own copy. its type is a number type or a fixed array of one. a pointer, string, struct, growable array or function type is error: x: is shared, so it holds numbers: no pointers, strings or structs (each worker has its own heap). an initializer is a constant, as for any global. 5.16.3 only the functions written in the block can name its shared variables. a use anywhere else, including in another hold block, is error: x: is shared in a hold block; only the functions written inside that block can use it (they hold its lock). a use in a closure, even one written inside the block, is error: x: is shared; a closure can't use it, because it may run after the hold block's lock is released. a function of a hold block can't be `o3` (error: f: is in a hold block and can't be o3). diff --git a/src/baremetal/run.sh b/src/baremetal/run.sh index 43f8a4c..5f812fa 100755 --- a/src/baremetal/run.sh +++ b/src/baremetal/run.sh @@ -69,7 +69,7 @@ if [ "$mode" = run ] || [ "$mode" = io ]; then # the map is sized to the program, so a bigger runtime or program just gets a bigger board ${CC:-$(sh src/tools/findcc.sh)} -O2 -w src/tools/genmap.c -o "$D/genmap" GENMAP="$D/genmap" bash src/tools/automap.sh run "$D/img" src/runtime/run.zc "$src" >"$D/genmap.log" 2>&1 || { cat "$D/genmap.log" >&2; exit 1; } - cp src/micro/micro.c src/micro/micro_port.h src/core/fops.h src/core/bops.h src/core/stack.h src/core/genome.h "$D/img/" + cp src/micro/micro.c src/micro/micro_port.h src/core/fops.h src/core/bops.h src/core/stack.h src/core/hold.h src/core/genome.h "$D/img/" "$cc" $common $def -I"$D/img" -c "$D/img/micro.c" -o "$D/micro.o" if [ "$mode" = io ]; then "$cc" $common $def -DBARE_INPUT=1 -I"$D/img" -c src/baremetal/port.c -o "$D/port.o" @@ -80,7 +80,7 @@ if [ "$mode" = run ] || [ "$mode" = io ]; then fi elif [ "$mode" = compile ]; then [ -f build/core/genome.bin ] || { echo "baremetal: no build/core/genome.bin; run make genome first" >&2; exit 1; } - cp src/micro/micro.c src/micro/micro_port.h build/core/map/memmap.h build/core/genome.bin src/core/genome.h src/core/fops.h src/core/bops.h src/core/stack.h "$D/" + cp src/micro/micro.c src/micro/micro_port.h build/core/map/memmap.h build/core/genome.bin src/core/genome.h src/core/fops.h src/core/bops.h src/core/stack.h src/core/hold.h "$D/" "$cc" $common $def -I"$D" -c "$D/micro.c" -o "$D/micro.o" "$cc" $common $def -DBARE_INPUT=1 -I"$D" -c src/baremetal/port.c -o "$D/port.o" timeout=90 diff --git a/src/compiler/decl.zc b/src/compiler/decl.zc index fed7b8d..55e23a7 100644 --- a/src/compiler/decl.zc +++ b/src/compiler/decl.zc @@ -264,7 +264,7 @@ int kw_hold() { if (skind[s] == K_GVAR || skind[s] == K_GARR) { t = stype[s]; w = 1; if (skind[s] == K_GARR) { w = tsz(t); if (t[0] == TY_ARR) t = t[1]; } - if (t && (isptr(t) || isagg(t) || t == tbstr || t[0] == TY_DYN || t[0] == TY_FN)) serrn(sname[s], "is shared, so it holds numbers: no pointers, strings or structs (each actor has its own heap)"); + if (t && (isptr(t) || isagg(t) || t == tbstr || t[0] == TY_DYN || t[0] == TY_FN)) serrn(sname[s], "is shared, so it holds numbers: no pointers, strings or structs (each worker has its own heap)"); sflag[s] = sflag[s] | F_SHARED | rank * F_SHARED_RANK; if (hold_nshared >= 64) serr("too many shared variables in one hold block (64)"); else { hold_tab[hold_nshared * 2] = sval[s]; hold_tab[hold_nshared * 2 + 1] = w; hold_nshared++; } diff --git a/src/lsp/server.zc b/src/lsp/server.zc index d776b1d..85f3ea5 100644 --- a/src/lsp/server.zc +++ b/src/lsp/server.zc @@ -3100,6 +3100,18 @@ int cp_score(char* name) { if (exact) { if (n == i) return 0; return 10 + n - i; } return 100 + score + n - i; } +// a label already offered (a symbol named like a keyword wins over the keyword) +int cp_has(char* name) { + int k = 0, i; + while (k < cp_n) { + i = 0; + while (name[i] && cp_name[k][i] == name[i]) i++; + if (!name[i] && !cp_name[k][i]) return 1; + k++; + } + return 0; +} + int cp_add(char* name, int kind, int dead, int relevance) { int score = cp_score(name); if (score < 0) return 0; @@ -3270,8 +3282,8 @@ int do_completion(int i) { if (run.skind[s] != run.K_DOC && run.lookup(run.sname[s]) == s && (run.skind[s] != run.K_FN || (run.sflag[s] & (run.F_DEF | run.F_DECL)))) cp_symbol(s, run.sname[s]); s++; } - if (cp_score("hold") >= 0) { hn = 0; hov_text("hold { shared let c; fn f() { } }"); if (cp_add("hold", 14, 0, 1)) cp_docs[cp_n - 1] = cp_copy("a monitor: shared variables for every actor, used under the block's lock by the functions written in it"); } - if (cp_score("shared") >= 0) { hn = 0; hov_text("shared let name;"); if (cp_add("shared", 14, 0, 1)) cp_docs[cp_n - 1] = cp_copy("inside a hold block: a variable every actor sees, used only by the block's functions"); } + if (cp_score("hold") >= 0 && !cp_has("hold")) { hn = 0; hov_text("hold { shared let c; fn f() { } }"); if (cp_add("hold", 14, 0, 1)) cp_docs[cp_n - 1] = cp_copy("a monitor: shared variables for every actor, used under the block's lock by the functions written in it"); } + if (cp_score("shared") >= 0 && !cp_has("shared")) { hn = 0; hov_text("shared let name;"); if (cp_add("shared", 14, 0, 1)) cp_docs[cp_n - 1] = cp_copy("inside a hold block: a variable every actor sees, used only by the block's functions"); } run.probe_off = cp_cursor; old = chk_stream; chk_stream = 0; check_doc(i); chk_stream = old; run.probe_off = -1; member = 0; if (run.probe_has == 2 && run.probe_mem && run.probe_mem[0] == run.TY_STRUCT) member = run.probe_mem[2]; diff --git a/src/tools/parity.sh b/src/tools/parity.sh index 2d2086d..1b90885 100755 --- a/src/tools/parity.sh +++ b/src/tools/parity.sh @@ -71,7 +71,7 @@ SRCS="src/runtime/rt.zc src/core/core.zc src/runtime/run.zc" 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 -DEPS="$ZC0 $MKG $W/prefix.zc src/micro/micro.c src/micro/port_linux.c src/micro/micro_port.h src/core/fops.h src/core/bops.h src/core/stack.h src/core/genome.h build/core/map/memmap.h" +DEPS="$ZC0 $MKG $W/prefix.zc src/micro/micro.c src/micro/port_linux.c src/micro/micro_port.h src/core/fops.h src/core/bops.h src/core/stack.h src/core/hold.h src/core/genome.h build/core/map/memmap.h" # one line of a program's output: newlines as /, control bytes as ?, cut to 60 columns short() { diff --git a/src/tools/zcc b/src/tools/zcc index ba38e97..a53fe01 100755 --- a/src/tools/zcc +++ b/src/tools/zcc @@ -41,7 +41,7 @@ trap cleanup EXIT # the map and the genome for this program only ( cd "$here" && bash src/tools/automap.sh run "$work/run" src/runtime/run.zc "$abs" ) >/dev/null cp "$here/src/micro/micro.c" "$here/src/micro/micro_port.h" "$here/src/micro/port_linux.c" \ - "$here/src/core/fops.h" "$here/src/core/bops.h" "$here/src/core/stack.h" "$here/src/core/genome.h" "$work/run/" + "$here/src/core/fops.h" "$here/src/core/bops.h" "$here/src/core/stack.h" "$here/src/core/hold.h" "$here/src/core/genome.h" "$work/run/" # static where the toolchain allows it; a plain link otherwise, with a note static=-static diff --git a/tests/bench/bench_bytes.sh b/tests/bench/bench_bytes.sh index 775b71d..c509da4 100644 --- a/tests/bench/bench_bytes.sh +++ b/tests/bench/bench_bytes.sh @@ -10,7 +10,7 @@ T=$(mktemp -d) trap 'rm -rf "$T"' EXIT $CC -O2 -w src/tools/genmap.c -o "$T/genmap" || exit 1 GENMAP="$T/genmap" bash src/tools/automap.sh run "$T/run" src/runtime/run.zc tests/programs/bench_bytes.zc >/dev/null || exit 1 -cp src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/genome.h "$T/run/" +cp src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/hold.h src/core/genome.h "$T/run/" $CC -O2 -w -DJTYPE=int "$T/run/core.c" "$T/run/io_linux.c" -o "$T/zy" || exit 1 $CC -O2 -w "$T/run/micro.c" "$T/run/port_linux.c" -o "$T/zym" || exit 1 vms=("$T/zy" "$T/zym"); names=("interp" "micro") diff --git a/tests/bench/bench_share.sh b/tests/bench/bench_share.sh index 5cb1bfd..c47f5f5 100644 --- a/tests/bench/bench_share.sh +++ b/tests/bench/bench_share.sh @@ -8,7 +8,7 @@ T=$(mktemp -d) trap 'rm -rf "$T"' EXIT $CC -O2 -w src/tools/genmap.c -o "$T/genmap" || exit 1 "$T/genmap" --preset run -o "$T/run" >/dev/null || exit 1 -cp src/tools/zc0.c src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/genome.h "$T/run/" +cp src/tools/zc0.c src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/hold.h src/core/genome.h "$T/run/" $CC -O1 -w "$T/run/zc0.c" -o "$T/zc0" || exit 1 cat "$T/run/rt.zc" src/core/core.zc src/runtime/run.zc tests/programs/bench_share.zc | "$T/zc0" > "$T/run/genome.bin" || exit 1 $CC -O2 -w -DJTYPE=int "$T/run/core.c" "$T/run/io_linux.c" -o "$T/zy" || exit 1 diff --git a/tests/bench/bench_strings.sh b/tests/bench/bench_strings.sh index b14bf57..8bf3b77 100644 --- a/tests/bench/bench_strings.sh +++ b/tests/bench/bench_strings.sh @@ -8,7 +8,7 @@ T=$(mktemp -d) trap 'rm -rf "$T"' EXIT $CC -O2 -w src/tools/genmap.c -o "$T/genmap" || exit 1 "$T/genmap" --preset run -o "$T/run" >/dev/null || exit 1 -cp src/tools/zc0.c src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/genome.h "$T/run/" +cp src/tools/zc0.c src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/hold.h src/core/genome.h "$T/run/" $CC -O1 -w "$T/run/zc0.c" -o "$T/zc0" || exit 1 cat "$T/run/rt.zc" src/core/core.zc src/runtime/run.zc tests/programs/bench_strings.zc | "$T/zc0" > "$T/run/genome.bin" || exit 1 $CC -O2 -w -DJTYPE=int "$T/run/core.c" "$T/run/io_linux.c" -o "$T/zy" || exit 1 diff --git a/tests/drivers/imagekit.py b/tests/drivers/imagekit.py index 6cc083f..5df4810 100644 --- a/tests/drivers/imagekit.py +++ b/tests/drivers/imagekit.py @@ -9,7 +9,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[2] DEFAULT_MAP = ROOT / 'build' / 'core' / 'map' / 'memmap.h' MICRO_SOURCES = ['src/micro/micro.c', 'src/micro/micro_port.h', 'src/micro/port_linux.c', - 'src/core/fops.h', 'src/core/bops.h', 'src/core/stack.h', 'src/core/genome.h'] + 'src/core/fops.h', 'src/core/bops.h', 'src/core/stack.h', 'src/core/hold.h', 'src/core/genome.h'] def compiler(): diff --git a/tests/test.sh b/tests/test.sh index 90599ed..c31cfb7 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -337,10 +337,10 @@ s_small() { cmp -s $T/small/memmap.h $T/small-flags/memmap.h && cmp -s $T/small/rt.zc $T/small-flags/rt.zc \ && cmp -s $T/small/compiler.zc $T/small-flags/compiler.zc && cmp -s $T/small/map.txt $T/small-flags/map.txt \ && chk 1 1 "compiler flags reproduce the small map" || chk 0 1 "compiler flags reproduce the small map" - cp src/tools/zc0.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/genome.h $T/small/ + cp src/tools/zc0.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/hold.h src/core/genome.h $T/small/ $CC -O1 -w -fwrapv $T/small/zc0.c -o $T/zc0s || exit 1 cat $T/small/rt.zc src/core/core.zc $T/small/compiler.zc | $T/zc0s > $T/small/genome.bin || exit 1 - cp src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/micro/port_stub.c src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/genome.h $T/small/ + cp src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/micro/port_stub.c src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/hold.h src/core/genome.h $T/small/ $CC -pthread -DZY_THREADS -O2 -w -fwrapv $T/small/micro.c $T/small/port_linux.c -o $T/zyms || exit 1 $CC -pthread -O2 -w -fwrapv -DJTYPE=int $T/small/core.c $T/small/io_linux.c -o $T/zys || exit 1 $CC -ffreestanding -w -c $T/small/micro.c -o $T/micro_s.o && $CC -ffreestanding -w -c $T/small/port_stub.c -o $T/stub_s.o && chk 1 1 "small microcore compiles freestanding" || chk 0 1 "small microcore compiles freestanding" @@ -381,14 +381,14 @@ s_runonly() { # its own run map: the run-only image is built against this one, and the # small-machine section lays out its own rather than sharing this $T/genmap --preset run -o $T/run || exit 1 - cp src/tools/zc0.c src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/micro/port_stub.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/genome.h $T/run/ + cp src/tools/zc0.c src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/micro/port_stub.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/hold.h src/core/genome.h $T/run/ $CC -O1 -w -fwrapv $T/run/zc0.c -o $T/zc0r || exit 1 runonly() { # $1 label, $2 source. the program is already in the image, so it reads no source. # the map is sized to the program: src/tools/automap.sh lays it out for the image's own length local a b d=$T/run-$1 input=$2 if [ "$1" = gc ]; then input=$T/collector-progress.zc; sed -E 's/f >= [0-9]+/f > 0/' "$2" > "$input"; fi GENMAP=$T/genmap bash src/tools/automap.sh run $d src/runtime/run.zc "$input" >/dev/null || { chk 0 1 "run $1 builds"; return; } - cp src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/micro/port_stub.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/genome.h $d/ + cp src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/micro/port_stub.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/hold.h src/core/genome.h $d/ $CC -pthread -DZY_THREADS -O2 -w -fwrapv $d/micro.c $d/port_linux.c -o $T/zymr || { chk 0 1 "run $1 links"; return; } a=$($T/zy < "$input" 2>&1 | sed "s##$input#g" | tr '\n' '/') b=$($T/zymr &1 | tr '\n' '/') @@ -413,7 +413,7 @@ s_runonly() { runonly zc0bstr tests/programs/zc0bstr.zc runonly zc0umd tests/programs/zc0umd.zc runonly wiring examples/wiring.zc - d=$T/run-echo; GENMAP=$T/genmap bash src/tools/automap.sh run $d src/runtime/run.zc examples/echo.zc >/dev/null && cp src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/micro/port_stub.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/genome.h $d/ \ + d=$T/run-echo; GENMAP=$T/genmap bash src/tools/automap.sh run $d src/runtime/run.zc examples/echo.zc >/dev/null && cp src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/micro/port_stub.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/hold.h src/core/genome.h $d/ \ && $CC -pthread -DZY_THREADS -O2 -w -fwrapv $d/micro.c $d/port_linux.c -o $T/zymr \ && chk "$(printf 'abc\n' | $T/zymr | tr '\n' '/')" "$( (cat examples/echo.zc; printf '`abc\n') | $T/zy | tr '\n' '/')" "run echo" \ || chk 0 1 "run echo" diff --git a/tests/xref.baseline b/tests/xref.baseline index 26b6931..79e948b 100644 --- a/tests/xref.baseline +++ b/tests/xref.baseline @@ -161,6 +161,7 @@ fbnd chk_reset closure ctx_pop fn_begin kw_unbounded fchk chk_reset ctx_pop defer_one fn_begin kw_unchecked fdepth block blockexpr casebody chk_reset ctx_pop fn_begin kw_for kw_switch substmt fframe chk_reset ctx_pop prologue +fhold fndef fieldcap field_note fieldn field_note types_init fieldnames field_note @@ -236,6 +237,11 @@ hl_lev hint_set hl_n chk_unit hint_set hl_off hint_set hlimit build_image_grow chk_unit reserve_code start syntax_resident +hold_count kw_hold +hold_cur kw_hold +hold_desc kw_hold +hold_nshared kw_hold +hold_tab kw_hold ht_end ht_note ht_n ht_note ht_start ht_note @@ -630,7 +636,7 @@ sdsrc addsym def_note reserve_symbols send_check args field seqtop seqasm zc_init sfix addsym enhdef fndef fnref reserve_symbols -sflag add_kw addsym blob_load call enhdef fndef fndone fwdfn_at ginstance kw_extern kw_struct methcall methdecl prescan_fn reserve_symbols retcheck +sflag add_kw addsym blob_load call enhdef fndef fndone fwdfn_at ginstance kw_extern kw_hold kw_struct methcall methdecl prescan_fn reserve_symbols retcheck sgb sig_attach sgb_initial sigcap sig_attach -- cgit v1.2.3