aboutsummaryrefslogtreecommitdiff
path: root/README.md
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 /README.md
parentmerge spec conformance suite (diff)
parentmerge main into work/line-tables (diff)
merge line tables
Diffstat (limited to 'README.md')
-rw-r--r--README.md31
1 files changed, 25 insertions, 6 deletions
diff --git a/README.md b/README.md
index 35e94b6..75d01fb 100644
--- a/README.md
+++ b/README.md
@@ -272,6 +272,25 @@ zy: bad heap pointer
the line is the `new` that made the block. a double free prints and returns; it does not smash the block. `rt.memreport()` prints one line per recorded object that is still live, `leak: line 12 3 words`, and returns how many. the word count is the block's payload, which is a power-of-two chunk minus the 5 word header, not the size you asked for. the table holds 64 sites. freed blocks stay in it until the storage is reused, which is how a double free still knows the line. nothing here changes programs that never say `memdebug`.
+### line tables and backtraces
+
+a fault names the place in the source it came from, and the calls that led there. the compiler records, for every function it compiles, which source line each stretch of its code came from, and the runtime's fatal reports print the call stack after their first line, which stays as it was:
+
+```
+zy: index 4 out of range (length 4)
+ at <stdin>:5 in get
+ at <stdin>:11 in sum
+ at <stdin>:34 in main
+```
+
+the innermost call comes first. `rt.trace()` prints the same lines without stopping. the reports that print one are an out-of-range index, a bad heap pointer, a use after free, a double free, running out of heap memory and a frame overflow. a source read from stdin is named `<stdin>`; an included file goes by its own name. a stack deeper than 28 frames shows the innermost 24, a line `... N more frames` (N counts return-stack words, not only calls), and the outermost 4.
+
+`lines on;` and `lines off;` at the top level turn the recording on and off for the functions that follow. it is on for a program and off for the genome (`src/tools/build.zc` says `lines off;`), so frames in the runtime and the compiler are left out of a trace. methods, closures and `run.evals` code get their own entries; check mode records nothing. each function's table is a block among the code, [next, entry, end, name, file, count, then count words of offset << 32 | line], chained from `rt.line_blocks`, newest first. an image built with `build_image` carries its own chain, and zc0 makes the same tables for a run-only image: `src/tools/automap.sh` puts a comment `//:lines NAME` in front of the program, which turns them on and counts lines from the next one.
+
+the runtime finds the frames through gate op 56, which returns the return-stack pointer as of the gate store (the word after the top return address). the interpreter, both jits, llvm, the microcore (and so wasm and bare metal) and the javascript vm answer it. what the trace cannot see: o3 frames, which are not on the return stack; a function whose code straddles two code segments, which is not recorded; and stack overflows the core reports itself.
+
+the cost: the genome is 395477 words with lines off and would be 410937 (+3.9%) with them on. a run-only image grows by the size of its program's tables, for example 31051 to 31119 words for `tests/programs/trace_run.zc`. compile time is not measurably different: `examples/mandel.zc` compiles and runs in about 22 ms either way on the x86-64 jit, and the genome compiles itself in about 1.2 s either way.
+
bit 4 makes the collector treat a word that falls inside a quarantined payload as a pointer, up to 64 words back to the header. an exact pointer already did this. turn it on when you want `p + k` to keep the block. it can also pin a block that a stale integer happens to land in, so it stays off unless you set the bit.
where paths join (after an `if`, a loop, a `switch` or `?:`) every variable has to be in the same state on every path that reaches the join, with one exception. a variable that became d, or was given away, on some path is dead afterwards, and the compiler frees it on the paths where it's still alive, on their way in. a loop's exit state is the join of its `break`s and its normal exit, so giving something away and breaking is fine. what a loop can't do is give something away on a path that comes back around to the top, since the next pass would use it dead. a u object doesn't get the dead treatment: it still has to leave.
@@ -553,12 +572,12 @@ the initial layout reserves code, compiler tables, the genome, and stacks below
0x080000..0x08FFFF type records (65536 words)
0x090000 frame pointer word (ZFP); 0x090002 is the syscall gate
0x090100..0x113FFF compiler data: interned names, symbol table (16384), locals, fixups, tables
-0x114100..0x163FFF genome: 0x114100 entry, then the symbol table address and the image end (327680 words reserved)
-0x164100..0x1A3FFF frame stack, grows down from 0x1A4100 (262144 words)
-0x1A4100..0x1B40FF return stack, grows up
-0x1B4100..0x1BC0FF mark stack
-0x1BC100..0x1C4FF0 data stack, grows down
-0x1C5000..0x224FFF the heap to start with (0x60000 words), then it grows up to MW
+0x114100..0x17C0FF genome: 0x114100 entry, then the symbol table address and the image end (425984 words reserved)
+0x17C100..0x1BC0FF frame stack, grows down from 0x1BC100 (262144 words)
+0x1BC100..0x1CC0FF return stack, grows up
+0x1CC100..0x1D40FF mark stack
+0x1D4100..0x1DCFF0 data stack, grows down
+0x1DD000..0x23CFFF the heap to start with (0x60000 words), then it grows up to MW
```
the numbers describe the default build's initial reservations. compact compiler and run-only maps start with smaller reservations and also support growth. `build/bin/genmap` writes matching layouts (see "smaller machines"); `--preset fixed` retains the older bounded heap for compatibility. the host answers `core.syscall(9, k)` with facts about the map (0 total words, 1 heap start, 2 heap end for now, 3 bytes). argument 4 requests memory growth, and argument 5 changes the growth limit within the host's backing constraints.