Historical internal name "uncommonlisp" retired in favor of the
public name "lumbda" ahead of lumbda.com going live. Scope of
this commit:
Source files renamed:
uncommonlisp.py -> lumbda.py
asm/uncommonlisp.s -> asm/lumbda.s
c/uncommonlisp.h -> c/lumbda.h
whitepaper/uncommonlisp-whitepaper -> whitepaper/lumbda-whitepaper (.rst + .pdf)
Binaries renamed (tracked ones; c/ was always gitignored):
asm/uncommonlisp, asm/uncommonlisp-gc, asm/uncommonlisp.o,
asm/uncommonlisp-gc.o -> asm/lumbda(-gc)(.o)
c/.gitignore -> ignores lumbda
Internal string updates (sed pass ordered longest-first):
asm/uncommonlisp -> asm/lumbda
c/uncommonlisp -> c/lumbda
uncommonlisp.py -> lumbda.py
UNCOMMONLISP_BIN -> LUMBDA_BIN (asm/test.sh env var)
"uncommonlisp> " -> "lumbda> " (asm REPL prompt baked into binary)
UNCOMMONLISP -> LUMBDA (macros, comments)
uncommonlisp -> lumbda (prose)
Binary portal magic updated:
"ULPORTAL" -> "LUMBDAB1" # "Lumbda Binary v1"
Old portal files are not backward-compatible — this is a deliberate
break since it's the rename moment. S-expression portals already
carry their own ";; lumbda-portal v1" header and remain cleanly
versioned.
WHITEPAPER.pdf / WHITEPAPER.rst symlinks repointed to the renamed
files. Makefile's whitepaper target targets lumbda-whitepaper.pdf.
Not changed (intentional, separate phases):
- Filesystem directory /home/fox/git/uncommonlisp itself
(fox renames locally and the gitlab repo URL in a follow-up)
- tests.py hardcoded cwd=/home/fox/git/uncommonlisp
(matches the current on-disk location; will flip when the
directory rename ships)
- Git history (immutable; old commits still say uncommonlisp,
which is correct — that's what they were)
Verified:
137 asm no-GC + 137 asm GC + 571 Python + 83 C + 189 shared
functional tests all pass under the new names.
bench-gc-http (2000 req): all 4 cells behave as expected
(cells 1/2 flat, 3 leaks, 4 bounded at 1 chunk).
Python REPL, C REPL, asm REPL all start cleanly.
Root-cause fix for the residual crashes I had documented as known
issues in §6.6.4. Every heap_alloc call site was setting its type
byte with `orq $(HT_X << 8), -8(%rax)` — but OR merges with the
stale type byte from a free-list-reused block. A pair previously
used as a vector (type 5 = 0b101) re-allocated as pair (type 1 =
0b001) ends up with merged type 0b101 = still vector. Walker then
treats the pair as a vector, reads the pair's car as a "length",
and walks off the block end — hence the hash-set bench's "unbound
variable: t", memory bench's "unbound variable: lst", arena bench's
"unbound variable: k".
Fix: overwrite the byte instead of OR-ing. 18 sites converted from
`orq $(HT_X << 8), -8(%rax)` to `movb $HT_X, -7(%rax)`. Every
previously-residual crash gone on first rerun.
Refreshed benchmark numbers throughout §6.6:
§6.6 Memory table: 122× less memory at 26% slowdown (was 124×,
30%). Range shifted because the fix also accelerated the common
paths; ratio stable.
§6.6.4 HTTP soak at 50,000 requests × 16 concurrent × 4 cells:
no-GC + snapshot 630 req/s peak 100 KB growth 4 KB
GC + snapshot 633 req/s peak 120 KB growth 4 KB
no-GC + no snapshot 625 req/s peak 458 MB OOM at cap
GC + no snapshot 610 req/s peak 1,092 KB growth 852 KB
Cell 4 now sustains 50K requests with steady-state 1-chunk memory.
Previous residual edge at 50K (cell 4 failing to start) was a
manifestation of the same type-byte bug, now gone.
§6.6.3 Adaptive numbers collapsed to within ~1% across all three
workloads (was 6% / 7% / 17% deltas). Paper updated to honestly
report adaptive as a null experiment on these shapes — neutral
cost, same stats surface, default on.
§6.6 diagram: bench-gc.png refreshed to match new numbers.
137 asm no-GC + 137 asm GC + 189 shared functional all pass.
Hash-set / memory / arena / adaptive / HTTP benches all clean.
Adds §6.6.3 "Collaborative Meta-GC: From Greedy to Adaptive" with
the three-workload benchmark (friendly / hostile / mixed × greedy
/ adaptive). Honest read of the numbers:
friendly greedy 732 ms 1000 resets, 0 escapes
friendly adaptive 691 ms 1000 resets, 0 escapes (-6%)
hostile greedy 568 ms 0 resets, 1000 escapes
hostile adaptive 607 ms 0 resets, 1000 escapes, 11 skipped (+7%)
mixed greedy 1981 ms 17 resets, 1983 escapes
mixed adaptive 1694 ms 14 resets, 1986 escapes, 2 skipped (-17%)
Adaptive wins on friendly (-6%) and mixed (-17%, the policy's
design target). On fully hostile workloads implicit GC fires 982
of 1000 arenas before the dispatcher sees them, so the signal is
drowned and greedy happens to edge adaptive by ~7%. Section
explicitly calls out the collaborative-but-local structure
(shared state on arena_active + EMA + countdown, decisions made
locally by each component) and credits the benchmark work with
surfacing two real correctness bugs in the conservative stack
scan — 24-byte strings misread as env nodes, 40-byte strings
misread as 25-element vectors — both now fixed.
Also:
- meta-gc-policy.dot rewritten to show the adaptive gate
(rate > 50% + probe countdown) before the greedy verify path;
new skip branch, new EMA annotations on edges.
- §6 reproducibility list + Makefile bench-gc-adaptive target.
- PDF rebuilt at 2.64 MB.
137 asm no-GC + 137 asm GC + 189 shared functional tests pass
against the new asm.
Fox flagged that the "A diagram is worth 10,000 words" quote
appeared twice in the paper but nothing was actually illustrated.
Fixed by:
1. Refreshing every .dot source to match current reality:
- docs/asm-architecture.dot: 22 KB (was "13 KB"), 14 syscalls
(was 4), 91 builtins (was 34), djb2 hash (was "linear scan"),
TCP stack + heap-snapshot + portal boxes added.
- docs/benchmark-sumto.dot: sum-to(1M) i5-8350U numbers; C
--fast 238 ms, asm 670 ms, Python --fast 5,136 ms. Was
sum-to(50k) with stale numbers.
- docs/benchmark-ack.dot: ackermann(3,8) i5-8350U numbers. Was
ack(3,4) with stale numbers.
- docs/benchmark-binary-size.dot: asm 22 KB, C 205 KB, busybox
2.1 MB, python3 8.0 MB. Was comparing against different
baselines.
2. Regenerated all PNGs via `make docs`.
3. Embedded in the paper at meaningful points:
- §2 Architecture (Python): python-architecture.png
- §6.4 Three-way bench: benchmark-sumto.png, benchmark-ack.png
- §11 Three Implementations: c-architecture.png, asm-
architecture.png
- §11.3 HTTP + sockets: benchmark-binary-size.png
4. Removed the redundant quote from §12.3; the one in §11
remains because §11 now follows it with two real diagrams.
Prerequisite fox noted: "make sure diagrams are up to date before
using them to code." Done — every embedded figure has the current
numbers/topology, not the old ones.
5 new diagrams (Graphviz DOT → PNG):
benchmark-ack.dot — ack(3,4) across all 5 tiers
benchmark-sumto.dot — sum-to(50k) across all 5 tiers
benchmark-fib.dot — fib(35) iterative across all 5 tiers
benchmark-speedup.dot — JIT speedup ratios (7x-784x)
benchmark-binary-size.dot — 13KB asm vs 171KB C vs ~30MB Python
JIT: 0.19ms ack, 7x faster than CPython, 784x faster than Python VM.
Makefile docs target now auto-discovers all docs/*.dot files.
Fresh in-process benchmarks across all implementations:
JIT: ack 0.19ms, fib 0.09ms, sum 0.55ms
CPython: ack 1.3ms, fib 0.006ms, sum 5.5ms
C interp: ack 20ms, fib 0.06ms, sum 109ms
Python VM: ack 149ms, fib 0.75ms, sum 437ms
Assembly: ack 8ms, fib 0.6ms, sum 43ms
JIT runs Scheme 7-10x faster than CPython runs Python.
Updated: language identified as R7RS Scheme throughout.
Test count updated to 943 across all implementations.
4 architecture diagrams (Graphviz DOT → PNG):
python-architecture.dot — bytecode VM + continuations + portal
c-architecture.dot — tree-walker + VM + JIT tiers
asm-architecture.dot — syscalls-only, 13KB binary
jit-pipeline.dot — AST → x86_64 machine code flow
docs/README.md — full architecture docs with embedded diagrams
and performance summary across all implementations.
Makefile: add asm-repl, docs target, clean-docs. Header comments
document all targets and test suites.
CLAUDE.md: add "A diagram is worth 10,000 words" (russell@unturf.com),
implementation table, test suite inventory.
Assembly is 2.5-4x faster than C interpreter on recursive workloads.
JIT remains 33x faster than hand-written assembly.