# 22 — memory size the machine was a fixed 1,048,576 words, 8 mb, and the map was copied as hex into `core.c`, `micro.c`, `zc0.c`, `mkgenome.c`, `src/rt.zc`, `src/compiler.zc` and `tools/build.zc`. a board with a couple of megabytes of ram cannot boot that. ## the choice addresses in the genome are literals. a `const` is folded into the symbol table and into every instruction that uses it. the boot stub is `lit FTOP; lit ZFP; store; jump start`, and `lfa` is the recipe string `ld 655360`. three ways to support another size: 1. one map the genome reads at boot, and every address computed from it. that is a different instruction stream. the 1m image would not stay byte-identical, and the whole compiler would be adding bases it currently treats as constants. 2. one set of formulas of `MW` (`HEAP_LO = MW>>2`, `GORG = MW-(MW>>2)`, …) shared by every size. those formulas reproduce the 1m map, and `const SYSGATE = ZFP + 2` was compiled both ways and the self-hosted images matched. they do not leave room below 1m words. the compiler's tables are about 0x24000 words and the image is about 91000. scaling the 1m gaps shrinks the tables with the machine, and at 262144 words the gap from the frame pointer to the genome is 32768 words, which cannot hold them. 3. one genome per size, from one layout. the default sources keep the historical numbers. a smaller build is a copy of those sources with a different const block, compiled by `zc0` against the same `memmap.h` the c cores use. (3) is what landed. it is the smallest change that keeps the 1m image byte-identical. the cost is that a size is a rebuild, not a boot flag: the 1m `genome.h` does not run in the small core, and the small image does not run in the 1m core. `tools/genmap.c` writes the directory (`memmap.h`, `rt.zc`, and for the compiler profile `compiler.zc`). `lfa`'s immediate is rewritten there and nowhere else; the checked-in recipe stays `ld 655360`. `SYSGATE`, the type area, the local-type tables and the exit records are formulas in the shared sources (`ZFP + 2`, `ZFP - 16384`, `R_ASM + 1024`, `stype + NS`). they fold to the old numbers, so the default image does not change. `genmap` rewrites `ZFP - 16384` to `ZFP - 8192` in the small copy only, and lowers the snapshot cap from 8000 to 1000 and the quote cap from 2700 to 300 there too. those three literals stay in `src/compiler.zc`. the 1m cores read `memmap.h`. `MW` has to be a multiple of 4096 so the arm64 `cmp xn, #MW` still fits in a shifted imm12. at 1<<20 that instruction is still `0xf144001f`. ## what was measured on the 1m compiler, after compiling one program (the peaks, not the totals at `main`, except where the counter does not shrink): | | symbols | pool words | snapshot | quote tokens | code words | |---|---|---|---|---|---| | hello | 689 | 5695 | | | 171 | | types | 701 | 5823 | | | 1016 | | o3 | 700 | 5827 | 99 | | 1649 | | meta | 697 | 5811 | 22 | 14 | 685 | | life | 696 | 5781 | 182 | | 852 | | joins | 695 | 5773 | 68 | | 736 | an empty `main` is already ~688 symbols and ~5700 pool words: that is the compiler itself. the data stack high water on the 1m machine was 1028 words for every program tried, including a self-compile. frame depth peaked at 451 during that self-compile and under 350 compiling tests. return depth peaked at 80. the interpreter's data-stack check sits 1100 words above `DSLO`, so the data region has to be at least about 2200 words or a shallow program looks like an overflow. `tests/gc.zc` keeps 1000 live 16-word blocks (16000 words) through its first loop, then allocates far past the heap so the collector runs. the live-object count only matches the 1m output if that first loop does not collect. ## the floors **full compiler: 172032 words, 1376256 bytes.** `genmap small`. stage 27 raised the genome reservation from 90000 to 92000 words, and that machine is now 176128 words. ``` 0 .. 8192 program 8192 .. 28672 heap (20480) 28672 .. 36864 types (8192), not overlapping the heap 36864 ZFP, syscall gate at 36866 37120 .. 75264 compiler tables 75520 genome, 87661 words (zc0), ends at 163181 165520 .. 167568 frame 167568 .. 168080 return 168080 .. 168592 mark (512) 168592 .. 172016 data ``` verified on the microcore and on `core.c`, output matched the 1m interpreter: hello, fact, lists, typed, wiring, life, basics, wide, gc, types, alias, meta, arrays, joins, enhance, o3, csyn, umd, quarantine, and all three stack overflows. both microcores (this one and the run-only one) compile with `-ffreestanding`. **run-only: 32768 words, 262144 bytes.** `genmap run`, then `rt.zc` + `src/run.zc` + one program, compiled by `zc0`. no resident compiler and none of its tables. `src/run.zc` supplies `start` and `gc_roots`. ``` 0 .. 512 unused (code is in the genome) 512 .. 16896 heap (16384) 16928 ZFP, syscall gate at 16930 17152 genome 27392 .. 28416 frame 28416 .. 28672 return 28672 .. 28928 mark (256) 28928 .. 32752 data ``` hello is 9458 words, fact 9558, wide 9523, selfpatch 9578, echo 9458, gc 9666. gc's image ends at 26818, 574 words below the frame stack. output matched, including echo reading `abc` from stdin (there is no backtick, the program is already compiled). 256 kb cannot hold the compiler. 87661 words of image is about 700 kb before a heap, tables or a stack. ## limits - `examples/primes.zc` wants a 60000-word array, a 65536-word buddy block. the small heap is 20480 words and the program stops with `zc: out of heap memory`. the compiler is not the problem; grow the heap in `place_small` and MW follows. - the small machine does not self-host. `tools/build.zc` writes at word `0x10000`, and `build_image` allows `org + 0x30000` words. low memory here is 8192 words. producing the small image is `zc0`'s job, which is also why the small image is 87661 words and the self-hosted 1m image is 91158: those two compilers do not emit identical bytes, only the self-hosted fixpoint does. - `tests/buddy.zc` expects `131072 262144`, the 1m heap's top-level chunks. left out of the subset. - run-only source has to be the `zc0` subset. `tests/basics.zc` uses a block expression and `zc0` rejects it. types, `o3` and `quote` are out too. - the small compiler will die, rather than smash a neighbour, past 1000 snapshot words, 300 quote tokens, 1024 symbols or an 8192-word string pool. the peaks above are well under those. a program that is a lot bigger than `life.zc` or `o3.zc` may need a knob turned up in `genmap`. - the 1m type area still overlaps the top of the heap. that is historical and the image depends on it. the small map does not overlap. - still not a C ABI. a character is still one word. the microcore still has no o3 fast path. stage 25 folded `place_small` and `place_run` into one layout. the sizes above are now `genmap --preset small` and `genmap --preset run`, and a different heap is `--preset small --heap N`. see `history/25-genmap`.