LambLisp 01 Red Fox Alpha
From AI to Actuator
Loading...
Searching...
No Matches

Functions

Sexpr_t Platform_mop3_free_heap (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_cell_pool_free (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_free_internal (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_free_dma (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_largest_free_internal (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_largest_free_dma (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_fs_total (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_fs_used (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_free_stack (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_root_depth (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_root_depth_max (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_root_depth_max_reset (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_eval_progress (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
bool ll_progress_watch_start (uint32_t stale_ms, uint32_t poll_ms)
bool ll_progress_watch_stats (uint32_t *max_quiet_ms, uint32_t *stalls, uint32_t *poll_ms)
bool ll_progress_watch_arm (uint32_t stale_ms, uint32_t poll_ms)
Sexpr_t Platform_mop3_progress_watch_start (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_progress_watch_stats (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_progress_watch_arm (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_micros (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_llarduino_backend (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_llarduino_fmt_int (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_llarduino_fmt_real (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_llarduino_eol (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_millis (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_loop_elapsed_ms (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_loop_elapsed_us (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_lamb_reboot (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_exit (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_delay_ms (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_gc_idle_task (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_gc_collect (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_random_bytevector (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_random_integer (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_random_real (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_cell_block_size (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_n_cell_blocks (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_total_cells (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_nfree (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_max_cell_blocks (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_set_max_cell_blocks (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_extension_block_size (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_set_extension_block_size (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_expand_to_n_blocks (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_sysmem_inuse (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_gc_budget_ns (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_set_gc_budget_ns (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_gcload_target_pct (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_set_gcload_target_pct (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_verbosity (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_set_verbosity (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_repl_quiet (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t Platform_mop3_build_utc (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
const char *const * ll_feature_names (int *count)
Sexpr_t __lamb_platform_generic_install_mop3 (Lamb &lamb, Sexpr_t list_containing_env_target, Sexpr_t env_exec)

Variables

LambPlatform lambPlatform

Detailed Description

LambLisp Platform (generic) builtins.

Function Documentation

◆ Platform_mop3_free_heap()

Sexpr_t Platform_mop3_free_heap ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return bytes of free heap memory on the platform.

◆ Platform_mop3_cell_pool_free()

Sexpr_t Platform_mop3_cell_pool_free ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

THE POOL CELL BLOCKS AND PAYLOADS SHARE. Platform.free-heap is esp_get_free_heap_size() – the AGGREGATE of internal DRAM and PSRAM – which cannot see the pool that actually runs out. cell_pool_free() reports the pool cell blocks are allocated from, and ll_platform_generic.h already names the failure this exposes: the cell heap grows until PSRAM is full and "starves the PAYLOADS (strings, bignum digits, vectors, bytecode) that share that pool".

EXPOSED BECAUSE THE COLLAPSE IS UNINSTRUMENTED. 951 KB can disappear between the last reading and the first alloc_exec failure, with ZERO samples in between – every diagnostic that fires is downstream of the event. A Scheme-visible reading lets bench-timed/run bracket the loss to one benchmark and one phase, which is the one thing none of the existing instrumentation can do.

◆ Platform_mop3_largest_free_dma()

Sexpr_t Platform_mop3_largest_free_dma ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

(Platform.largest-free-dma) – largest CONTIGUOUS DMA-capable block, in bytes.

WHY THIS EXISTS, AND WHY IT REPLACED A PROBE BUILT ON tone. Measuring whether a board is still USABLE after something has eaten its internal memory needs an instrument with two properties, and a driver's init call has neither:

IT MUST BE REPEATABLE. tone() reaches tone_init(), which creates a FreeRTOS queue ONCE. After the first successful call it can never fail again, whatever the memory state – so an instrument built on it reports "fine" forever. Measured on a classic ESP32: a mass NCG pass drove internal free from 26,532 B to 628 B and the tone probe still reported ZERO failures. A ONE-SHOT INITIALISER CANNOT BE A REPEATABLE PROBE. IT MUST NAME THE RESOURCE THAT ACTUALLY STARVES. DMA-capable is STRICTER than internal, and it is what a camera, an I2S ring or a queue needs. Internal headroom can look adequate while every DMA request refuses.

AND LARGEST, NOT FREE, for the reason ncg_exec_pool_init() already records: free is not the number that decides a CONTIGUOUS request. It once printed INTERNAL free=85448 to explain a refusal whose real blocker was a 14,324-byte largest block. Read this beside Platform.free-dma, never instead of it – the pair distinguishes exhaustion from fragmentation, and they need different fixes.

◆ Platform_mop3_fs_total()

Sexpr_t Platform_mop3_fs_total ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Filesystem space, in bytes. Platform.fs-total is the size of the mounted filesystem and Platform.fs-used is what is currently occupied; free space is the difference. Both report 0 where no filesystem is mounted, so "no filesystem" is distinguishable from "full filesystem".

WHY AN APPLICATION SHOULD READ THESE BEFORE A SUSTAINED WRITE. Flash write latency is not flat: it rises sharply as the filesystem fills, because the allocator has to work harder to find and erase space. Measured on an ESP32-S3 with a 128-block partition, one chunked file transfer: 76.7 ms per chunk round trip with the filesystem clean, and 1,211 ms per chunk with ~82 KB of other files present – about sixteen times slower, appearing when those files are added and disappearing when they are removed. The writes still succeed and their checksums still match, so nothing fails and nothing warns; a program with a deadline simply starts missing it, and the delay looks like it came from somewhere else entirely.

So a program that must meet a deadline while writing should check free space first and degrade deliberately – write less, write elsewhere, or decline – rather than discover the cost.

◆ Platform_mop3_free_stack()

Sexpr_t Platform_mop3_free_stack ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return bytes of free stack space on the platform.

◆ Platform_mop3_root_depth()

Sexpr_t Platform_mop3_root_depth ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

(Platform.root-depth) and (Platform.root-depth-max) – how much of the GC root stack is in use now, and the deepest it has ever been. (Platform.root-depth-max!) re-baselines the peak to the current depth so one piece of work can be measured on its own.

WHAT THE ROOT STACK IS, IN ONE SENTENCE: the runtime records there every heap value it is holding on behalf of code that is mid-execution, so the collector cannot reclaim something still in use. Its capacity is fixed at build time, and exhausting it raises a catchable error rather than corrupting anything – so this pair is a capacity-planning instrument, not a fault detector.

READ THEM AS A PAIR, AND PREFER THE PEAK. The instantaneous depth is whatever happens to be held at the moment you ask, which on an idle REPL is near nothing; it cannot tell you what a workload DID. Two very different programs look identical in a single reading – one that nests procedure calls deeply, and one that iterates a long loop accumulating held values – and they have opposite remedies. The peak distinguishes them: deep nesting is bounded by how far the program recurses, while a loop's peak grows with its ITERATION COUNT, so a peak that scales with the size of the input names a loop.

HOW TO USE IT. Call (Platform.root-depth-max!) immediately before the work and (Platform.root-depth-max) immediately after; the difference is that work's demand. Run it at two input sizes. A figure that stays flat as the input grows is bounded by the program's structure and will stay safe on a larger input; one that doubles when the input doubles will not, and the input size at which it would exhaust the stack is worth computing before a deployment rather than discovering in one.

THE PEAK IS NOT RESET BY A COLLECTION and does not fall when values are released – it is a high-water mark, deliberately, because the number that matters for capacity is the worst moment the program has ever reached and not the comfortable one it is in now.

◆ Platform_mop3_root_depth_max_reset()

Sexpr_t Platform_mop3_root_depth_max_reset ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

< the new baseline, so a caller can record it

◆ Platform_mop3_eval_progress()

Sexpr_t Platform_mop3_eval_progress ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

(Platform.eval-progress) – a monotonic token that advances as the evaluator allocates.

WHAT IT IS FOR. A long computation and a stalled interpreter are indistinguishable from outside the VM: both look like a board that is not answering. From inside they are easy to separate – a long computation keeps allocating and a stalled one does not. Sampling this value, waiting, and sampling again therefore distinguishes SLOW from STOPPED, which no timer can do on its own.

HOW TO USE IT. Compare two samples for equality and nothing else: the value is an opaque token, not a count, and it wraps. Sample it from a timer, a supervisory task or a monitoring loop – never from the computation being watched, which is the thing that may be stuck.

READING IT ALLOCATES, so two consecutive reads differ by a few counts even with nothing in between. Compare the SIZE of a delta against a known-idle baseline, not its mere presence: a delta of a handful means nothing happened, a delta of thousands means work is being done.

◆ ll_progress_watch_start()

bool ll_progress_watch_start ( uint32_t stale_ms,
uint32_t poll_ms )

Declared here rather than #included from "ll_vm_progress_watch.h".

THIS FILE SHIPS IN THE CUSTOMER PACKAGE AND THAT HEADER DOES NOT. The release cut copies src/ minus every ll_vm_* and w3* file, so an #include "ll_vm_<anything>.h" here compiles fine in this tree and fails in the customer's with "ll_vm_progress_watch.h: No such file or directory" – a build we do not run and they do. (ll_wasm_browser.cpp includes ll_vm_term.h and is safe only because the include sits inside its #if LL_EMSCRIPTEN.) The SYMBOL is fine: it is compiled into the prebuilt liblamblisp.a that ships alongside, so the link resolves – it is only the header text that is absent. Keep shipped files declaring what they need from the VM, or get it from LambLisp.h, which does ship.

◆ Platform_mop3_progress_watch_start()

Sexpr_t Platform_mop3_progress_watch_start ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

(Platform.progress-watch-start MS) – begin OBSERVING whether the evaluator stalls.

Starts a low-priority sampler that reads the progress token every POLL-MS milliseconds (100 by default, floored at 10) and prints one line if the evaluator has not allocated for MS milliseconds. POLL-MS is the RESOLUTION of the margin that Platform.progress-watch-stats reports, not merely a cost: a gap shorter than one poll cannot be seen at all. It never resets or interrupts anything. Returns #t if a sampler started, #f if one was already running, if the task could not be created, or on a platform where sampling is not provided.

WHAT IT IS FOR. A deadline-driven program cannot tell a long computation from a stuck one by watching the clock, and neither can an operator watching a serial port. This reports the difference: one line, naming how long the evaluator was quiet, with no effect on the program. Leave it running during a long soak and read what the worst legitimate quiet period actually is – that measurement is what makes any later automatic recovery safe to set, and guessing it is how a watchdog ends up restarting healthy work.

MS IS FLOORED AT 100. Below that it measures its own sampling noise rather than the program. A sensible starting point is several times the longest uninterruptible step the application performs; a flash erase or a large allocation can legitimately occupy the evaluator for hundreds of milliseconds.

POLL-MS IS SETTABLE BECAUSE IT IS THE MARGIN'S RESOLUTION, NOT JUST A COST KNOB. The sampler records the gap between allocations only at poll boundaries, so a workload that allocates at least once per poll reports a gap of exactly POLL-MS every time – the FLOOR, indistinguishable from any smaller gap. Measured on an S3 at the 100 ms default: a 19-second allocating workload reported max_quiet_ms=100, i.e. nothing resolvable. To measure the peak rather than the floor, poll faster; the cost is a wakeup per interval and nothing else, since the task reads one word. Floored at 10 ms in ll_progress_watch_start.

< the floor that matters is enforced below; this only stops a negative cast

◆ Platform_mop3_progress_watch_stats()

Sexpr_t Platform_mop3_progress_watch_stats ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

(Platform.progress-watch-stats) – the MARGIN, as (max-quiet-ms stalls poll-ms), or #f.

Returns #f when no sampler is running, which is deliberately not a list of zeros: a watch that never started and a watch that saw nothing produce the same numbers and mean opposite things.

READ ALL THREE OR NONE. max-quiet-ms is the longest interval the evaluator went without allocating, and it is a MARGIN only while stalls is 0 – the field records the largest gap observed, so a deliberate stall raises it too and would then be reporting the stall back to you. And it is an UNDERESTIMATE by up to poll-ms, because a gap that ends just after a sample is recorded short; the worst true gap is at most max-quiet-ms + poll-ms. A threshold taken from the raw figure is optimistic by that much, in the direction that reboots healthy work.

WHAT IT IS FOR: arm the watch, run the real application for as long as you can stand, then read this. The number it gives is how close your workload came to looking stopped – which is the only honest basis for choosing a threshold that is later allowed to reset the unit.

BUILT BACK TO FRONT WITH EACH PARTIAL LIST PROTECTED. cons() protects its own arguments, but the list being accumulated is a local value not reachable from sexpr, so it needs a root across the next allocation – see the mop3 rule in CLAUDE.md. ONE FRAME, THREE PUSHES, NO POPS. A frame records the rootstack depth on entry and restores it on exit, so each re-push needs no matching pop and the depth cannot drift. The re-push after every assignment is required, not redundant: a push records the VALUE at that instant, and res is a different cell after each cons.

◆ Platform_mop3_progress_watch_arm()

Sexpr_t Platform_mop3_progress_watch_arm ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

(Platform.progress-watch-arm MS [POLL-MS]) – let a stall RESET the unit.

Until this is called the watch only reports. Armed, a stall of MS milliseconds records the reason wedged, bumps the same death count a declared (dead!) uses – one anti-loop limit, not two – and resets.

THE ACTUATORS ARE LEFT WHERE THE RESET LEAVES THEM. That is a deliberate decision, not an oversight: the mechanism that knows how to stop this machine runs IN the evaluator, and the watch fires precisely when the evaluator cannot run. Of the three ways out – a separate safing task, a board that de-energises on reset, or accepting it – the owner chose to accept it. On a machine that can move, weigh that before arming.

SO ARM IT LATE AND SET MS GENEROUSLY. A false fire costs a reboot with the outputs wherever they were, which is the expensive direction. Measured on an S3: a real workload's worst quiet period is 330-385 ms during STARTUP and 10-30 ms once running, so arming after the application has loaded puts a 1000 ms threshold ~30x away instead of ~2.6x. Read (Platform.progress-watch-stats) on your own workload before choosing.

◆ Platform_mop3_micros()

Sexpr_t Platform_mop3_micros ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return microseconds since boot as an integer.

◆ Platform_mop3_llarduino_backend()

Sexpr_t Platform_mop3_llarduino_backend ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return milliseconds since boot as an integer. LLArduino introspection. These exist so the layer's Arduino-fidelity guarantees are ASSERTED rather than asserted-in-a-comment: (llarduino-fmt-int 255 16) must be "ff" with no 0x prefix, (llarduino-fmt-real 1.5 2) must be "1.50" not "1.5", and (llarduino-eol) must be "\r\n" – all rendered through the real Print, so a divergence from a device shows up as a test failure.

◆ Platform_mop3_loop_elapsed_ms()

Sexpr_t Platform_mop3_loop_elapsed_ms ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return elapsed milliseconds since the start of the current Arduino loop() call.

◆ Platform_mop3_loop_elapsed_us()

Sexpr_t Platform_mop3_loop_elapsed_us ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return elapsed microseconds since the start of the current Arduino loop() call.

◆ Platform_mop3_lamb_reboot()

Sexpr_t Platform_mop3_lamb_reboot ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Reboot the platform (embedded) or do nothing (POSIX).

◆ Platform_mop3_exit()

Sexpr_t Platform_mop3_exit ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Flush stdio and exit (POSIX) or reboot (embedded).

< release platform resources

< embedded: reboot after cleanup

< unreachable

◆ Platform_mop3_delay_ms()

Sexpr_t Platform_mop3_delay_ms ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Delay for the given number of milliseconds using platform delay().

◆ Platform_mop3_gc_idle_task()

Sexpr_t Platform_mop3_gc_idle_task ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Run the GC idle task for up to the given number of microseconds.

◆ Platform_mop3_gc_collect()

Sexpr_t Platform_mop3_gc_collect ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Run one complete GC cycle (mark + sweep); use before benchmarks to reset GC phase.

◆ Platform_mop3_random_bytevector()

Sexpr_t Platform_mop3_random_bytevector ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return a bytevector of n cryptographically random bytes.

◆ Platform_mop3_random_integer()

Sexpr_t Platform_mop3_random_integer ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return a cryptographically random integer.

◆ Platform_mop3_random_real()

Sexpr_t Platform_mop3_random_real ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return a cryptographically random float32 in [0, 1).

< [0, 2^64-1] * 2^-64 → [0,1) exact

◆ Platform_mop3_cell_block_size()

Sexpr_t Platform_mop3_cell_block_size ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return the number of cells in the first (fixed) memory block.

◆ Platform_mop3_n_cell_blocks()

Sexpr_t Platform_mop3_n_cell_blocks ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return the current number of allocated memory blocks.

◆ Platform_mop3_total_cells()

Sexpr_t Platform_mop3_total_cells ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

THE CELL POOL, computed ONCE in the VM. Callers used to reconstruct it as nblocks * cell_block_size which is right only if every block is the same size. With a 65,536-cell base block and 8,192-cell expansions that overstated the pool by 7.2x (4,194,304 reported against a true 581,632), and every occupancy figure derived from it was wrong by the same factor. total_cells() is baseBlockSize + NexpansionBlocks * expansionBlockSize – ask the VM, do not re-derive it.

◆ Platform_mop3_nfree()

Sexpr_t Platform_mop3_nfree ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return the current number of free cells in the GC heap.

◆ Platform_mop3_max_cell_blocks()

Sexpr_t Platform_mop3_max_cell_blocks ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return the maximum number of GC blocks allowed before the memory_cap_reached warning fires.

◆ Platform_mop3_set_max_cell_blocks()

Sexpr_t Platform_mop3_set_max_cell_blocks ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Set the maximum number of GC blocks before memory_cap_reached warning fires.

◆ Platform_mop3_set_extension_block_size()

Sexpr_t Platform_mop3_set_extension_block_size ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Set the number of cells in each future extension memory block.

◆ Platform_mop3_expand_to_n_blocks()

Sexpr_t Platform_mop3_expand_to_n_blocks ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Expand the heap to at least n blocks; allocates extension blocks as needed.

◆ Platform_mop3_sysmem_inuse()

Sexpr_t Platform_mop3_sysmem_inuse ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

(Platform.gc-diag) -> (pass-max-us urgent-max-us urgent-calls). Reported through the RESULT of a mop3 so the bench can display it – warn() output never survives the embedded path (W3_REPL_QUIET at the source, plus serial_run.py dropping any line naming LambMemory or gc_pass), so a warn-based probe silently reports nothing and looks like a clean result. (Platform.sysmem-inuse) -> bytes currently held by the SYSTEM allocator, or -1.

THE CELL POOL CANNOT ANSWER THE FINALIZATION QUESTION, which is why this exists. A vector, bytevector or heap string keeps its ELEMENTS in a malloc'd block and only its handle in a cell – measured: a (make-vector 5000) moved the free-cell count by FIVE. So a backing block that is never freed when its cell is collected is a real leak that every cell-based counter reports as clean. This reads the allocator directly.

-1 means "this build cannot tell", NOT "zero leaked": mallinfo2 is glibc-only, so wasm, MinGW and the ESP32 return -1 and a caller must treat that as UNKNOWN and skip, never as a pass.

◆ Platform_mop3_gc_budget_ns()

Sexpr_t Platform_mop3_gc_budget_ns ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return the GC time budget per loop iteration in nanoseconds.

◆ Platform_mop3_set_gc_budget_ns()

Sexpr_t Platform_mop3_set_gc_budget_ns ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Set the GC time budget per loop iteration in nanoseconds.

◆ Platform_mop3_gcload_target_pct()

Sexpr_t Platform_mop3_gcload_target_pct ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return the target GC load as a percentage of loop time.

◆ Platform_mop3_set_gcload_target_pct()

Sexpr_t Platform_mop3_set_gcload_target_pct ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Set the target GC load percentage (0–100).

◆ Platform_mop3_verbosity()

Sexpr_t Platform_mop3_verbosity ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Return the current verbosity level (0 = silent, higher = more logging).

◆ Platform_mop3_set_verbosity()

Sexpr_t Platform_mop3_set_verbosity ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Set the verbosity level.

◆ Platform_mop3_repl_quiet()

Sexpr_t Platform_mop3_repl_quiet ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Quiet REPL: enable/disable line-mode quiet console (suppress async log() output and the per-char redisplay() prompt-reprint). The LLIP tunnel uses this – (llt) sets it on, (en) clears it – so a tool's frames aren't garbled by the prompt-redraw flood.

◆ Platform_mop3_build_utc()

Sexpr_t Platform_mop3_build_utc ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

(lamb-build-utc) -> the compile-time W3_BUILD_UTC of the RUNNING binary.

WHY THIS NEEDS TO BE REACHABLE FROM SCHEME. It is the one value that identifies WHICH BINARY is executing, and until now it existed only in the boot banner – readable solely from a serial transcript, which requires a RESET to capture. That makes it unusable for the thing it is most needed for: confirming an OTA replaced the image, over the network, on a board with no cable attached. Resetting to find out defeats the purpose.

IT IS THE ONE OBSERVABLE NO TOOL IN THE CHAIN CAN FABRICATE. The binary reports its own identity, so a verb cannot claim a successful update by exiting 0 – and several verbs in this toolset have been found doing exactly that. A changed stamp is proof the image changed; an unchanged stamp is proof it did not, whatever anything else reported.

NOTE it dates the BUILD, not the source: a dirty tree produces a binary that is no commit at all (see the header of src/w3_build.h). Use it to answer "is this a different binary than before", which is a question it answers exactly, and never "which commit is this".

◆ ll_feature_names()

const char *const * ll_feature_names ( int * count)

The feature identifiers reported by (features) and matched by cond-expand.

(features) – R7RS (scheme base): the list of feature identifiers this implementation provides (kept consistent with cond-expand).

ONE list, two consumers – see the declaration in ll_platform_generic.h for why.

Only MECHANICALLY DERIVABLE identifiers belong here: the implementation, the platform, the CPU and the byte order, each read from a compile-time macro so it cannot disagree with the binary it is compiled into. A feature that asserts a LANGUAGE property – full-unicode, exact-closed, ratios – is a promise about behaviour and is added only once something tests that the promise holds. Claiming one that is only PARTLY true is worse than omitting it: a program guarding on it takes the wrong branch and says nothing, whereas a missing feature makes it take the conservative branch. full-unicode is the live example – the reader and writer handle every codepoint to U+10FFFF, but char-upcase and friends cover ASCII and Latin-1 only, so the feature is NOT reported.

Platform. LL_WINDOWS and LL_WASM both imply LL_POSIX and then subtract from it, so they are tested FIRST and the posix/unix/linux arm excludes them – see the LL_WINDOWS and LL_WASM commentary in ll_platform_generic.h.

CPU.

Byte order.

◆ __lamb_platform_generic_install_mop3()

Sexpr_t __lamb_platform_generic_install_mop3 ( Lamb & lamb,
Sexpr_t list_containing_env_target,
Sexpr_t env_exec )

REGISTERED ONLY WHEN THE INSTRUMENT IS COMPILED IN, so (defined? 'Platform.root-depth-max) is a TRUE answer about this build. A -1 return is the right signal for a C++ caller and the wrong one as the ONLY signal for a Scheme caller: root-balance-tests.scm guards its depth-boundedness arm on defined? and carries a correct skip branch ("depth boundedness is unmeasured on this build"), and leaving these bound with LL_ROOT_HIGH_WATER=0 made that skip UNREACHABLE – the arm then compared p-base/p-sm/p-big/p-loop, every one of them -1, so every comparison held and the suite reported depth boundedness as PROVEN on a board while measuring nothing. A degenerate pass is worse than a failure: it is indistinguishable from a real result and it removes the reason to look again. Caught before it ran on hardware. Platform.root-depth STAYS REGISTERED unconditionally – it reads Nroots directly, costs nothing and is always true.