--- title: "Stone Memory" description: "Immutable arena allocation" --- ## Overview Stone memory is a separate allocation arena for immutable values. Objects in stone memory are permanent — they are never moved, never freed, and never touched by the garbage collector. The `stone()` function in ƿit petrifies a value, deeply freezing it and all its descendants. Stoned objects have the S bit set in their object header. ## The Stone Arena Stone memory uses bump allocation from a contiguous arena: ``` stone_base ──────── stone_free ──────── stone_end [allocated objects] [free space ] ``` Allocation advances `stone_free` forward. When the arena is exhausted, overflow pages are allocated via the system allocator and linked together: ```c struct StonePage { struct StonePage *next; size_t size; uint8_t data[]; }; ``` ## The S Bit Bit 3 of the object header is the stone flag. When set: - The object is **immutable** — writes disrupt - The object is **excluded from GC** — the collector skips it entirely - For text objects, the length field caches the **hash** instead of the character count (since the text cannot change, the hash is computed once and reused) ## What Gets Stoned When `stone(value)` is called: 1. If the value is already stone, return immediately 2. Recursively walk all nested values (array elements, record fields, etc.) 3. Copy each mutable object into the stone arena 4. Set the S bit on each copied object 5. Return the stoned value The operation is deep — an entire object graph becomes permanently immutable. ## Text Interning The stone arena maintains a hash table for text interning. When a text value is stoned, it is looked up in the intern table. If an identical string already exists in stone memory, the existing one is reused. This deduplicates strings and makes equality comparison O(1) for stoned text. The hash is computed with `fash64` over the packed UTF-32 words. ## Usage Patterns ### Module Return Values Every module's return value is automatically stoned: ```javascript // config.cm return { debug: true, timeout: 30 } // The returned object is stone — shared safely between actors ``` ### Message Passing Messages between actors are stoned before delivery, ensuring actors never share mutable state. ### Constants Literal objects and arrays that can be determined at compile time may be allocated directly in stone memory. ## Mutable Text Concatenation String concatenation in a loop (`s = s + "x"`) is optimized to O(n) amortized by leaving concat results **unstoned** with over-allocated capacity. On the next concatenation, if the destination text is mutable (S bit clear) and has enough room, the VM appends in-place with zero allocation. ### How It Works When the VM executes `concat dest, dest, src` (same destination and left operand — a self-assign pattern): 1. **Inline fast path**: If `dest` holds a heap text, is not stoned, and `length + src_length <= capacity` — append characters in place, update length, done. No allocation, no GC possible. 2. **Growth path** (`JS_ConcatStringGrow`): Allocate a new text with `capacity = max(new_length * 2, 16)`, copy both operands, and return the result **without stoning** it. The 2x growth factor means a loop of N concatenations does O(log N) allocations totaling O(N) character copies. 3. **Exact-fit path** (`JS_ConcatString`): When `dest != left` (not self-assign), the existing exact-fit stoned path is used. This is the normal case for expressions like `var c = a + b`. ### Safety Invariant **An unstoned heap text is uniquely referenced by exactly one slot.** This is enforced by the `stone_text` mcode instruction, which the [streamline optimizer](streamline.md#7-insert_stone_text-mutable-text-escape-analysis) inserts before any instruction that would create a second reference to the value (move, store, push, setarg, put). Two VM-level guards cover cases where the compiler cannot prove the type: `get` (closure reads) and `return` (inter-frame returns). ### Why Over-Allocation Is GC-Safe - The copying collector copies based on `cap56` (the object header's capacity field), not `length`. Over-allocated capacity survives GC. - `js_alloc_string` zero-fills the packed data region, so padding beyond `length` is always clean. - String comparisons, hashing, and interning all use `length`, not `cap56`. Extra capacity is invisible to string operations. ## Relationship to GC The Cheney copying collector only operates on the mutable heap. During collection, when the collector encounters a pointer to stone memory (S bit set), it skips it — stone objects are roots that never move. This means stone memory acts as a permanent root set with zero GC overhead.