aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 08:09:40 +0000
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 08:09:40 +0000
commita3e9e53e64a1aa5692bca2b60bcbff6a0fcfdba1 (patch)
tree2598c50e36e488935211c2e304b720a58315bacb /src/runtime
parentembed: tests, editor support and docs; js vm file access; test fixes (diff)
lsp: quick fixes for hints and bare library names; comment cleanup
an undefined bare name that a module exports now says which one: "put_c is used but never defined; did you mean to include rt and use rt.put_c?", and the import quick fix is titled the same way and marked preferred when it is the only one, so the editor's auto-fix applies it. hints gain quick fixes that keep the program's meaning: an unused parameter, or a local with no other use, is renamed to _; a constant local with one declarator becomes const; a power-of-two multiply becomes a parenthesized shift (not when a * / or % binds its left operand); and let p = rt.new(n) with a small constant becomes let p[n]. the hints no longer fire for _ discards or for locals already declared const. whole-line /* */ comments in zc sources are // comments now, and //: is gone from the maintained sources: the notes it marked were mostly the documentation of the declaration below, which hovers now show. section headers are separated from the declarations after them.
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/rt.zc218
1 files changed, 109 insertions, 109 deletions
diff --git a/src/runtime/rt.zc b/src/runtime/rt.zc
index a54d88c..ef5a5c6 100644
--- a/src/runtime/rt.zc
+++ b/src/runtime/rt.zc
@@ -1,33 +1,33 @@
namespace rt;
include run;
-//: ============================================================================
-//: rt.zc: the runtime half of the genome. memory map, i/o, arithmetic helpers,
-//: the heap (size-class free lists) and the mark-sweep collector.
-//: ============================================================================
+// ============================================================================
+// rt.zc: the runtime half of the genome. memory map, i/o, arithmetic helpers,
+// the heap (size-class free lists) and the mark-sweep collector.
+// ============================================================================
-//: how the runtime is laid out, for a reader coming in cold:
-//:
-//: memory map generated constants name the regions used by a build. the
-//: compiler and runtime use the same names and values.
-//: i/o, strings thin wrappers over the cpu's i/o opcodes, plus the two
-//: string forms (char* strings and packed byte strings).
-//: heap a buddy allocator with a 5 word header on every block.
-//: new/free/recycle are built on it. every heap object has at
-//: most one owner (the container that holds it), and freeing
-//: an owner frees what it owns.
-//: growable arrays T[?] handles, built on the heap.
-//: mark-sweep a conservative collector. it is the safety net behind
-//: free: anything the compiler's ownership analysis could not
-//: free gets reclaimed here.
-//: memory debug optional checks (use after free, double free, leaks).
-//: syscalls file and environment access through one gate word.
-//:
-//: everything is a 64-bit word, and an address is a word index, not a byte
-//: index. the cpu is a stack machine, so "asm { a @ . }" pushes a, fetches the
-//: word at that address with @, then outputs it with . (see the vm reference).
-//:
-//: ---- memory map (words) ----
+// how the runtime is laid out, for a reader coming in cold:
+//
+// memory map generated constants name the regions used by a build. the
+// compiler and runtime use the same names and values.
+// i/o, strings thin wrappers over the cpu's i/o opcodes, plus the two
+// string forms (char* strings and packed byte strings).
+// heap a buddy allocator with a 5 word header on every block.
+// new/free/recycle are built on it. every heap object has at
+// most one owner (the container that holds it), and freeing
+// an owner frees what it owns.
+// growable arrays T[?] handles, built on the heap.
+// mark-sweep a conservative collector. it is the safety net behind
+// free: anything the compiler's ownership analysis could not
+// free gets reclaimed here.
+// memory debug optional checks (use after free, double free, leaks).
+// syscalls file and environment access through one gate word.
+//
+// everything is a 64-bit word, and an address is a word index, not a byte
+// index. the cpu is a stack machine, so "asm { a @ . }" pushes a, fetches the
+// word at that address with @, then outputs it with . (see the vm reference).
+//
+// ---- memory map (words) ----
// src/tools/genmap writes the map values for each build between these markers.
// keep the names and order stable so programs can use the same map interface
@@ -51,7 +51,7 @@ const DSHI = 0x1D4FF0;
const MAGIC = 0x2B7E1516;
// @end-map
-//: frames grow in stable segments so pointers to a caller's locals stay valid.
+// frames grow in stable segments so pointers to a caller's locals stay valid.
word frame_floor = FBOT, frame_top = FTOP, frame_node;
int frame_chunk = FTOP - FBOT, frame_guard = (FTOP - FBOT) >> 2, frame_busy, gc_active;
@@ -61,7 +61,7 @@ word return_low = RS0, return_top = MARK0 - 8, return_node;
const int F_STACK = 1024;
const int F_INTERNAL = 512;
-//: ---- virtual enhancements ----
+// ---- virtual enhancements ----
// install(desc) passes a descriptor to the core through the % opcode. the core
// reads it and installs the region it describes in the cpu, so the compiler can replace
@@ -84,7 +84,7 @@ enhance ishl(a, b) micro "a = shl a b";
enhance lfa(k) micro "r1 = ld 589824; k = add r1 k";
// 1 when i is outside 0..n-1 (the unsigned compare also catches negative i), else 0.
enhance outside(i, n) micro "i = ltu i n; i = xor i 1";
-//: ---- bit operations and float conversions ----
+// ---- bit operations and float conversions ----
// these use single machine instructions where the cpu has them.
@@ -114,7 +114,7 @@ enhance zf2s(a) micro "a = f2s a";
// s2f, the software form used where the cpu has none.
enhance zs2f(a) micro "a = s2f a";
-//: runtime code segments retain the addresses of earlier functions and globals.
+// runtime code segments retain the addresses of earlier functions and globals.
word code_blocks;
// distinguish a raw function address from a heap closure's environment block.
int callable_code(word f) {
@@ -124,13 +124,13 @@ int callable_code(word f) {
return 0;
}
-//: ---- i/o ----
+// ---- i/o ----
-//: output can be redirected. mode 1 drops what is written and mode 2 appends
-//: it to a buffer; mode 3 counts characters without storing them (the server
-//: uses these modes while it checks a document,
-//: so a parse word that prints can't write into the protocol stream).
-//: raw_c always reaches the terminal.
+// output can be redirected. mode 1 drops what is written and mode 2 appends
+// it to a buffer; mode 3 counts characters without storing them (the server
+// uses these modes while it checks a document,
+// so a parse word that prints can't write into the protocol stream).
+// raw_c always reaches the terminal.
int outmode; char* outbuf; int outn, outcap; word outgrow;
// write one character to the terminal with no translation.
@@ -185,12 +185,12 @@ int eq_w(char* a, char* b) {
while (a[i] && a[i] == b[i]) i++;
return a[i] == b[i];
}
-//: ---- word helpers ----
+// ---- word helpers ----
-//: copy and fill by word. copy_w copies forward, so overlapping ranges are only
-//: valid when dst is below src. a long run above the program area goes to the
-//: host in one call (ops 18 and 19 of the syscall gate); a host that answers -1
-//: (a board, the js vm, a range that overlaps itself) gets the plain loop.
+// copy and fill by word. copy_w copies forward, so overlapping ranges are only
+// valid when dst is below src. a long run above the program area goes to the
+// host in one call (ops 18 and 19 of the syscall gate); a host that answers -1
+// (a board, the js vm, a range that overlaps itself) gets the plain loop.
const int SYS_FILL = 18, SYS_COPY = 19;
// copy n words from src to dst.
@@ -342,7 +342,7 @@ bstr n_to_s(int n) {
return p;
}
-//: ---- arithmetic the hardware doesn't have ----
+// ---- arithmetic the hardware doesn't have ----
word[64] pow2;
int zrem;
@@ -380,42 +380,42 @@ int zmod(int a, int b) {
// a shifted left by b bits, for b under 64.
int zshl(int a, int b) { return a * pow2[b & 63]; }
-//: ---- heap ----
+// ---- heap ----
-//: a buddy allocator. the heap is cut into aligned power-of-two chunks, every
-//: block is a power of two words, and a freed block merges with its buddy
-//: (the other half of the block they were split from) whenever that one is
-//: free too, so freed space can be reused at any size. every block has a 5
-//: word header before the pointer the program sees:
-//: p[-5] block size
-//: p[-4] flags in the low 16 bits, size class in the next 5
-//: 0 free (check word cleared) or quarantined (check word still p ^ MAGIC)
-//: 1 live 2 releasing 3 marked 4 quarantined but a word still points at it
-//: bit 8 (value 8) is orthogonal: the object is shared. free is a no-op,
-//: own and release do not follow it, and the sweep still collects it when
-//: nothing points at it. bits 0..2 keep the meanings above.
-//: p[-3] p ^ MAGIC p[-2] owner (the container that owns it, or 0)
-//: p[-1] its type (a type record from new(T), 0 from new(n), -(n + 1) from o3's new_raw)
-//: free blocks sit on a doubly linked list per class, linked through p[0], p[1].
-//: free quarantines: the block stays intact until a sweep finds nothing pointing
-//: at it. recycle is the fast path for a pointer the program has already
-//: dropped: it pushes the block onto its size-class list and does not walk the
-//: heap. a two-word node is still 8 words (the smallest block); a three-word
-//: node fits in 8 instead of 16.
+// a buddy allocator. the heap is cut into aligned power-of-two chunks, every
+// block is a power of two words, and a freed block merges with its buddy
+// (the other half of the block they were split from) whenever that one is
+// free too, so freed space can be reused at any size. every block has a 5
+// word header before the pointer the program sees:
+// p[-5] block size
+// p[-4] flags in the low 16 bits, size class in the next 5
+// 0 free (check word cleared) or quarantined (check word still p ^ MAGIC)
+// 1 live 2 releasing 3 marked 4 quarantined but a word still points at it
+// bit 8 (value 8) is orthogonal: the object is shared. free is a no-op,
+// own and release do not follow it, and the sweep still collects it when
+// nothing points at it. bits 0..2 keep the meanings above.
+// p[-3] p ^ MAGIC p[-2] owner (the container that owns it, or 0)
+// p[-1] its type (a type record from new(T), 0 from new(n), -(n + 1) from o3's new_raw)
+// free blocks sit on a doubly linked list per class, linked through p[0], p[1].
+// free quarantines: the block stays intact until a sweep finds nothing pointing
+// at it. recycle is the fast path for a pointer the program has already
+// dropped: it pushes the block onto its size-class list and does not walk the
+// heap. a two-word node is still 8 words (the smallest block); a three-word
+// node fits in 8 instead of 16.
const int HDR = 5;
-//: p[-4] bit, orthogonal to live / mark / releasing.
+// p[-4] bit, orthogonal to live / mark / releasing.
const int F_SHARE = 8;
-//: flags live in these bits; the size class is above them.
+// flags live in these bits; the size class is above them.
const int F_LOW = 65535;
int hbump; word[32] freel; int gc_runs, gc_freed, gc_auto = 1;
-//: block walks and new chunks stop here.
+// block walks and new chunks stop here.
int hstop;
-//: while nonzero, balloc will not return a block overlapping this range.
+// while nonzero, balloc will not return a block overlapping this range.
int hold_lo, hold_hi;
-//: words of memory the host has (0: the heap cannot grow); hcut: the heap was cut, so it must not grow.
+// words of memory the host has (0: the heap cannot grow); hcut: the heap was cut, so it must not grow.
int hmax, hcut;
-//: memdebug bits, and the line the compiler stamps before new.
+// memdebug bits, and the line the compiler stamps before new.
int dbg, alloc_line;
// the size of the top-level chunk holding heap offset off, within [0, hstop)
@@ -563,7 +563,7 @@ word balloc(int k) {
}
return 0;
}
-//: ---- a heap that grows ----
+// ---- a heap that grows ----
// the heap is [HEAP_LO, hstop). on a map with HEAP_GROW the memory above
// HEAP_HI belongs to the host (a big zeroed array nobody has touched yet), and
@@ -613,7 +613,7 @@ int hfree() {
}
return n;
}
-//: end of the block bfree last made, for the sweep.
+// end of the block bfree last made, for the sweep.
int fend;
// put block b back, merging it with its buddy as long as it can
int bfree(word b) {
@@ -650,10 +650,10 @@ int bdrop(word b) {
// quarantined)? the check word p[-3] == p ^ MAGIC validates the pointer before
// checking it against block metadata; a random value would have to match by chance.
word isobj(word x) { return x >= HEAP_LO + HDR && x < hbump && x[-3] == (x ^ MAGIC) && (x[-4] & 1); }
-//: the number of words to skip in p[0..n) before one that could be a pointer
-//: to a block (see op 20 in core.c); n when none can be. a long payload of
-//: plain numbers costs one host call instead of an isobj per word. a host that
-//: does not have the op (-1) is asked once and then never again.
+// the number of words to skip in p[0..n) before one that could be a pointer
+// to a block (see op 20 in core.c); n when none can be. a long payload of
+// plain numbers costs one host call instead of an isobj per word. a host that
+// does not have the op (-1) is asked once and then never again.
const int SYS_SCAN = 20;
int scan_off;
// walk the n words at p and mark every heap object they point to.
@@ -956,7 +956,7 @@ word tget(word p, int i) {
return p[i];
}
-//: ---- growable arrays ----
+// ---- growable arrays ----
// a T[?] is a handle: [len, cap, items, element size]. items is an object the
// handle owns; growing swaps it for a bigger one and moves its children over.
@@ -1085,12 +1085,12 @@ int dlen(word h) { return h[0]; }
// the capacity of a growable array: how many elements it can hold before it grows.
int dcap(word h) { return h[1]; }
-//: ---- mark-sweep ----
+// ---- mark-sweep ----
-//: conservative and non-moving. roots: the program area (globals live
-//: between functions), the genome's globals, the compiler's input stack,
-//: the live part of the frame stack and the live part of the data stack.
-//: opcode 619 pushes that index. a word below it is not a root.
+// conservative and non-moving. roots: the program area (globals live
+// between functions), the genome's globals, the compiler's input stack,
+// the live part of the frame stack and the live part of the data stack.
+// opcode 619 pushes that index. a word below it is not a root.
int ms_top, ms_over, gc_lo, gc_hi;
word ms_buf = MARK0; int markcap = MARKN;
@@ -1213,12 +1213,12 @@ int scrub() {
return 0;
}
-//: ---- memory debug ----
+// ---- memory debug ----
-//: dbg bits: 1 header checks and allocation sites, 2 poison the payload on free
-//: (the word 57005, which is 0xDEAD), 4 interior pointers keep a quarantined
-//: block (the mark walks back at most 64 words to a real header). off unless
-//: a program requests. sites are p ^ MAGIC so the collector does not treat them as pointers.
+// dbg bits: 1 header checks and allocation sites, 2 poison the payload on free
+// (the word 57005, which is 0xDEAD), 4 interior pointers keep a quarantined
+// block (the mark walks back at most 64 words to a real header). off unless
+// a program requests. sites are p ^ MAGIC so the collector does not treat them as pointers.
const int NSITE = 64;
const int POISON = 57005;
@@ -1301,28 +1301,28 @@ int memreport() {
return n;
}
-//: ---- host syscalls ----
+// ---- host syscalls ----
-//: a store of the op number to SYSGATE runs it. the three words after the gate
-//: are the arguments, and the store leaves the result in the gate.
-//: 1 open(path, 0 read | 1 write-create) 2 close(fd)
-//: 3 read(fd, buf, n) 4 write(fd, buf, n) bytes, one per word
-//: 5 getenv(name, buf, cap) 6 time() unix seconds
-//: 21 argc() 22 argv(i, buf, cap) 23 envc() 24 envp(i, buf, cap)
-//: 40 raw(block) any linux syscall: the block holds the number and six arguments.
-//: the result is the kernel's, or -errno. see Host.raw in src/lib/std.zc
+// a store of the op number to SYSGATE runs it. the three words after the gate
+// are the arguments, and the store leaves the result in the gate.
+// 1 open(path, 0 read | 1 write-create) 2 close(fd)
+// 3 read(fd, buf, n) 4 write(fd, buf, n) bytes, one per word
+// 5 getenv(name, buf, cap) 6 time() unix seconds
+// 21 argc() 22 argv(i, buf, cap) 23 envc() 24 envp(i, buf, cap)
+// 40 raw(block) any linux syscall: the block holds the number and six arguments.
+// the result is the kernel's, or -errno. see Host.raw in src/lib/std.zc
const int SYSGATE = ZFP + 2;
const int SYS_OPEN = 1, SYS_CLOSE = 2, SYS_READ = 3, SYS_WRITE = 4, SYS_GETENV = 5, SYS_TIME = 6;
-/* terminates the host process with the exit status supplied in argument a. */
+// terminates the host process with the exit status supplied in argument a.
const int SYS_EXIT = 7;
const int SYS_BOPEN = 10, SYS_BREAD = 11, SYS_BWRITE = 12, SYS_BGETENV = 13;
const int SYS_RAW = 40;
-//: the command line and the environment. argv holds the program path, which is
-//: <eval> when the source came from stdin, so argc is 1 even with no arguments.
-//: the string ops take an index and copy one entry across, one character per
-//: word like SYS_GETENV: a zero buffer requests the length alone, so the caller
-//: can size the string before it fetches it. src/lib/std.zc builds the arrays.
+// the command line and the environment. argv holds the program path, which is
+// <eval> when the source came from stdin, so argc is 1 even with no arguments.
+// the string ops take an index and copy one entry across, one character per
+// word like SYS_GETENV: a zero buffer requests the length alone, so the caller
+// can size the string before it fetches it. src/lib/std.zc builds the arrays.
const int SYS_ARGC = 21, SYS_ARG = 22, SYS_ENVC = 23, SYS_ENV = 24;
// byte-string paths. the address is the first byte (addr_b of the word after
// the length), not the string object. char* string syscalls are unchanged.
@@ -1333,15 +1333,15 @@ int bread(int fd, bstr s, int n) { return syscall(SYS_BREAD, fd, addr_b(bstr_sto
int bwrite(int fd, bstr s, int n) { return syscall(SYS_BWRITE, fd, addr_b(bstr_storage(s) + 1), n); }
// copy the value of the environment variable name into dst, returning its length.
int bgetenv(bstr name, bstr dst, int n) { return syscall(SYS_BGETENV, addr_b(bstr_storage(name) + 1), addr_b(bstr_storage(dst) + 1), n); }
-//: ---- c abi ----
+// ---- c abi ----
-//: ops 14..17 reach native code (see the c abi section of core.c and extern in
-//: compiler.zc). cfind(name) is 1 if a native symbol resolves (name is a byte
-//: string), cload(path) opens a library so its symbols resolve, and
-//: cbmake(entry, sig) is a native function pointer that runs the zc function at
-//: entry (cfunc(f) in the compiler builds the signature). a native pointer
-//: from cbmake only works while a native call is in progress, and not on the
-//: arm64 jit, where it is 0.
+// ops 14..17 reach native code (see the c abi section of core.c and extern in
+// compiler.zc). cfind(name) is 1 if a native symbol resolves (name is a byte
+// string), cload(path) opens a library so its symbols resolve, and
+// cbmake(entry, sig) is a native function pointer that runs the zc function at
+// entry (cfunc(f) in the compiler builds the signature). a native pointer
+// from cbmake only works while a native call is in progress, and not on the
+// arm64 jit, where it is 0.
const int SYS_CCALL = 14, SYS_CFIND = 15, SYS_CBMAKE = 16, SYS_CLOAD = 17;
// the address of the native symbol name in the loaded libraries, or 0 when it is not