Skip to main content

logicaffeine_compile/codegen_riscv/
runtime.rs

1//! The C runtime shim compiled alongside the emitted `main.s`: the heap (text, sequences, maps, records, closures), the
2//! formatters and the task scheduler, kept in C (not hand-rolled asm) for the same reason the AOT-C tier calls libc:
3//! correctness over ceremony. Every block comes from the C tier's collector (`codegen_c::runtime::C_GC_COLLECTOR`),
4//! emitted first, so a program holds only the memory it can still reach, as on every other tier.
5
6/// `logos_show_f64` renders an f64 EXACTLY like the VM's `Show` (Rust's `{}` Display: shortest decimal
7/// that round-trips, NEVER scientific notation, bare integral floats, `NaN`/`inf`/`-inf`/`-0`). It finds
8/// the shortest significant-digit count whose `%e` form round-trips via `strtod`, then reformats that to
9/// fixed decimal notation so any magnitude prints without an exponent — matching Rust for every finite
10/// value. Called with the double in `fa0` (a named param, so no variadic-FP marshalling needed).
11pub const RISCV_RUNTIME_C: &str = concat!(
12    crate::codegen_c::runtime::lc_gc_collector_source!(),
13    crate::codegen_c::runtime::lc_gc_api_source!(),
14r#"
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <errno.h>
19#include <stdint.h>
20#include <math.h>
21#include <time.h>
22#include <ucontext.h>
23#include <unistd.h>
24
25/* Nothing in a program unwinds: a failure prints its sentence and exits, and a task switches stacks by its
26   context. So the runtime's call-frame information goes to `.debug_frame`, which nothing reads at run time,
27   rather than `.eh_frame`, every entry of which a static binary's startup registers before `main`. */
28__asm__(".cfi_sections .debug_frame");
29
30/* A failure: the program stops with its reason alone on stderr, exit status 1 — the report every tier
31   gives (`FailWith`, `Fail with …`, a broken contract's reason, a runtime error) — or, inside a task of
32   a concurrent program, the task stops and never returns here, and the run fails with its first
33   failure once every other task is quiescent (the scheduler installs `logos_fail_hook` for the run). */
34static void (*logos_fail_hook)(const char *) = 0;
35
36__attribute__((noreturn)) void logos_fatal(const char *reason) {
37    fflush(stdout);
38    if (logos_fail_hook) logos_fail_hook(reason);
39    fprintf(stderr, "%s\n", reason);
40    exit(1);
41}
42
43__attribute__((noreturn)) static void logos_fatalf(const char *fmt, long long arg) {
44    char buf[256];
45    snprintf(buf, sizeof buf, fmt, arg);
46    logos_fatal(lc_gc_strdup(buf));
47}
48
49/* Rust's `char::is_whitespace`. */
50static int logos_is_space(unsigned cp) {
51    return (cp >= 0x09 && cp <= 0x0D) || cp == 0x20 || cp == 0x85 || cp == 0xA0 || cp == 0x1680
52        || (cp >= 0x2000 && cp <= 0x200A) || cp == 0x2028 || cp == 0x2029 || cp == 0x202F || cp == 0x205F
53        || cp == 0x3000;
54}
55
56/* The code point UTF-8 encodes at `s` into `cp`, answering its length in bytes. */
57static int logos_utf8_at(const unsigned char *s, unsigned *cp) {
58    if (s[0] < 0x80) { *cp = s[0]; return 1; }
59    if (s[0] < 0xE0) { *cp = ((s[0] & 0x1Fu) << 6) | (s[1] & 0x3Fu); return 2; }
60    if (s[0] < 0xF0) { *cp = ((s[0] & 0x0Fu) << 12) | ((s[1] & 0x3Fu) << 6) | (s[2] & 0x3Fu); return 3; }
61    *cp = ((s[0] & 0x07u) << 18) | ((s[1] & 0x3Fu) << 12) | ((s[2] & 0x3Fu) << 6) | (s[3] & 0x3Fu);
62    return 4;
63}
64
65/* `str::trim` — a copy of `s` without its leading and trailing whitespace. */
66static char *logos_trim(const char *s) {
67    const unsigned char *b = (const unsigned char *)s, *e = b + strlen(s);
68    unsigned cp;
69    while (b < e) {
70        int n = logos_utf8_at(b, &cp);
71        if (!logos_is_space(cp)) break;
72        b += n;
73    }
74    while (e > b) {
75        const unsigned char *p = e - 1;
76        while (p > b && (*p & 0xC0) == 0x80) p--;
77        logos_utf8_at(p, &cp);
78        if (!logos_is_space(cp)) break;
79        e = p;
80    }
81    size_t n = (size_t)(e - b);
82    char *out = (char *)lc_gc_alloc_atomic(n + 1);
83    memcpy(out, b, n);
84    out[n] = '\0';
85    return out;
86}
87
88/* The sentences an operation that fails stops the program with — the VM's own, which the emitter takes from
89   the VM and emits beside the program, and which a helper reads only on its way to stopping:
90   `logos_index_refusal` is a format of the index (`%lld`), `logos_missing_key_refusal`, `logos_parse_int_refusal`
91   and `logos_parse_float_refusal` of a text (`%s`), `logos_random_bounds_refusal` of the two bounds
92   (`%1$lld`, `%2$lld`), `logos_utf8_invalid_refusal` of a sequence's length and start (`%1$d`, `%2$lld`) and
93   `logos_utf8_incomplete_refusal` of a start (`%lld`). */
94extern const char logos_index_refusal[], logos_missing_key_refusal[], logos_exact_division_refusal[],
95    logos_float_division_refusal[],
96    logos_negative_exponent_refusal[], logos_exponent_too_large_refusal[], logos_parse_int_refusal[],
97    logos_parse_float_refusal[], logos_random_bounds_refusal[], logos_utf8_invalid_refusal[],
98    logos_utf8_incomplete_refusal[], logos_await_without_listen_refusal[];
99
100/* The 0-based cell of index `i` among `len` that is not a 1-based one in range — the rest of the VM's
101   `resolve_index`: `-i` counts from the back, and any other index stops the program. Every accessor tests the
102   1-based index in range itself, inline, and calls this out of line for everything else. */
103__attribute__((noinline, cold)) static long long logos_index_from_back(long long i, long long len) {
104    if (i <= -1 && (unsigned long long)0 - (unsigned long long)i <= (unsigned long long)len) return len + i;
105    logos_fatalf(logos_index_refusal, i);
106}
107
108/* The 0-based cell of 1-based index `i` among `len` (the VM's `resolve_index`). */
109static inline long long logos_resolve_index(long long i, long long len) {
110    if (__builtin_expect(i < 1 || i > len, 0)) return logos_index_from_back(i, len);
111    return i - 1;
112}
113
114/* Render an f64 into a fresh string EXACTLY like Rust's `{}` Display (see logos_show_f64). */
115char *logos_fmt_f64(double x) {
116    if (isnan(x)) return lc_gc_strdup("NaN");
117    if (isinf(x)) return lc_gc_strdup(x < 0 ? "-inf" : "inf");
118
119    /* Shortest significant-digit count whose scientific form round-trips. */
120    char sci[64];
121    int prec;
122    for (prec = 0; prec <= 17; prec++) {
123        snprintf(sci, sizeof sci, "%.*e", prec, x);
124        if (strtod(sci, NULL) == x) break;
125    }
126
127    /* Parse sci = [-]D[.FFF]e[+-]EE into a sign, a digit string, and a decimal exponent. */
128    const char *p = sci;
129    char sign = 0;
130    if (*p == '-') { sign = '-'; p++; }
131    char digits[40];
132    int nd = 0;
133    digits[nd++] = *p++;                 /* leading digit D */
134    if (*p == '.') { p++; while (*p != 'e' && *p != 'E') digits[nd++] = *p++; }
135    p++;                                  /* skip 'e' */
136    int exp = atoi(p);                    /* decimal point sits after (exp+1) digits from the start */
137    int pointpos = exp + 1;
138
139    /* Reformat as fixed decimal (no exponent), matching Rust's Display. */
140    char *out = (char *)lc_gc_alloc_atomic(600);
141    int oi = 0;
142    if (sign) out[oi++] = '-';
143    if (pointpos <= 0) {
144        out[oi++] = '0';
145        out[oi++] = '.';
146        for (int i = 0; i < -pointpos; i++) out[oi++] = '0';
147        for (int i = 0; i < nd; i++) out[oi++] = digits[i];
148    } else if (pointpos >= nd) {
149        for (int i = 0; i < nd; i++) out[oi++] = digits[i];
150        for (int i = 0; i < pointpos - nd; i++) out[oi++] = '0';
151    } else {
152        for (int i = 0; i < nd; i++) {
153            if (i == pointpos) out[oi++] = '.';
154            out[oi++] = digits[i];
155        }
156    }
157    out[oi] = '\0';
158    return out;
159}
160
161void logos_show_f64(double x) {
162    char *s = logos_fmt_f64(x);
163    printf("%s\n", s);
164    lc_gc_free(s);
165}
166
167/* ---- Text (heap char*) ---- */
168
169/* Stringify one slot value to a fresh string, by its static kind (0=Int, 1=Float, 2=Bool,
170 * 3=Text) — the AOT analog of `to_display_string`. `bits` is the raw 64-bit slot content (an i64, an
171 * f64 bit pattern, 0/1, or a `char*`). */
172"#,
173    crate::codegen_c::runtime::exact_numbers_source!(),
174    r#"
175/* ---- Rational: a handle to an exact number of the section above (an `LDec`), which follows the VM's rules for
176   two exact numbers — a whole rational answers `/`, `%`, `//`, `**` and the bitwise operators as the integer it
177   equals. An operand is a rational handle (`flag` 1) or a machine Int (`flag` 0). ---- */
178
179static long long logos_ldec_box(LDec d) {
180    LDec *p = (LDec *)lc_gc_alloc(sizeof(LDec));
181    *p = d;
182    return (long long)p;
183}
184
185static LDec logos_ldec_of(long long v, int flag) { return flag ? *(const LDec *)v : ldec_from_i64(v); }
186
187/* An Int as the rational it equals. */
188long long logos_ldec_of_int(long long v) { return logos_ldec_box(ldec_from_i64(v)); }
189
190/* `a op b`, a rational: op 0 `+`, 1 `-`, 2 `*`, 3 `/`, 4 the exact `/`, 5 `//`, 6 `%`, 7 `**`. */
191long long logos_ldec_op(long long a, int af, long long b, int bf, int op) {
192    LDec x = logos_ldec_of(a, af), y = logos_ldec_of(b, bf);
193    switch (op) {
194        case 0: return logos_ldec_box(ldec_add(x, y));
195        case 1: return logos_ldec_box(ldec_sub(x, y));
196        case 2: return logos_ldec_box(ldec_mul(x, y));
197        case 3: return logos_ldec_box(ldec_quotient(x, y));
198        case 4:
199            if (ldec_is_zero(&y)) logos_fatal(logos_exact_division_refusal);
200            return logos_ldec_box(ldec_div(x, y));
201        case 5: return logos_ldec_box(ldec_floor_quotient(x, y));
202        case 6: return logos_ldec_box(ldec_remainder(x, y));
203        default: return logos_ldec_box(ldec_power(x, y));
204    }
205}
206
207/* `a & b`, `a | b`, `a ^ b`, `a << b`, `a >> b` (`op` '&', '|', '^', '<', '>') over two whole numbers, an Int. */
208long long logos_ldec_bitwise(long long a, int af, long long b, int bf, int op) {
209    return ldec_bitwise(logos_ldec_of(a, af), logos_ldec_of(b, bf), (char)op);
210}
211
212/* The order of two exact numbers: -1, 0 or 1. */
213int logos_ldec_cmp(long long a, int af, long long b, int bf) { return ldec_cmp(logos_ldec_of(a, af), logos_ldec_of(b, bf)); }
214
215/* The order of a rational against a real by the real's exact value: -1, 0 or 1, and 2 when the real is NaN. */
216int logos_ldec_cmp_f64(long long a, double f) { return ldec_cmp_f64(*(const LDec *)a, f); }
217
218/* The double the VM converts a rational to beside a real. */
219double logos_ldec_to_f64(long long a) { return ldec_to_f64(*(const LDec *)a); }
220
221/* `a op f`, or `f op a` when the rational is on the right (`rational_left` 0), a rational beside a real, as the VM
222   computes it on the rational's double: op 0 `+`, 1 `-`, 2 `*`, 3 `/`, 4 `**`. */
223double logos_ldec_real_op(long long a, double f, int rational_left, int op) {
224    double x = ldec_to_f64(*(const LDec *)a);
225    double l = rational_left ? x : f, r = rational_left ? f : x;
226    switch (op) {
227        case 0: return l + r;
228        case 1: return l - r;
229        case 2: return l * r;
230        case 3:
231            if (r == 0.0) logos_fatal(logos_float_division_refusal);
232            return l / r;
233        default: return pow(l, r);
234    }
235}
236
237/* A rational where the VM takes a machine integer: the integer a whole one is, else the place's refusal. */
238long long logos_ldec_as_i64(long long a, const char *past_word, const char *fraction) {
239    return ldec_as_i64(*(const LDec *)a, past_word, fraction);
240}
241
242/* `num/den`, or the integer a whole rational equals. */
243char *logos_ldec_str(long long handle) { return ldec_to_string((const LDec *)handle); }
244
245/* ---- Temporal: Date (days since epoch) / Moment (nanos since epoch), ≡ the VM's Show ---- */
246
247/* Floored (Euclidean) division — a pre-epoch Moment yields the correct date + a 0..86399 time-of-day. */
248static long long logos_fdiv(long long a, long long b) {
249    long long q = a / b;
250    if ((a % b != 0) && ((a < 0) != (b < 0))) q--;
251    return q;
252}
253
254/* Civil date from days-since-epoch (Howard Hinnant's algorithm, matching `RuntimeValue::Date`). */
255static void logos_civil(long long days, long long *year, long long *mon, long long *day) {
256    long long z = days + 719468;
257    long long era = (z >= 0 ? z : z - 146096) / 146097;
258    long long doe = z - era * 146097;
259    long long yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
260    long long y = yoe + era * 400;
261    long long doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
262    long long mp = (5 * doy + 2) / 153;
263    long long d = doy - (153 * mp + 2) / 5 + 1;
264    long long m = mp + (mp < 10 ? 3 : -9);
265    *year = y + (m <= 2 ? 1 : 0);
266    *mon = m;
267    *day = d;
268}
269
270char *logos_date_str(long long days) {
271    long long y, m, d;
272    logos_civil(days, &y, &m, &d);
273    char *b = (char *)lc_gc_alloc_atomic(32);
274    snprintf(b, 32, "%04lld-%02lld-%02lld", y, m, d);
275    return b;
276}
277
278char *logos_moment_str(long long nanos) {
279    long long total_seconds = logos_fdiv(nanos, 1000000000LL);
280    long long days = logos_fdiv(total_seconds, 86400LL);
281    long long day_seconds = total_seconds - days * 86400LL;
282    long long hours = day_seconds / 3600;
283    long long minutes = (day_seconds % 3600) / 60;
284    long long y, m, d;
285    logos_civil(days, &y, &m, &d);
286    char *b = (char *)lc_gc_alloc_atomic(48);
287    snprintf(b, 48, "%04lld-%02lld-%02lld %02lld:%02lld", y, m, d, hours, minutes);
288    return b;
289}
290
291/* `today` / `now` — the injected fixed clock (env `LOGOS_FIXED_DAYS`/`LOGOS_FIXED_NANOS`, set by the
292   differential harness so the AOT binary and the VM oracle read the SAME instant) else the real clock. */
293long long logos_today(void) {
294    const char *e = getenv("LOGOS_FIXED_DAYS");
295    if (e) return atoll(e);
296    return (long long)(time(NULL) / 86400);
297}
298
299long long logos_now(void) {
300    const char *e = getenv("LOGOS_FIXED_NANOS");
301    if (e) return atoll(e);
302    return (long long)time(NULL) * 1000000000LL;
303}
304
305/* time.lg's `now()` — the Unix time in milliseconds from the real clock, as the interpreter reads it. */
306long long logos_time_now(void) {
307    struct timespec ts;
308    timespec_get(&ts, TIME_UTC);
309    return (long long)ts.tv_sec * 1000 + (long long)(ts.tv_nsec / 1000000);
310}
311
312/* env.lg's `get(name)`, in the halves the VM expands it into: whether the process environment sets the
313   variable, and its text (`""` when it is unset). */
314long long logos_env_has(const char *name) {
315    return getenv(name) != NULL;
316}
317
318char *logos_env_value(const char *name) {
319    const char *v = getenv(name);
320    return lc_gc_strdup(v ? v : "");
321}
322
323/* How a cell of the exact extension displays, set by the extension's adapter when the shim embeds it
324   (`codegen_riscv::runtime`); a program that holds no cell never reads it. */
325static char *(*logos_tower_text_hook)(long long);
326
327char *logos_val_to_str(long long bits, int kind) {
328    switch (kind) {
329        case 0: { char *b = (char *)lc_gc_alloc_atomic(24); snprintf(b, 24, "%lld", bits); return b; }
330        case 1: { double f; memcpy(&f, &bits, 8); return logos_fmt_f64(f); }
331        case 2: return lc_gc_strdup(bits ? "true" : "false");
332        case 4: return logos_ldec_str(bits); /* Rational */
333        case 5: return logos_date_str(bits); /* Date */
334        case 6: return logos_moment_str(bits); /* Moment */
335        case 7: return lc_gc_strdup("nothing"); /* a value-or-nothing holding nothing */
336        case 8: { /* a PeerAgent: `<peer topic>` */
337            size_t n = strlen((const char *)bits) + 8;
338            char *b = (char *)lc_gc_alloc_atomic(n);
339            snprintf(b, n, "<peer %s>", (const char *)bits);
340            return b;
341        }
342        case 9: return logos_tower_text_hook(bits); /* a cell of the exact extension */
343        default: return lc_gc_strdup((const char *)bits); /* Text: copy so the result is independently owned */
344    }
345}
346
347/* ---- A Text a register owns: `t = t + v` in place ----
348   While a register's owned flag is set, its Text is a block of its own — its length and capacity just before
349   the characters — which an append grows in place, amortized. Any other Text is copied into such a block
350   first, so an alias never sees the change. The characters stay an ordinary C string. */
351typedef struct { size_t len, cap; } logos_text_block;
352
353/* `t = t + v` into the register at `slot`: `v`'s text by its kind tag. `v` is read before anything moves. */
354void logos_text_append_own(char **slot, long long *owned, long long v, int tag) {
355    char *made = tag == 3 ? NULL : logos_val_to_str(v, tag);
356    const char *add = made ? made : (const char *)v;
357    size_t n = strlen(add);
358    logos_text_block *b;
359    if (*owned) {
360        b = (logos_text_block *)*slot - 1;
361    } else {
362        size_t len = strlen(*slot);
363        b = (logos_text_block *)lc_gc_alloc_atomic(sizeof *b + len + n + 1);
364        b->len = len;
365        b->cap = len + n;
366        memcpy(b + 1, *slot, len + 1);
367        *owned = 1;
368    }
369    if (b->len + n > b->cap) {
370        size_t cap = b->cap * 2 > b->len + n ? b->cap * 2 : b->len + n;
371        b = (logos_text_block *)lc_gc_realloc_atomic(b, sizeof *b + cap + 1);
372        b->cap = cap;
373    }
374    memcpy((char *)(b + 1) + b->len, add, n + 1);
375    b->len += n;
376    *slot = (char *)(b + 1);
377    lc_gc_free(made);
378}
379
380/* `Op::Concat` — stringify both operands (by kind) and join, matching the VM's `arith::concat`. */
381char *logos_concat_vals(long long a, int ak, long long b, int bk) {
382    char *sa = logos_val_to_str(a, ak);
383    char *sb = logos_val_to_str(b, bk);
384    size_t la = strlen(sa), lb = strlen(sb);
385    char *out = (char *)lc_gc_alloc_atomic(la + lb + 1);
386    memcpy(out, sa, la);
387    memcpy(out + la, sb, lb);
388    out[la + lb] = '\0';
389    lc_gc_free(sa);
390    lc_gc_free(sb);
391    return out;
392}
393
394/* `Show` of a Text value — raw contents + newline, exactly like the VM. */
395void logos_show_text(const char *s) { printf("%s\n", s); }
396
397/* `Fail with <value>.` — the value exactly as `Show` renders it. */
398void logos_fail_val(long long bits, int kind) { logos_fatal(logos_val_to_str(bits, kind)); }
399
400/* ---- Program arguments (`args()`) + `parseInt` ---- */
401static int logos_argc = 0;
402static char **logos_argv = NULL;
403void logos_set_args(int argc, char **argv) { logos_argc = argc; logos_argv = argv; }
404
405/* A failure naming a Text: `refusal` is a format of it (`%s`). */
406__attribute__((noreturn)) static void logos_fatals(const char *refusal, const char *text) {
407    size_t n = strlen(refusal) + strlen(text) + 1;
408    char *why = (char *)lc_gc_alloc_atomic(n);
409    snprintf(why, n, refusal, text);
410    logos_fatal(why);
411}
412
413/* `parseInt(t)` — the VM's: `t` trimmed (`str::trim`), then the whole of it an `i64` in base 10 as
414   `str::parse` reads one (an optional sign, then digits, in range). Anything else stops the program with
415   `logos_parse_int_refusal`. */
416long long logos_parse_int(const char *s) {
417    const char *refusal = logos_parse_int_refusal;
418    const char *p = logos_trim(s);
419    int negative = *p == '-';
420    if (*p == '+' || *p == '-') p++;
421    if (!*p) logos_fatals(refusal, s);
422    unsigned long long v = 0, limit = negative ? 9223372036854775808ULL : 9223372036854775807ULL;
423    for (; *p; p++) {
424        if (*p < '0' || *p > '9') logos_fatals(refusal, s);
425        unsigned long long d = (unsigned long long)(*p - '0');
426        if (v > (limit - d) / 10) logos_fatals(refusal, s);
427        v = v * 10 + d;
428    }
429    return negative ? (long long)(0 - v) : (long long)v;
430}
431
432/* Whether `s` spells `word`, ASCII case folded. */
433static int logos_ascii_is(const char *s, const char *word) {
434    for (; *word; s++, word++) {
435        char c = *s >= 'A' && *s <= 'Z' ? (char)(*s - 'A' + 'a') : *s;
436        if (c != *word) return 0;
437    }
438    return !*s;
439}
440
441/* `parseFloat(t)` — the VM's: `t` trimmed, then the whole of it an `f64` as `str::parse` reads one: an
442   optional sign, then `inf`, `infinity` or `nan` in any case, or digits with at most one point among them
443   and at least one digit, then an optional exponent (`e`, an optional sign, digits). Anything else stops the
444   program with `logos_parse_float_refusal`. The value is the correctly rounded one (`strtod`). */
445double logos_parse_float(const char *s) {
446    const char *refusal = logos_parse_float_refusal;
447    const char *t = logos_trim(s), *p = t;
448    int negative = *p == '-';
449    if (*p == '+' || *p == '-') p++;
450    if (logos_ascii_is(p, "inf") || logos_ascii_is(p, "infinity")) return negative ? -INFINITY : INFINITY;
451    if (logos_ascii_is(p, "nan")) return negative ? -NAN : NAN;
452    int digits = 0;
453    while (*p >= '0' && *p <= '9') { p++; digits++; }
454    if (*p == '.') {
455        p++;
456        while (*p >= '0' && *p <= '9') { p++; digits++; }
457    }
458    if (!digits) logos_fatals(refusal, s);
459    if (*p == 'e' || *p == 'E') {
460        p++;
461        if (*p == '+' || *p == '-') p++;
462        if (!(*p >= '0' && *p <= '9')) logos_fatals(refusal, s);
463        while (*p >= '0' && *p <= '9') p++;
464    }
465    if (*p) logos_fatals(refusal, s);
466    return strtod(t, NULL);
467}
468
469/* `chr(codepoint)` — a single-character UTF-8 Text handle, matching the VM's `char::from_u32`. */
470char *logos_chr(long long cp) {
471    char *out = (char *)lc_gc_alloc_atomic(5);
472    unsigned int c = (unsigned int)cp;
473    int n = 0;
474    if (c < 0x80) { out[n++] = (char)c; }
475    else if (c < 0x800) { out[n++] = (char)(0xC0 | (c >> 6)); out[n++] = (char)(0x80 | (c & 0x3F)); }
476    else if (c < 0x10000) {
477        out[n++] = (char)(0xE0 | (c >> 12));
478        out[n++] = (char)(0x80 | ((c >> 6) & 0x3F));
479        out[n++] = (char)(0x80 | (c & 0x3F));
480    } else {
481        out[n++] = (char)(0xF0 | (c >> 18));
482        out[n++] = (char)(0x80 | ((c >> 12) & 0x3F));
483        out[n++] = (char)(0x80 | ((c >> 6) & 0x3F));
484        out[n++] = (char)(0x80 | (c & 0x3F));
485    }
486    out[n] = '\0';
487    return out;
488}
489
490/* ---- CRDT counters (G-Counter / PN-Counter) — a per-replica count map ---- */
491/* The observable is the SUM; merge/join take the per-replica MAX (IDEMPOTENT — a redelivered state
492   never double-counts), so it converges regardless of order/duplication. The replica ids are drawn
493   from a local monotone sequence: the differential observable (the sum) depends only on the ids being
494   DISTINCT and STABLE within a run, not their absolute values, so it matches the VM whatever the VM's
495   own (process-global) replica sequence happens to be. */
496
497typedef struct { long long my_rid; long long len, cap; long long *rids; long long *cnts; } logos_crdt;
498
499static long long logos_replica_seq = 0;
500
501logos_crdt *logos_crdt_new(void) {
502    logos_crdt *c = (logos_crdt *)lc_gc_alloc(sizeof(logos_crdt));
503    c->my_rid = logos_replica_seq++;
504    c->len = 1; c->cap = 4;
505    c->rids = (long long *)lc_gc_alloc_atomic((size_t)c->cap * 8);
506    c->cnts = (long long *)lc_gc_alloc_atomic((size_t)c->cap * 8);
507    c->rids[0] = c->my_rid; c->cnts[0] = 0;
508    return c;
509}
510
511/* Increment (or decrement) THIS counter's own replica entry, in place. */
512void logos_crdt_bump(logos_crdt *c, long long amount, int negate) {
513    long long d = negate ? -amount : amount;
514    for (long long i = 0; i < c->len; i++) {
515        if (c->rids[i] == c->my_rid) { c->cnts[i] += d; return; }
516    }
517    /* my_rid always present (seeded in _new); defensive. */
518    c->cnts[0] += d;
519}
520
521long long logos_crdt_sum(const logos_crdt *c) {
522    long long s = 0;
523    for (long long i = 0; i < c->len; i++) s += c->cnts[i];
524    return s;
525}
526
527static void logos_crdt_put_max(logos_crdt *d, long long rid, long long cnt) {
528    for (long long i = 0; i < d->len; i++) {
529        if (d->rids[i] == rid) { if (cnt > d->cnts[i]) d->cnts[i] = cnt; return; }
530    }
531    if (d->len == d->cap) {
532        d->cap *= 2;
533        d->rids = (long long *)lc_gc_realloc_atomic(d->rids, (size_t)d->cap * 8);
534        d->cnts = (long long *)lc_gc_realloc_atomic(d->cnts, (size_t)d->cap * 8);
535    }
536    d->rids[d->len] = rid; d->cnts[d->len] = cnt; d->len++;
537}
538
539/* Lattice join: per-replica MAX of `s` into `d`, in place (idempotent, commutative, associative). */
540void logos_crdt_merge(logos_crdt *d, const logos_crdt *s) {
541    for (long long i = 0; i < s->len; i++) logos_crdt_put_max(d, s->rids[i], s->cnts[i]);
542}
543
544/* `copy` of a counter: the same state, the same replica. */
545logos_crdt *logos_crdt_clone(const logos_crdt *c) {
546    logos_crdt *d = (logos_crdt *)lc_gc_alloc(sizeof(logos_crdt));
547    *d = *c;
548    d->rids = (long long *)lc_gc_alloc_atomic((size_t)c->cap * 8);
549    d->cnts = (long long *)lc_gc_alloc_atomic((size_t)c->cap * 8);
550    memcpy(d->rids, c->rids, (size_t)c->len * 8);
551    memcpy(d->cnts, c->cnts, (size_t)c->len * 8);
552    return d;
553}
554
555/* ---- Closures — a heap `[body_index, captured values…, present flags…]` cell array ---- */
556
557long long *logos_closure_new(long long body, long long ncap) {
558    long long *c = (long long *)lc_gc_alloc((size_t)(1 + 2 * ncap) * 8);
559    c[0] = body;
560    return c;
561}
562
563void logos_closure_set(long long *c, long long idx, long long val) { c[idx] = val; }
564
565/* ---- `FormatValue` spec helpers (the spec is a compile-time constant, dispatched in codegen) ---- */
566
567/* Currency `$` — `${:.2}` (two decimals). Matches `apply_format_spec`'s float path. */
568char *logos_fmt_currency(double f) {
569    char *b = (char *)lc_gc_alloc_atomic(64);
570    snprintf(b, 64, "$%.2f", f);
571    return b;
572}
573
574/* Precision `.N` — `{:.N}` fixed-decimal float (round-to-nearest-even, matching Rust's Display). */
575char *logos_fmt_fixed(double f, int prec) {
576    char *b = (char *)lc_gc_alloc_atomic(64 + (size_t)prec);
577    snprintf(b, 64 + (size_t)prec, "%.*f", prec, f);
578    return b;
579}
580
581/* Alignment/width — pad `s` to `width` columns: dir '>' right, '<' left, '^' center (extra column on
582   the RIGHT, matching Rust's `{:^w}`). No truncation when `s` is already wide enough. */
583char *logos_pad(const char *s, long long width, char dir) {
584    size_t len = strlen(s);
585    if ((long long)len >= width) return lc_gc_strdup(s);
586    size_t pad = (size_t)width - len;
587    char *out = (char *)lc_gc_alloc_atomic((size_t)width + 1);
588    size_t left = dir == '>' ? pad : dir == '^' ? pad / 2 : 0;
589    size_t right = pad - left;
590    memset(out, ' ', left);
591    memcpy(out + left, s, len);
592    memset(out + left + len, ' ', right);
593    out[width] = '\0';
594    return out;
595}
596
597/* EXACT i64-vs-f64 comparison for a mixed Int/Float relational op — replicates the VM's
598 * `cmp_i64_f64_exact` + `rel` (crates/logicaffeine_base/src/numeric.rs, semantics/compare.rs). `i`/`f`
599 * are the numeric operands (the Int first regardless of source order); `reversed` = 1 when the SOURCE
600 * order was (Float, Int) so the comparison is (f vs i); `op` selects lt=0/gt=1/le=2/ge=3/eq=4/ne=5.
601 * Returns 0/1. A NaN operand is unordered: every ordering/eq is false, `!=` is true — matching `rel`. */
602int logos_cmp_i64_f64_rel(long long i, double f, int reversed, int op) {
603    int ord; /* -2 unordered, else -1 (i<f) / 0 (i==f) / 1 (i>f) */
604    if (isnan(f)) {
605        ord = -2;
606    } else if (isinf(f)) {
607        ord = (f > 0) ? -1 : 1;
608    } else if (i <= (1LL << 53) && i >= -(1LL << 53)) {
609        double di = (double)i;                 /* |i| <= 2^53 is exact */
610        ord = di < f ? -1 : (di > f ? 1 : 0);
611    } else if (f >= 9223372036854775808.0) {   /* f >= 2^63 > any i64 */
612        ord = -1;
613    } else if (f < -9223372036854775808.0) {   /* f < -2^63 < any i64 */
614        ord = 1;
615    } else {
616        long long t = (long long)f;            /* trunc toward zero, exact for f in i64 range */
617        ord = i < t ? -1 : (i > t ? 1 : 0);
618    }
619    if (reversed && ord != -2) ord = -ord;
620    switch (op) {
621        case 0: return ord == -1;              /* lt */
622        case 1: return ord == 1;               /* gt */
623        case 2: return ord == -1 || ord == 0;  /* le  (None -> false) */
624        case 3: return ord == 0 || ord == 1;   /* ge  (None -> false) */
625        case 4: return ord == 0;               /* eq  (None -> false) */
626        default: return ord != 0;              /* ne  (None -> true)  */
627    }
628}
629
630
631/* ---- Seq (heap growable array of uniform 8-byte cells) ----
632 * Every scalar element (i64 / f64-bits / char*) is one 8-byte cell; the element KIND is tracked by the
633 * emitter and only needed for Show. VALUE SEMANTICS BY OWNERSHIP: `Move` copies the handle (share), and a
634 * register's collection is mutated only through a `_own` helper handed the register's SLOT and its OWNED
635 * flag. A clear flag means the handle may be reachable from somewhere else (an alias, a global, a field, an
636 * iterator, a closure), so the helper first copies the collection into the slot and sets the flag; then it
637 * mutates in place. An alias or an in-flight iterator keeps its own list — the VM's copy-on-write and
638 * snapshot — and a register that owns its collection mutates it in amortized O(1). The emitter keeps the
639 * flag: an op that makes a fresh collection sets it, any op that could leave the handle elsewhere clears it. */
640typedef struct { long long len, cap; long long *data; } logos_seq;
641
642/* An empty sequence with room for `capacity` cells, in one block: the cells follow the header until the sequence
643   first outgrows them (`logos_seq_room`). A record, a tuple and an enum value never grow, so each is one block. */
644logos_seq *logos_seq_new_cap(long long capacity) {
645    if (capacity < 0) capacity = 0;
646    logos_seq *s = (logos_seq *)lc_gc_alloc(sizeof(logos_seq) + (size_t)capacity * 8);
647    s->len = 0;
648    s->cap = capacity;
649    s->data = capacity ? (long long *)(s + 1) : NULL;
650    return s;
651}
652
653/* Room for `cap` cells: cells that still follow the header move to a block of their own, any other block resizes. */
654static void logos_seq_room(logos_seq *a, long long cap) {
655    if (a->data == (long long *)(a + 1)) {
656        long long *cells = (long long *)lc_gc_alloc((size_t)cap * 8);
657        memcpy(cells, a->data, (size_t)a->len * 8);
658        a->data = cells;
659    } else {
660        a->data = (long long *)lc_gc_realloc(a->data, (size_t)cap * 8);
661    }
662    a->cap = cap;
663}
664
665/* A fresh sequence, with room for the cells its first pushes add: an enum value's tag and payload, a tuple's items,
666   a list's first items all go in its one block. */
667logos_seq *logos_seq_new(void) { return logos_seq_new_cap(4); }
668
669static logos_seq *logos_seq_dup(const logos_seq *a) {
670    logos_seq *s = logos_seq_new_cap(a->len);
671    s->len = a->len;
672    if (a->len) memcpy(s->data, a->data, (size_t)a->len * 8);
673    return s;
674}
675
676/* In-place append, into a collection its caller has made its own; the header pointer identity stays stable
677   (only the cells move), and growth is taken out of line. */
678__attribute__((noinline)) static void logos_seq_push_growing(logos_seq *a, long long val) {
679    logos_seq_room(a, a->cap ? a->cap * 2 : 4);
680    a->data[a->len++] = val;
681}
682
683void logos_seq_push_inplace(logos_seq *a, long long val) {
684    if (__builtin_expect(a->len == a->cap, 0)) {
685        logos_seq_push_growing(a, val);
686        return;
687    }
688    a->data[a->len++] = val;
689}
690
691/* The collection in `*slot`, made the register's own: a copy when `*owned` is clear. */
692static logos_seq *logos_seq_own(logos_seq **slot, long long *owned) {
693    if (!*owned) { *slot = logos_seq_dup(*slot); *owned = 1; }
694    return *slot;
695}
696
697/* The register's own collection, for an op that changes several of its cells in place (`Merge`): the
698   emitter calls it only when the register's owned flag is clear. */
699logos_seq *logos_seq_make_own(logos_seq **slot, long long *owned) {
700    return logos_seq_own(slot, owned);
701}
702
703__attribute__((noinline)) static void logos_seq_push_copying(logos_seq **slot, long long *owned, long long val) {
704    logos_seq_push_inplace(logos_seq_own(slot, owned), val);
705}
706
707/* `Push val to xs`. `val` arrives by value, read before the register is made its own. */
708void logos_seq_push_own(logos_seq **slot, long long *owned, long long val) {
709    if (__builtin_expect(!*owned, 0)) {
710        logos_seq_push_copying(slot, owned, val);
711        return;
712    }
713    logos_seq_push_inplace(*slot, val);
714}
715
716/* `args()` → a `Seq of Text` of the process argv (index 1 is the program name, exactly as the VM's
717   `program_args`), so `item 2 of args()` is the first real argument. */
718logos_seq *logos_args(void) {
719    logos_seq *s = logos_seq_new();
720    for (int i = 0; i < logos_argc; i++) logos_seq_push_inplace(s, (long long)logos_argv[i]);
721    return s;
722}
723
724long long logos_seq_len(const logos_seq *a) { return a->len; }
725
726/* `Pop from xs into v` — the last element, removed (`present` 1), or `nothing` from an empty list (`present`
727   0). An empty pop changes nothing, so it leaves the register's ownership as it found it. */
728/* The call sites whose callee stops the program itself rather than answer nothing: pairs of a return
729   address and the sentence to stop with, ended by a zero pair. The emitter writes the table; a helper
730   searches it only on its way to stopping. */
731extern const struct { const void *site; const char *reason; } logos_fail_sites[];
732
733__attribute__((noreturn, noinline)) static void logos_fail_at(const void *site) {
734    for (long long i = 0; logos_fail_sites[i].site; i++) {
735        if (logos_fail_sites[i].site == site) logos_fatal(logos_fail_sites[i].reason);
736    }
737    abort();
738}
739
740typedef struct { long long present, value; } logos_popped;
741logos_popped logos_seq_pop_own(logos_seq **slot, long long *owned) {
742    logos_popped r = {0, 0};
743    if (!(*slot)->len) return r;
744    logos_seq *s = logos_seq_own(slot, owned);
745    r.present = 1;
746    r.value = s->data[--s->len];
747    return r;
748}
749
750/* `Pop from xs into v` where the op after it fails on `nothing`: the last element, or, from an empty list,
751   the program stopped with the sentence registered for the call site. */
752__attribute__((noinline)) long long logos_seq_pop_value(logos_seq **slot, long long *owned) {
753    if (!(*slot)->len) logos_fail_at(__builtin_return_address(0));
754    logos_seq *s = logos_seq_own(slot, owned);
755    return s->data[--s->len];
756}
757
758/* In-place indexed write (1-based) — a struct-field mutation that must be visible through every alias
759   of the struct (`CrdtBump`'s counter update; the seq header pointer stays stable). */
760__attribute__((noinline, cold)) static void logos_seq_set_from_back(logos_seq *a, long long i, long long val) {
761    a->data[logos_index_from_back(i, a->len)] = val;
762}
763
764void logos_seq_set_inplace(logos_seq *a, long long i, long long val) {
765    if (__builtin_expect(i < 1 || i > a->len, 0)) {
766        logos_seq_set_from_back(a, i, val);
767        return;
768    }
769    a->data[i - 1] = val;
770}
771
772/* `Set item i of xs to val` (1-based, or from the back when negative), and a struct's field write (its cells
773   are a seq). An index out of range stops the program as the VM does, before anything is copied. */
774__attribute__((noinline)) static void logos_seq_set_own_copying(logos_seq **slot, long long *owned, long long i, long long val) {
775    long long at = logos_resolve_index(i, (*slot)->len);
776    logos_seq_own(slot, owned)->data[at] = val;
777}
778
779void logos_seq_set_own(logos_seq **slot, long long *owned, long long i, long long val) {
780    logos_seq *s = *slot;
781    if (__builtin_expect(*owned && i >= 1 && i <= s->len, 1)) {
782        s->data[i - 1] = val;
783        return;
784    }
785    logos_seq_set_own_copying(slot, owned, i, val);
786}
787
788/* The value in field `cell` (1-based) of the record in `*slot`, made the register's own record's own: the record
789   copied unless `*owned` says the register is its only holder, the value copied with `copy` unless
790   `*field_owned` says the record is its only holder, and both flags set. */
791__attribute__((noinline)) static long long logos_field_own(logos_seq **slot, long long *owned, long long *field_owned,
792    long long cell, long long (*copy)(long long)) {
793    logos_seq *record = logos_seq_own(slot, owned);
794    long long value = record->data[cell - 1];
795    if (!*field_owned) {
796        value = copy(value);
797        record->data[cell - 1] = value;
798        *field_owned = 1;
799    }
800    return value;
801}
802
803static long long logos_field_seq_copy(long long list) { return (long long)logos_seq_dup((const logos_seq *)list); }
804
805__attribute__((noinline)) static void logos_field_push_copying(logos_seq **slot, long long *owned, long long *field_owned,
806    long long cell, long long val) {
807    logos_seq_push_inplace((logos_seq *)logos_field_own(slot, owned, field_owned, cell, logos_field_seq_copy), val);
808}
809
810/* `Push val to r's field` — into the register's own record's own list: in place while the field flag is set
811   (the emitter keeps a field flag set only while its register's owned flag is), so a loop of pushes copies at
812   most once. */
813void logos_field_push_own(logos_seq **slot, long long *owned, long long *field_owned, long long cell, long long val) {
814    if (__builtin_expect(!*field_owned, 0)) {
815        logos_field_push_copying(slot, owned, field_owned, cell, val);
816        return;
817    }
818    logos_seq_push_inplace((logos_seq *)(*slot)->data[cell - 1], val);
819}
820
821static long long logos_field_crdt_copy(long long c) { return (long long)logos_crdt_clone((const logos_crdt *)c); }
822
823__attribute__((noinline)) static void logos_field_bump_copying(logos_seq **slot, long long *owned, long long *field_owned,
824    long long cell, long long amount, int negate) {
825    logos_crdt_bump((logos_crdt *)logos_field_own(slot, owned, field_owned, cell, logos_field_crdt_copy), amount, negate);
826}
827
828/* `Increase/Decrease r's counter by amount` — the register's own record's own counter: in place while the
829   field flag is set. */
830void logos_field_bump_own(logos_seq **slot, long long *owned, long long *field_owned, long long cell, long long amount,
831    int negate) {
832    if (__builtin_expect(!*field_owned, 0)) {
833        logos_field_bump_copying(slot, owned, field_owned, cell, amount, negate);
834        return;
835    }
836    logos_crdt_bump((logos_crdt *)(*slot)->data[cell - 1], amount, negate);
837}
838
839/* `a followed by b` — fresh seq with a's elements then b's. */
840logos_seq *logos_seq_concat(const logos_seq *a, const logos_seq *b) {
841    logos_seq *s = logos_seq_new_cap(a->len + b->len);
842    s->len = a->len + b->len;
843    if (a->len) memcpy(s->data, a->data, (size_t)a->len * 8);
844    if (b->len) memcpy(s->data + a->len, b->data, (size_t)b->len * 8);
845    return s;
846}
847
848/* `items start through end of a` — the VM's `slice`: the 1-based inclusive range, and empty unless it lies
849   within the list (`slice.get(start - 1 .. end)`, the bounds read as unsigned). */
850logos_seq *logos_seq_slice(const logos_seq *a, long long start, long long end) {
851    unsigned long long from = (unsigned long long)start ? (unsigned long long)start - 1 : 0, to = (unsigned long long)end;
852    logos_seq *s = logos_seq_new();
853    if (from < to && to <= (unsigned long long)a->len) {
854        for (unsigned long long i = from; i < to; i++) logos_seq_push_inplace(s, a->data[i]);
855    }
856    return s;
857}
858
859/* Element equality by kind (0=Int, 1=Float, 3=Text) — the VM's `values_equal` for scalar cells. */
860static int logos_cell_eq(long long a, long long b, int kind) {
861    if (kind == 3) return strcmp((const char *)a, (const char *)b) == 0;
862    if (kind == 1) { double x, y; memcpy(&x, &a, 8); memcpy(&y, &b, 8); return x == y; }
863    return a == b;
864}
865
866/* Two struct fields compared by kind (a policy `ObjectFieldEquals` — the VM's `values_equal`). The
867   cells are 1-based; struct field indices are always in range. */
868int logos_struct_field_eq(logos_seq *a, long long ai, logos_seq *b, long long bi, int kind) {
869    return logos_cell_eq(a->data[ai - 1], b->data[bi - 1], kind);
870}
871
872/* `a contains val` — membership by element kind. */
873int logos_seq_contains(const logos_seq *a, long long val, int kind) {
874    for (long long i = 0; i < a->len; i++) {
875        if (logos_cell_eq(a->data[i], val, kind)) return 1;
876    }
877    return 0;
878}
879
880/* `a contains val` where the items or the needle are rationals (`items_rational`, `val_rational`): by value, an Int
881   by the rational it equals. */
882int logos_seq_contains_exact(const logos_seq *a, int items_rational, long long val, int val_rational) {
883    for (long long i = 0; i < a->len; i++) {
884        if (logos_ldec_cmp(a->data[i], items_rational, val, val_rational) == 0) return 1;
885    }
886    return 0;
887}
888
889/* An Int put among rationals goes in as the rational it equals: a literal's item, a pushed one, one stored. */
890void logos_seq_push_inplace_ldec(logos_seq *a, long long v) { logos_seq_push_inplace(a, logos_ldec_of_int(v)); }
891void logos_seq_push_own_ldec(logos_seq **slot, long long *owned, long long v) {
892    logos_seq_push_own(slot, owned, logos_ldec_of_int(v));
893}
894void logos_seq_set_own_ldec(logos_seq **slot, long long *owned, long long i, long long v) {
895    logos_seq_set_own(slot, owned, i, logos_ldec_of_int(v));
896}
897
898/* A list of Ints as the list of the rationals they equal. */
899logos_seq *logos_seq_as_ldec(const logos_seq *a) {
900    logos_seq *s = logos_seq_new();
901    for (long long i = 0; i < a->len; i++) logos_seq_push_inplace_ldec(s, a->data[i]);
902    return s;
903}
904
905/* ---- Set (insertion-ordered, dedup'd — shares the logos_seq storage) ---- */
906
907/* `Add val to s` — append if not already present (dedup). A present element changes nothing. */
908void logos_set_add_own(logos_seq **slot, long long *owned, long long val, int kind) {
909    if (logos_seq_contains(*slot, val, kind)) return;
910    logos_seq_push_inplace(logos_seq_own(slot, owned), val);
911}
912
913/* `Remove val from s` — drop every matching element, keeping the order of the rest. An absent element
914   changes nothing. */
915void logos_set_remove_own(logos_seq **slot, long long *owned, long long val, int kind) {
916    if (!logos_seq_contains(*slot, val, kind)) return;
917    logos_seq *s = logos_seq_own(slot, owned);
918    long long kept = 0;
919    for (long long i = 0; i < s->len; i++) {
920        if (!logos_cell_eq(s->data[i], val, kind)) s->data[kept++] = s->data[i];
921    }
922    s->len = kept;
923}
924
925/* `a union b` — a's elements, then b's novel elements (insertion-ordered). */
926logos_seq *logos_set_union(const logos_seq *a, const logos_seq *b, int kind) {
927    logos_seq *s = logos_seq_dup(a);
928    for (long long i = 0; i < b->len; i++) {
929        if (!logos_seq_contains(s, b->data[i], kind)) {
930            if (s->len == s->cap) logos_seq_room(s, s->cap ? s->cap * 2 : 4);
931            s->data[s->len++] = b->data[i];
932        }
933    }
934    return s;
935}
936
937/* `a intersect b` — a's elements that are also in b (a's order). */
938logos_seq *logos_set_intersect(const logos_seq *a, const logos_seq *b, int kind) {
939    logos_seq *s = logos_seq_new();
940    for (long long i = 0; i < a->len; i++) {
941        if (logos_seq_contains(b, a->data[i], kind)) {
942            if (s->len == s->cap) logos_seq_room(s, s->cap ? s->cap * 2 : 4);
943            s->data[s->len++] = a->data[i];
944        }
945    }
946    return s;
947}
948
949/* `Show` of a set — `{1, 2, 3}` (braces; insertion order; text elements unquoted). */
950void logos_set_show_int(const logos_seq *a) {
951    putchar('{');
952    for (long long i = 0; i < a->len; i++) { if (i) fputs(", ", stdout); printf("%lld", a->data[i]); }
953    fputs("}\n", stdout);
954}
955void logos_set_show_text(const logos_seq *a) {
956    putchar('{');
957    for (long long i = 0; i < a->len; i++) { if (i) fputs(", ", stdout); fputs((const char *)a->data[i], stdout); }
958    fputs("}\n", stdout);
959}
960
961/* `copy of a` — deep clone. */
962logos_seq *logos_seq_clone(const logos_seq *a) { return logos_seq_dup(a); }
963
964/* A struct is a fixed array of `n` field cells (declaration order), zero-initialized. */
965logos_seq *logos_struct_new(long long n) {
966    logos_seq *s = logos_seq_new_cap(n);
967    if (n > 0) memset(s->data, 0, (size_t)n * 8);
968    s->len = n > 0 ? n : 0;
969    return s;
970}
971
972/* Print without a trailing newline (for building `Name { f: v, ... }` piece by piece). */
973void logos_print_raw(const char *s) { fputs(s, stdout); }
974void logos_print_nl(void) { putchar('\n'); }
975
976/* Granlund–Montgomery / libdivide unsigned magic division — a byte-for-byte port of the VM's
977 * `magic_eval` (vm/compiler.rs). Emitted only for a constant non-power-of-two divisor with an
978 * Oracle-proven non-negative dividend; `mul_back == 0` is `/`, `mul_back == c` is `%`. */
979long long logos_magic_div(long long x, unsigned long long magic, int more, long long mul_back) {
980    unsigned long long n = (unsigned long long)x;
981    unsigned long long q;
982    if (more & 0x80) {
983        q = n >> (more & 0x3F);
984    } else {
985        unsigned long long hi = (unsigned long long)(((unsigned __int128)magic * (unsigned __int128)n) >> 64);
986        if (more & 0x40) {
987            unsigned long long t = ((n - hi) >> 1) + hi;
988            q = t >> (more & 0x3F);
989        } else {
990            q = hi >> (more & 0x3F);
991        }
992    }
993    if (mul_back == 0) return (long long)q;
994    return x - (long long)q * mul_back;
995}
996
997/* Integer exponentiation by squaring (wrapping, matching the VM's in-i64 result). A negative exponent, or
998 * one past 32 bits, stops the program with the VM's sentence. */
999long long logos_ipow(long long base, long long exp) {
1000    if (exp < 0) logos_fatal(logos_negative_exponent_refusal);
1001    if (exp > 4294967295LL) logos_fatal(logos_exponent_too_large_refusal);
1002    unsigned long long r = 1, b = (unsigned long long)base, e = (unsigned long long)exp;
1003    while (e > 0) { if (e & 1) r *= b; b *= b; e >>= 1; }
1004    return (long long)r;
1005}
1006
1007/* `lo to hi` — a materialized inclusive integer range (empty if lo > hi). */
1008logos_seq *logos_seq_range(long long lo, long long hi) {
1009    logos_seq *s = logos_seq_new();
1010    for (long long v = lo; v <= hi; v++) {
1011        if (s->len == s->cap) logos_seq_room(s, s->cap ? s->cap * 2 : 8);
1012        s->data[s->len++] = v;
1013    }
1014    return s;
1015}
1016
1017/* `start to end`, and `items start through end of a`, with a bound that is a rational (`lf`, `hf`): each the
1018   integer a whole one is, a fraction or one past i64 refused in the VM's words for the place. */
1019logos_seq *logos_seq_range_ldec(long long lo, int lf, long long hi, int hf, const char *past_word, const char *fraction) {
1020    return logos_seq_range(lf ? logos_ldec_as_i64(lo, past_word, fraction) : lo,
1021                           hf ? logos_ldec_as_i64(hi, past_word, fraction) : hi);
1022}
1023logos_seq *logos_seq_slice_ldec(const logos_seq *a, long long start, int sf, long long end, int ef,
1024                                const char *past_word, const char *fraction) {
1025    return logos_seq_slice(a, sf ? logos_ldec_as_i64(start, past_word, fraction) : start,
1026                           ef ? logos_ldec_as_i64(end, past_word, fraction) : end);
1027}
1028
1029/* `item i of xs` — 1-based, or from the back when negative; an index out of range stops the program as the
1030   VM does. */
1031__attribute__((noinline, cold)) static long long logos_seq_index_from_back(const logos_seq *a, long long i) {
1032    return a->data[logos_index_from_back(i, a->len)];
1033}
1034
1035long long logos_seq_index(const logos_seq *a, long long i) {
1036    if (__builtin_expect(i < 1 || i > a->len, 0)) return logos_seq_index_from_back(a, i);
1037    return a->data[i - 1];
1038}
1039
1040/* `length of text` — BYTE length (the VM's `Text` length is `s.len()`, not the char count). */
1041long long logos_text_len(const char *s) { return (long long)strlen(s); }
1042
1043/* `item i of text` — 1-based character access returning a single-character Text, matching the VM's
1044   `index_get`: an ASCII string indexes by byte; otherwise the i-th Unicode scalar (negative i counts
1045   from the end); an out-of-range index errors like the VM (exit nonzero). */
1046char *logos_text_index(const char *s, long long i) {
1047    long long blen = (long long)strlen(s);
1048    int ascii = 1;
1049    for (long long k = 0; k < blen; k++) {
1050        if ((unsigned char)s[k] >= 0x80) { ascii = 0; break; }
1051    }
1052    if (ascii && i >= 1 && i <= blen) {
1053        char *out = (char *)lc_gc_alloc_atomic(2);
1054        out[0] = s[i - 1];
1055        out[1] = '\0';
1056        return out;
1057    }
1058    long long char_len = blen;
1059    if (!ascii) {
1060        char_len = 0;
1061        for (long long k = 0; k < blen;) {
1062            unsigned char c = (unsigned char)s[k];
1063            int n = c < 0x80 ? 1 : c < 0xE0 ? 2 : c < 0xF0 ? 3 : 4;
1064            k += n;
1065            char_len++;
1066        }
1067    }
1068    long long idx;
1069    if (i >= 1) {
1070        idx = i - 1;
1071        if (idx >= char_len) goto oob;
1072    } else if (i <= -1) {
1073        long long back = -i;
1074        if (back > char_len) goto oob;
1075        idx = char_len - back;
1076    } else {
1077        goto oob;
1078    }
1079    {
1080        long long k = 0, ci = 0;
1081        while (k < blen) {
1082            unsigned char c = (unsigned char)s[k];
1083            int n = c < 0x80 ? 1 : c < 0xE0 ? 2 : c < 0xF0 ? 3 : 4;
1084            if (ci == idx) {
1085                char *out = (char *)lc_gc_alloc_atomic(n + 1);
1086                for (int t = 0; t < n; t++) out[t] = s[k + t];
1087                out[n] = '\0';
1088                return out;
1089            }
1090            k += n;
1091            ci++;
1092        }
1093    }
1094oob:
1095    logos_fatalf(logos_index_refusal, i);
1096}
1097
1098/* ---- Files: the VM's sandboxed in-memory VFS, which keeps files only inside a `## Test`'s sealed world ----
1099   A program keeps none, so every read finds nothing and stops the program with `missing` (a format of the
1100   path) — the VM's sentence, taken from the VM by the emitter. */
1101__attribute__((noreturn)) void logos_vfs_read(const char *path, const char *missing) {
1102    size_t n = strlen(missing) + strlen(path) + 1;
1103    char *why = (char *)lc_gc_alloc_atomic(n);
1104    snprintf(why, n, missing, path);
1105    logos_fatal(why);
1106}
1107
1108/* `Repeat for c in t` — the Text's characters, each a Text of its own. */
1109logos_seq *logos_text_chars(const char *s) {
1110    logos_seq *out = logos_seq_new();
1111    const unsigned char *p = (const unsigned char *)s;
1112    while (*p) {
1113        unsigned cp;
1114        int n = logos_utf8_at(p, &cp);
1115        char *c = (char *)lc_gc_alloc_atomic((size_t)n + 1);
1116        memcpy(c, p, (size_t)n);
1117        c[n] = '\0';
1118        logos_seq_push_inplace(out, (long long)c);
1119        p += n;
1120    }
1121    return out;
1122}
1123
1124/* Eight bytes from the system's entropy; `getentropy` fails only for a request over 256 bytes. */
1125static unsigned long long logos_entropy(void) {
1126    unsigned long long draw;
1127    if (getentropy(&draw, sizeof draw) != 0) abort();
1128    return draw;
1129}
1130
1131/* `randomInt(lo, hi)` — uniform over `lo..=hi` from the system's entropy (rejection sampling, so no bound is
1132   favored); crossed bounds stop the program with `logos_random_bounds_refusal`. */
1133long long logos_random_int(long long lo, long long hi) {
1134    if (lo > hi) {
1135        char why[256];
1136        snprintf(why, sizeof why, logos_random_bounds_refusal, lo, hi);
1137        logos_fatal(lc_gc_strdup(why));
1138    }
1139    unsigned long long span = (unsigned long long)hi - (unsigned long long)lo, draw, limit;
1140    if (span == ~0ULL) return (long long)logos_entropy();
1141    limit = ~0ULL - (~0ULL % (span + 1) + 1) % (span + 1);
1142    do draw = logos_entropy(); while (draw > limit);
1143    return (long long)((unsigned long long)lo + draw % (span + 1));
1144}
1145
1146/* `text_bytes(t)` — each UTF-8 byte of the Text, as an Int 0..255. */
1147logos_seq *logos_text_bytes(const char *s) {
1148    logos_seq *out = logos_seq_new();
1149    for (const unsigned char *p = (const unsigned char *)s; *p; p++) logos_seq_push_inplace(out, *p);
1150    return out;
1151}
1152
1153/* `readReals(path)` — the file's little-endian binary64 values, eight bytes a value in order, each slot of the
1154   Seq of Real the value's bits; refused as `logicaffeine_system::file::read_reals` refuses: a file it cannot read,
1155   or one that is not a whole number of values. */
1156logos_seq *logos_read_reals(const char *path) {
1157    FILE *f = fopen(path, "rb");
1158    char *why;
1159    size_t n;
1160    if (!f) {
1161        const char *reason = strerror(errno);
1162        n = strlen(path) + strlen(reason) + 64;
1163        why = (char *)lc_gc_alloc_atomic(n);
1164        snprintf(why, n, "readReals: cannot read '%s': %s", path, reason);
1165        logos_fatal(why);
1166    }
1167    fseek(f, 0, SEEK_END);
1168    long size = ftell(f);
1169    fseek(f, 0, SEEK_SET);
1170    if (size < 0 || size % 8 != 0) {
1171        fclose(f);
1172        n = strlen(path) + 96;
1173        why = (char *)lc_gc_alloc_atomic(n);
1174        snprintf(why, n, "readReals: '%s' holds %ld bytes, not a whole number of 8-byte Reals", path, size);
1175        logos_fatal(why);
1176    }
1177    logos_seq *out = logos_seq_new_cap(size / 8 > 0 ? size / 8 : 1);
1178    unsigned char b[8];
1179    for (long i = 0; i < size / 8; i++) {
1180        if (fread(b, 1, 8, f) != 8) logos_fatals("readReals: cannot read '%s'", path);
1181        unsigned long long bits = 0;
1182        for (int k = 7; k >= 0; k--) bits = (bits << 8) | b[k];
1183        logos_seq_push_inplace(out, (long long)bits);
1184    }
1185    fclose(f);
1186    return out;
1187}
1188
1189/* The length a UTF-8 sequence led by `b` claims, 0 for a byte no sequence starts with — Rust's
1190   `utf8_char_width`. */
1191static int logos_utf8_width(unsigned b) {
1192    if (b < 0x80) return 1;
1193    if (b < 0xC2) return 0;
1194    if (b < 0xE0) return 2;
1195    if (b < 0xF0) return 3;
1196    if (b < 0xF5) return 4;
1197    return 0;
1198}
1199
1200/* Rust's UTF-8 validation (`core::str::from_utf8`), step for step: 1 when `v[0..n)` is UTF-8, else 0 with
1201   where the valid prefix ends and the length of the invalid sequence there — 0 when the bytes end inside a
1202   sequence — the two facts `Utf8Error` reports. */
1203static int logos_utf8_valid(const unsigned char *v, long long n, long long *valid_up_to, int *error_len) {
1204    long long i = 0;
1205    while (i < n) {
1206        long long start = i;
1207        unsigned first = v[i], b;
1208#define LOGOS_UTF8_FAIL(len) do { *valid_up_to = start; *error_len = (len); return 0; } while (0)
1209#define LOGOS_UTF8_NEXT() do { if (++i >= n) LOGOS_UTF8_FAIL(0); b = v[i]; } while (0)
1210#define LOGOS_UTF8_CONT(len) do { LOGOS_UTF8_NEXT(); if (b < 0x80 || b >= 0xC0) LOGOS_UTF8_FAIL(len); } while (0)
1211        if (first >= 0x80) {
1212            switch (logos_utf8_width(first)) {
1213            case 2:
1214                LOGOS_UTF8_CONT(1);
1215                break;
1216            case 3:
1217                LOGOS_UTF8_NEXT();
1218                if (!((first == 0xE0 && b >= 0xA0 && b <= 0xBF) || (first >= 0xE1 && first <= 0xEC && b >= 0x80 && b <= 0xBF)
1219                      || (first == 0xED && b >= 0x80 && b <= 0x9F) || (first >= 0xEE && b >= 0x80 && b <= 0xBF)))
1220                    LOGOS_UTF8_FAIL(1);
1221                LOGOS_UTF8_CONT(2);
1222                break;
1223            case 4:
1224                LOGOS_UTF8_NEXT();
1225                if (!((first == 0xF0 && b >= 0x90 && b <= 0xBF) || (first >= 0xF1 && first <= 0xF3 && b >= 0x80 && b <= 0xBF)
1226                      || (first == 0xF4 && b >= 0x80 && b <= 0x8F)))
1227                    LOGOS_UTF8_FAIL(1);
1228                LOGOS_UTF8_CONT(2);
1229                LOGOS_UTF8_CONT(3);
1230                break;
1231            default:
1232                LOGOS_UTF8_FAIL(1);
1233            }
1234        }
1235#undef LOGOS_UTF8_CONT
1236#undef LOGOS_UTF8_NEXT
1237#undef LOGOS_UTF8_FAIL
1238        i++;
1239    }
1240    return 1;
1241}
1242
1243/* `text_from_bytes(bytes)` — the low byte of each element, as a Text when they are UTF-8. Otherwise the
1244   program stops with `logos_utf8_invalid_refusal` (of the invalid sequence's length and where it starts) or
1245   `logos_utf8_incomplete_refusal` (of where the unfinished sequence starts). */
1246char *logos_text_from_bytes(const logos_seq *b) {
1247    unsigned char *bytes = (unsigned char *)lc_gc_alloc_atomic((size_t)b->len + 1);
1248    for (long long i = 0; i < b->len; i++) bytes[i] = (unsigned char)(b->data[i] & 0xff);
1249    bytes[b->len] = '\0';
1250    long long at;
1251    int len;
1252    if (!logos_utf8_valid(bytes, b->len, &at, &len)) {
1253        char why[512];
1254        if (len) snprintf(why, sizeof why, logos_utf8_invalid_refusal, len, at);
1255        else snprintf(why, sizeof why, logos_utf8_incomplete_refusal, at);
1256        logos_fatal(lc_gc_strdup(why));
1257    }
1258    return (char *)bytes;
1259}
1260
1261void logos_seq_show_int(const logos_seq *a) {
1262    putchar('[');
1263    for (long long i = 0; i < a->len; i++) { if (i) fputs(", ", stdout); printf("%lld", a->data[i]); }
1264    fputs("]\n", stdout);
1265}
1266
1267void logos_seq_show_bool(const logos_seq *a) {
1268    putchar('[');
1269    for (long long i = 0; i < a->len; i++) { if (i) fputs(", ", stdout); fputs(a->data[i] ? "true" : "false", stdout); }
1270    fputs("]\n", stdout);
1271}
1272
1273void logos_seq_show_float(const logos_seq *a) {
1274    putchar('[');
1275    for (long long i = 0; i < a->len; i++) {
1276        if (i) fputs(", ", stdout);
1277        double f; memcpy(&f, &a->data[i], 8);
1278        char *s = logos_fmt_f64(f); fputs(s, stdout); lc_gc_free(s);
1279    }
1280    fputs("]\n", stdout);
1281}
1282
1283/* Text elements render UNQUOTED inside a list, like the VM's `[foo, bar]`. */
1284void logos_seq_show_text(const logos_seq *a) {
1285    putchar('[');
1286    for (long long i = 0; i < a->len; i++) { if (i) fputs(", ", stdout); fputs((const char *)a->data[i], stdout); }
1287    fputs("]\n", stdout);
1288}
1289
1290/* ---- Map (insertion-ordered key→value; parallel key/value arrays of uniform 8-byte cells) ---- */
1291typedef struct { long long len, cap; long long *keys; long long *vals; } logos_map;
1292
1293logos_map *logos_map_new(void) {
1294    logos_map *m = (logos_map *)lc_gc_alloc(sizeof(logos_map));
1295    m->len = 0; m->cap = 0; m->keys = NULL; m->vals = NULL;
1296    return m;
1297}
1298
1299logos_map *logos_map_new_cap(long long capacity) {
1300    if (capacity < 0) capacity = 0;
1301    logos_map *m = (logos_map *)lc_gc_alloc(sizeof(logos_map));
1302    m->len = 0; m->cap = capacity;
1303    m->keys = capacity ? (long long *)lc_gc_alloc((size_t)capacity * 8) : NULL;
1304    m->vals = capacity ? (long long *)lc_gc_alloc((size_t)capacity * 8) : NULL;
1305    return m;
1306}
1307
1308static logos_map *logos_map_dup(const logos_map *a) {
1309    logos_map *m = (logos_map *)lc_gc_alloc(sizeof(logos_map));
1310    m->len = a->len; m->cap = a->len;
1311    m->keys = a->len ? (long long *)lc_gc_alloc((size_t)a->len * 8) : NULL;
1312    m->vals = a->len ? (long long *)lc_gc_alloc((size_t)a->len * 8) : NULL;
1313    if (a->len) { memcpy(m->keys, a->keys, (size_t)a->len * 8); memcpy(m->vals, a->vals, (size_t)a->len * 8); }
1314    return m;
1315}
1316
1317/* `Set item key of m to val` — insert or update (insertion-ordered), in the register's own map (a copy
1318   when its owned flag is clear; see the Seq section). `key_kind` selects key compare. */
1319void logos_map_set_own(logos_map **slot, long long *owned, long long key, long long val, int key_kind) {
1320    if (!*owned) { *slot = logos_map_dup(*slot); *owned = 1; }
1321    logos_map *m = *slot;
1322    for (long long i = 0; i < m->len; i++) {
1323        if (logos_cell_eq(m->keys[i], key, key_kind)) { m->vals[i] = val; return; }
1324    }
1325    if (m->len == m->cap) {
1326        m->cap = m->cap ? m->cap * 2 : 4;
1327        m->keys = (long long *)lc_gc_realloc(m->keys, (size_t)m->cap * 8);
1328        m->vals = (long long *)lc_gc_realloc(m->vals, (size_t)m->cap * 8);
1329    }
1330    m->keys[m->len] = key; m->vals[m->len] = val; m->len++;
1331}
1332
1333/* The key-wise merge of two maps (the VM's `crdt_merge_field` on maps): `a`'s entries in its order, each key
1334   `b` also holds taking the merge of the two values — Ints summed (`value_rule` 0) or `b`'s value (1) — then
1335   `b`'s other keys in its order. A fresh map. */
1336logos_map *logos_map_merge(const logos_map *a, const logos_map *b, int key_kind, int value_rule) {
1337    logos_map *out = logos_map_dup(a);
1338    for (long long i = 0; i < b->len; i++) {
1339        long long j = 0;
1340        while (j < out->len && !logos_cell_eq(out->keys[j], b->keys[i], key_kind)) j++;
1341        if (j < out->len) {
1342            out->vals[j] = value_rule == 0
1343                ? (long long)((unsigned long long)out->vals[j] + (unsigned long long)b->vals[i])
1344                : b->vals[i];
1345            continue;
1346        }
1347        if (out->len == out->cap) {
1348            out->cap = out->cap ? out->cap * 2 : 4;
1349            out->keys = (long long *)lc_gc_realloc(out->keys, (size_t)out->cap * 8);
1350            out->vals = (long long *)lc_gc_realloc(out->vals, (size_t)out->cap * 8);
1351        }
1352        out->keys[out->len] = b->keys[i];
1353        out->vals[out->len] = b->vals[i];
1354        out->len++;
1355    }
1356    return out;
1357}
1358
1359/* `Merge`/`Converge` of cell `cell` (1-based) of two records of one type, for the fields the emitter does
1360   not merge inline: what the VM's `crdt_merge_field` makes of the target's value and the source's, by the
1361   emitter's rule — 3 a fresh counter holding the per-replica maximum, 4 the union of sets of element kind
1362   `a`, 5 the key-wise merge of maps keyed by kind `a` under value rule `b`. */
1363long long logos_merge_cell(const logos_seq *target, const logos_seq *source, long long cell, int how, int a, int b) {
1364    long long mine = target->data[cell - 1], theirs = source->data[cell - 1];
1365    switch (how) {
1366        case 3: {
1367            logos_crdt *merged = logos_crdt_clone((const logos_crdt *)mine);
1368            logos_crdt_merge(merged, (const logos_crdt *)theirs);
1369            return (long long)merged;
1370        }
1371        case 4: return (long long)logos_set_union((const logos_seq *)mine, (const logos_seq *)theirs, a);
1372        case 5: return (long long)logos_map_merge((const logos_map *)mine, (const logos_map *)theirs, a, b);
1373        default: abort();
1374    }
1375}
1376
1377/* `item key of m` — lookup; a missing key stops the program with the VM's sentence, naming the key. */
1378long long logos_map_get(const logos_map *a, long long key, int key_kind) {
1379    for (long long i = 0; i < a->len; i++) {
1380        if (logos_cell_eq(a->keys[i], key, key_kind)) return a->vals[i];
1381    }
1382    char *shown = logos_val_to_str(key, key_kind);
1383    size_t n = strlen(logos_missing_key_refusal) + strlen(shown) + 1;
1384    char *why = (char *)lc_gc_alloc_atomic(n);
1385    snprintf(why, n, logos_missing_key_refusal, shown);
1386    logos_fatal(why);
1387}
1388
1389/* `length of map` — the entry count. */
1390long long logos_map_len(const logos_map *a) { return a->len; }
1391
1392/* `map contains key` — key membership (never errors on a missing key), matching the VM's `Contains`. */
1393long long logos_map_has(const logos_map *a, long long key, int key_kind) {
1394    for (long long i = 0; i < a->len; i++) {
1395        if (logos_cell_eq(a->keys[i], key, key_kind)) return 1;
1396    }
1397    return 0;
1398}
1399
1400/* `Repeat for (k, v) in m` — the map's entries in order, each a `(key, value)` tuple. */
1401logos_seq *logos_map_pairs(const logos_map *m) {
1402    logos_seq *out = logos_seq_new();
1403    for (long long i = 0; i < m->len; i++) {
1404        logos_seq *pair = logos_seq_new();
1405        logos_seq_push_inplace(pair, m->keys[i]);
1406        logos_seq_push_inplace(pair, m->vals[i]);
1407        logos_seq_push_inplace(out, (long long)pair);
1408    }
1409    return out;
1410}
1411
1412/* `Show` of a map — `{k: v, ...}` (insertion order; keys/values formatted by kind, text unquoted). */
1413void logos_map_show(const logos_map *a, int key_kind, int val_kind) {
1414    putchar('{');
1415    for (long long i = 0; i < a->len; i++) {
1416        if (i) fputs(", ", stdout);
1417        char *k = logos_val_to_str(a->keys[i], key_kind);
1418        char *v = logos_val_to_str(a->vals[i], val_kind);
1419        printf("%s: %s", k, v);
1420        lc_gc_free(k); lc_gc_free(v);
1421    }
1422    fputs("}\n", stdout);
1423}
1424
1425/* ---- Deep copy and structural equality — the VM's `deep_clone` and `values_equal`, by the shape the emitter
1426   gives each kind. A shape is `[form, n, parts]`: forms 0 (Int), 7 (Text), 8 (Float) and 9 (Rational)
1427   are copied as values; 1 is a sequence with one element shape; 10 a set; 2 a map (key and value shapes);
1428   3 a declared enum with tagged constructor payload shapes; 6 a counter. */
1429/* Forms 11 (tuple) and 12 (record) use the sequence layout without collection identity.
1430   Forms 13/14 are Option/Result constructors. Forms 16..20 distinguish Bool, Date, Moment,
1431   Word32 and opaque identities from Int; 21 is a peer; 22 a cell of the exact extension; 23 `nothing`; 24 a span,
1432   its months and days packed into one word. */
1433typedef struct { long long form, n; const void *const *parts; } logos_shape;
1434typedef struct { long long tag, n; const logos_shape *const *parts; } logos_variant;
1435
1436/* Form 22 is a cell of the exact extension, which the extension's adapter copies and compares (two values of which
1437   one is a cell, each by its own shape), set when the shim embeds it. */
1438static long long (*logos_tower_copy_hook)(long long);
1439static int (*logos_tower_equal_hook)(long long, const logos_shape *, long long, const logos_shape *);
1440
1441long long logos_deep_copy(long long v, const logos_shape *s) {
1442    if (s->form == 22) return logos_tower_copy_hook(v);
1443    if (s->form == 0 || (s->form >= 7 && s->form <= 9) || s->form >= 16 || !v) return v;
1444    switch (s->form) {
1445        case 1:
1446        case 11:
1447        case 12:
1448        case 10: {
1449            const logos_seq *a = (const logos_seq *)v;
1450            logos_seq *c = logos_seq_new();
1451            for (long long i = 0; i < a->len; i++) {
1452                const logos_shape *cell = (const logos_shape *)s->parts[s->n == 1 ? 0 : i];
1453                logos_seq_push_inplace(c, logos_deep_copy(a->data[i], cell));
1454            }
1455            return (long long)c;
1456        }
1457        case 2: {
1458            const logos_map *a = (const logos_map *)v;
1459            logos_map *c = logos_map_dup(a);
1460            for (long long i = 0; i < a->len; i++) {
1461                c->keys[i] = logos_deep_copy(a->keys[i], (const logos_shape *)s->parts[0]);
1462                c->vals[i] = logos_deep_copy(a->vals[i], (const logos_shape *)s->parts[1]);
1463            }
1464            return (long long)c;
1465        }
1466        case 3:
1467        case 13:
1468        case 14: {
1469            const logos_seq *a = (const logos_seq *)v;
1470            const logos_variant *ctor = 0;
1471            for (long long i = 0; i < s->n; i++) {
1472                const logos_variant *r = (const logos_variant *)s->parts[i];
1473                if (r->tag == a->data[0]) ctor = r;
1474            }
1475            logos_seq *c = logos_seq_new();
1476            for (long long i = 0; i < a->len; i++) {
1477                long long cell = a->data[i];
1478                if (i > 0 && ctor && i - 1 < ctor->n) cell = logos_deep_copy(cell, ctor->parts[i - 1]);
1479                logos_seq_push_inplace(c, cell);
1480            }
1481            return (long long)c;
1482        }
1483        default: return (long long)logos_crdt_clone((const logos_crdt *)v);
1484    }
1485}
1486
1487/* Whether `a` and `b`, two values of shape `s`, are equal by structure. */
1488int logos_deep_eq(long long a, long long b, const logos_shape *s) {
1489    switch (s->form) {
1490        case 0: case 16: case 17: case 18: case 19: case 20: case 23: case 24: return a == b;
1491        case 22: return logos_tower_equal_hook(a, s, b, s);
1492        case 7: case 21: return strcmp((const char *)a, (const char *)b) == 0;
1493        case 8: { double x, y; memcpy(&x, &a, 8); memcpy(&y, &b, 8); return x == y; }
1494        case 9: return logos_ldec_cmp(a, 1, b, 1) == 0;
1495    }
1496    /* Only the VM's identity-bearing collections short-circuit on identity. A record, tuple or
1497       enum still compares its fields, so an aliased NaN payload is unequal to itself. */
1498    if (a == b && (s->form == 1 || s->form == 2 || s->form == 10)) return 1;
1499    if (!a || !b) return 0;
1500    switch (s->form) {
1501        case 1:
1502        case 11:
1503        case 12: {
1504            const logos_seq *x = (const logos_seq *)a, *y = (const logos_seq *)b;
1505            if (x->len != y->len) return 0;
1506            for (long long i = 0; i < x->len; i++) {
1507                if (!logos_deep_eq(x->data[i], y->data[i], (const logos_shape *)s->parts[s->n == 1 ? 0 : i])) return 0;
1508            }
1509            return 1;
1510        }
1511        case 10: {
1512            const logos_seq *x = (const logos_seq *)a, *y = (const logos_seq *)b;
1513            if (x->len != y->len) return 0;
1514            for (long long i = 0; i < x->len; i++) {
1515                long long j = 0;
1516                while (j < y->len && !logos_deep_eq(x->data[i], y->data[j], (const logos_shape *)s->parts[0])) j++;
1517                if (j == y->len) return 0;
1518            }
1519            return 1;
1520        }
1521        case 2: {
1522            const logos_map *x = (const logos_map *)a, *y = (const logos_map *)b;
1523            if (x->len != y->len) return 0;
1524            for (long long i = 0; i < x->len; i++) {
1525                long long j = 0;
1526                while (j < y->len && !logos_deep_eq(x->keys[i], y->keys[j], (const logos_shape *)s->parts[0])) j++;
1527                if (j == y->len || !logos_deep_eq(x->vals[i], y->vals[j], (const logos_shape *)s->parts[1])) return 0;
1528            }
1529            return 1;
1530        }
1531        case 3:
1532        case 13:
1533        case 14: {
1534            const logos_seq *x = (const logos_seq *)a, *y = (const logos_seq *)b;
1535            if (x->data[0] != y->data[0] || x->len != y->len) return 0;
1536            const logos_variant *ctor = 0;
1537            for (long long i = 0; i < s->n; i++) {
1538                const logos_variant *r = (const logos_variant *)s->parts[i];
1539                if (r->tag == x->data[0]) ctor = r;
1540            }
1541            for (long long i = 1; i < x->len; i++) {
1542                if (ctor && i - 1 < ctor->n ? !logos_deep_eq(x->data[i], y->data[i], ctor->parts[i - 1]) : x->data[i] != y->data[i]) return 0;
1543            }
1544            return 1;
1545        }
1546        default: return logos_crdt_sum((const logos_crdt *)a) == logos_crdt_sum((const logos_crdt *)b);
1547    }
1548}
1549
1550/* Cross-kind structural equality retains both layouts. Int and Float cells use exact numeric
1551   comparison, never an interpretation of one kind's bits as another kind's value. */
1552int logos_deep_eq_pair(long long a, long long b, const logos_shape *s, const logos_shape *t) {
1553    if (s == t) return logos_deep_eq(a, b, s);
1554    if (s->form == 22 || t->form == 22) return logos_tower_equal_hook(a, s, b, t);
1555    if (s->form == 6) return logos_deep_eq_pair(logos_crdt_sum((const logos_crdt *)a), b, &(logos_shape){0, 0, 0}, t);
1556    if (t->form == 6) return logos_deep_eq_pair(a, logos_crdt_sum((const logos_crdt *)b), s, &(logos_shape){0, 0, 0});
1557    if (s->form == 0 && t->form == 0) return a == b;
1558    if ((s->form == 0 && t->form == 8) || (s->form == 8 && t->form == 0)) {
1559        long long bits = s->form == 8 ? a : b;
1560        double f; memcpy(&f, &bits, 8);
1561        return logos_cmp_i64_f64_rel(s->form == 0 ? a : b, f, 0, 4);
1562    }
1563    if ((s->form == 9 && t->form == 0) || (s->form == 0 && t->form == 9)) {
1564        return logos_ldec_cmp(a, s->form == 9, b, t->form == 9) == 0;
1565    }
1566    if ((s->form == 9 && t->form == 8) || (s->form == 8 && t->form == 9)) {
1567        long long bits = s->form == 8 ? a : b;
1568        double f; memcpy(&f, &bits, 8);
1569        return logos_ldec_cmp_f64(s->form == 9 ? a : b, f) == 0;
1570    }
1571    if (s->form != t->form || !a || !b) return 0;
1572    switch (s->form) {
1573        case 1:
1574        case 11: {
1575            const logos_seq *x = (const logos_seq *)a, *y = (const logos_seq *)b;
1576            if (x->len != y->len) return 0;
1577            for (long long i = 0; i < x->len; i++) {
1578                const logos_shape *u = (const logos_shape *)s->parts[s->n == 1 ? 0 : i];
1579                const logos_shape *v = (const logos_shape *)t->parts[t->n == 1 ? 0 : i];
1580                if (!logos_deep_eq_pair(x->data[i], y->data[i], u, v)) return 0;
1581            }
1582            return 1;
1583        }
1584        case 2: {
1585            const logos_map *x = (const logos_map *)a, *y = (const logos_map *)b;
1586            if (x->len != y->len) return 0;
1587            for (long long i = 0; i < x->len; i++) {
1588                long long j = 0;
1589                while (j < y->len && !logos_deep_eq_pair(x->keys[i], y->keys[j],
1590                    (const logos_shape *)s->parts[0], (const logos_shape *)t->parts[0])) j++;
1591                if (j == y->len || !logos_deep_eq_pair(x->vals[i], y->vals[j],
1592                    (const logos_shape *)s->parts[1], (const logos_shape *)t->parts[1])) return 0;
1593            }
1594            return 1;
1595        }
1596        case 13:
1597        case 14: {
1598            const logos_seq *x = (const logos_seq *)a, *y = (const logos_seq *)b;
1599            if (x->len != y->len || x->data[0] != y->data[0]) return 0;
1600            const logos_variant *u = 0, *v = 0;
1601            for (long long i = 0; i < s->n; i++) {
1602                const logos_variant *r = (const logos_variant *)s->parts[i];
1603                if (r->tag == x->data[0]) u = r;
1604            }
1605            for (long long i = 0; i < t->n; i++) {
1606                const logos_variant *r = (const logos_variant *)t->parts[i];
1607                if (r->tag == y->data[0]) v = r;
1608            }
1609            if (!u || !v || u->n != v->n || x->len != u->n + 1) return 0;
1610            for (long long i = 0; i < u->n; i++) {
1611                if (!logos_deep_eq_pair(x->data[i+1], y->data[i+1], u->parts[i], v->parts[i])) return 0;
1612            }
1613            return 1;
1614        }
1615        /* Different nominal records/enums or different scalar kinds cannot compare equal. */
1616        default: return 0;
1617    }
1618}
1619
1620/* How a value of shape `s` displays, the VM's `to_display_string`: a scalar as `Show` prints it, a sequence as
1621   `[a, b]`, a set as `{a, b}`, a tuple as `(a, b)` and a map as `{k: v}`, each part by its own shape. */
1622typedef struct { char *text; size_t len, cap; } logos_text_buf;
1623
1624static void logos_buf_put(logos_text_buf *b, const char *s) {
1625    size_t n = strlen(s);
1626    if (b->len + n + 1 > b->cap) {
1627        size_t cap = b->cap * 2 > b->len + n + 1 ? b->cap * 2 : b->len + n + 1;
1628        b->text = (char *)lc_gc_realloc_atomic(b->text, cap);
1629        b->cap = cap;
1630    }
1631    memcpy(b->text + b->len, s, n + 1);
1632    b->len += n;
1633}
1634
1635static void logos_shaped_into(logos_text_buf *b, long long v, const logos_shape *s);
1636
1637static void logos_parts_into(logos_text_buf *b, const logos_seq *a, const logos_shape *s, const char *open, const char *close) {
1638    logos_buf_put(b, open);
1639    for (long long i = 0; i < a->len; i++) {
1640        if (i) logos_buf_put(b, ", ");
1641        logos_shaped_into(b, a->data[i], (const logos_shape *)s->parts[s->n == 1 ? 0 : i]);
1642    }
1643    logos_buf_put(b, close);
1644}
1645
1646static void logos_shaped_into(logos_text_buf *b, long long v, const logos_shape *s) {
1647    char *made = 0;
1648    switch (s->form) {
1649        case 0: case 19: made = logos_val_to_str(v, 0); break;
1650        case 16: made = logos_val_to_str(v, 2); break;
1651        case 7: logos_buf_put(b, (const char *)v); return;
1652        case 8: made = logos_val_to_str(v, 1); break;
1653        case 9: made = logos_val_to_str(v, 4); break;
1654        case 17: made = logos_val_to_str(v, 5); break;
1655        case 18: made = logos_val_to_str(v, 6); break;
1656        case 21: made = logos_val_to_str(v, 8); break;
1657        case 6: made = logos_val_to_str(logos_crdt_sum((const logos_crdt *)v), 0); break;
1658        case 22: made = logos_tower_text_hook(v); break;
1659        case 23: logos_buf_put(b, "nothing"); return;
1660        case 1: logos_parts_into(b, (const logos_seq *)v, s, "[", "]"); return;
1661        case 10: logos_parts_into(b, (const logos_seq *)v, s, "{", "}"); return;
1662        case 11: logos_parts_into(b, (const logos_seq *)v, s, "(", ")"); return;
1663        case 2: {
1664            const logos_map *m = (const logos_map *)v;
1665            logos_buf_put(b, "{");
1666            for (long long i = 0; i < m->len; i++) {
1667                if (i) logos_buf_put(b, ", ");
1668                logos_shaped_into(b, m->keys[i], (const logos_shape *)s->parts[0]);
1669                logos_buf_put(b, ": ");
1670                logos_shaped_into(b, m->vals[i], (const logos_shape *)s->parts[1]);
1671            }
1672            logos_buf_put(b, "}");
1673            return;
1674        }
1675        default: logos_fatalf("the RISC-V runtime has no display for a value of shape form %lld", s->form);
1676    }
1677    logos_buf_put(b, made);
1678    lc_gc_free(made);
1679}
1680
1681char *logos_shaped_str(long long v, const logos_shape *s) {
1682    logos_text_buf b = {0, 0, 0};
1683    logos_buf_put(&b, "");
1684    logos_shaped_into(&b, v, s);
1685    return b.text;
1686}
1687
1688void logos_show_shaped(long long v, const logos_shape *s) {
1689    char *text = logos_shaped_str(v, s);
1690    printf("%s\n", text);
1691    lc_gc_free(text);
1692}
1693
1694/* Snapshot iterator stack (for `Repeat for x in xs`). A snapshot is just the handle: the iterated
1695 * register's handle escapes into the iterator, which clears the register's owned flag, so a mutation of it
1696 * inside the loop copies first and the iterated list never changes under the iterator. Each task iterates
1697 * on a stack of its own, so a task that yields inside a loop resumes it where it left off (`logos_iters`). */
1698typedef struct { logos_seq *seq[256]; long long idx[256]; int top; } logos_iter_stack;
1699static logos_iter_stack *logos_iters(void);
1700void logos_iter_begin(logos_seq *s) {
1701    logos_iter_stack *it = logos_iters();
1702    it->seq[it->top] = s;
1703    it->idx[it->top] = 0;
1704    it->top++;
1705}
1706int logos_iter_has(void) {
1707    logos_iter_stack *it = logos_iters();
1708    int t = it->top - 1;
1709    return it->idx[t] < it->seq[t]->len;
1710}
1711long long logos_iter_take(void) {
1712    logos_iter_stack *it = logos_iters();
1713    int t = it->top - 1;
1714    return it->seq[t]->data[it->idx[t]++];
1715}
1716void logos_iter_end(void) { logos_iters()->top--; }
1717
1718/* ---- Deterministic cooperative concurrency: the C family's scheduler core under `ucontext` ---- */
1719"#,
1720    "#define LOGOS_FN static __attribute__((unused))\n",
1721    "#define LOGOS_VAR static\n",
1722    "#define LOGOS_API\n",
1723    "#define logos_alloc(n) lc_gc_calloc(1, (n))\n",
1724    "#define logos_release(p) lc_gc_free(p)\n",
1725    "#define LOGOS_STACK_ENTER(lo, hi) lc_gc_stack_enter((lo), (hi))\n",
1726    "#define LOGOS_STACK_LEAVE() lc_gc_stack_leave()\n",
1727    "#define LOGOS_TASK_TIER ucontext_t ctx; void *stack; void *fn; long long nargs; long long *args; logos_iter_stack *iters;\n",
1728    "static long long logos_riscv_call(void *fn, long long n, const long long *a);\n",
1729    "#define LOGOS_TASK_CALL(t) logos_riscv_call((t)->fn, (t)->nargs, (t)->args)\n",
1730    include_str!("../codegen_c/sched_core.c"),
1731    include_str!("../codegen_c/host_seed.c"),
1732    include_str!("../codegen_c/opt.c"),
1733    include_str!("../codegen_c/ucontext_tier.c"),
1734    r#"
1735/* A task body is an emitted `logos_fn_N`, which takes its arguments in a0…a7, as every call does. */
1736static long long logos_riscv_call(void *fn, long long n, const long long *a) {
1737    switch (n) {
1738        case 0: return ((long long (*)(void))fn)();
1739        case 1: return ((long long (*)(long long))fn)(a[0]);
1740        case 2: return ((long long (*)(long long, long long))fn)(a[0], a[1]);
1741        case 3: return ((long long (*)(long long, long long, long long))fn)(a[0], a[1], a[2]);
1742        case 4: return ((long long (*)(long long, long long, long long, long long))fn)(a[0], a[1], a[2], a[3]);
1743        case 5: return ((long long (*)(long long, long long, long long, long long, long long))fn)(a[0], a[1], a[2], a[3], a[4]);
1744        case 6: return ((long long (*)(long long, long long, long long, long long, long long, long long))fn)(a[0], a[1], a[2], a[3], a[4], a[5]);
1745        case 7: return ((long long (*)(long long, long long, long long, long long, long long, long long, long long))fn)(a[0], a[1], a[2], a[3], a[4], a[5], a[6]);
1746        default: return ((long long (*)(long long, long long, long long, long long, long long, long long, long long, long long))fn)(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
1747    }
1748}
1749
1750/* The running task's iterator stack; the program's own when no task runs. */
1751static logos_iter_stack logos_iters_outside;
1752static logos_iter_stack *logos_iters(void) {
1753    if (!logos_current) return &logos_iters_outside;
1754    if (!logos_current->iters) logos_current->iters = (logos_iter_stack *)lc_gc_calloc(1, sizeof(logos_iter_stack));
1755    return logos_current->iters;
1756}
1757
1758extern void *logos_fn_dispatch[];
1759
1760/* `Launch a task to f …` from the emitted code: the body by its index in `logos_fn_dispatch`, the
1761   arguments as the launching frame's contiguous slots. */
1762long long logos_spawn_fn(long long func_idx, long long nargs, const long long *args) {
1763    return logos_spawn(logos_fn_dispatch[func_idx], nargs, args);
1764}
1765
1766/* The box a select's receive arm resolves into: the emitted code reads it back at the wait. */
1767logos_opt *logos_opt_nothing(void) { return (logos_opt *)lc_gc_calloc(1, sizeof(logos_opt)); }
1768
1769/* ---- Networking — the VM's offline `NetInbox`: one node, no relay ----
1770   `Listen` names this node's inbox. `Send`/`Stream` put the message, stamped with the sender's inbox (empty
1771   before any `Listen`), on the loopback outbox under the topic it is addressed to. `Await` needs an inbox: it
1772   moves each outboxed message into the received queue when it is addressed to the inbox, dropping it
1773   otherwise, then takes the first received message (or batch) from the peer it names. A lone node sends
1774   nothing while it waits, so when that peer's message is not there the program waits for ever, as the VM's
1775   `Await` polls for ever. A topic is canonical: `logos_net_topic`, the relay's `canonical_topic`. */
1776
1777/* Whether `s` begins with `prefix`, ASCII case folded (`to_ascii_lowercase().starts_with`). */
1778static int logos_ascii_prefix(const char *s, const char *prefix) {
1779    for (; *prefix; s++, prefix++) {
1780        char c = *s >= 'A' && *s <= 'Z' ? (char)(*s - 'A' + 'a') : *s;
1781        if (c != *prefix) return 0;
1782    }
1783    return 1;
1784}
1785
1786/* Whether the `n` bytes at `s` parse as a `u16` (`str::parse::<u16>`: an optional `+`, then digits). */
1787static int logos_is_u16(const char *s, size_t n) {
1788    if (n && s[0] == '+') { s++; n--; }
1789    if (!n) return 0;
1790    unsigned long v = 0;
1791    for (size_t i = 0; i < n; i++) {
1792        if (s[i] < '0' || s[i] > '9') return 0;
1793        v = v * 10 + (unsigned long)(s[i] - '0');
1794        if (v > 65535) return 0;
1795    }
1796    return 1;
1797}
1798
1799/* `multiaddr_to_ws_url` — the `ws://` URL a libp2p multiaddr (or a ws/wss URL) names, or NULL when it names
1800   none. */
1801static char *logos_multiaddr_url(const char *addr) {
1802    char *t = logos_trim(addr);
1803    if (!*t) return NULL;
1804    if (logos_ascii_prefix(t, "ws://") || logos_ascii_prefix(t, "wss://")) return t;
1805    if (t[0] != '/') return NULL;
1806    long long ns = 1;
1807    for (const char *p = t + 1; *p; p++) ns += *p == '/';
1808    const char **seg = (const char **)lc_gc_alloc((size_t)ns * sizeof *seg);
1809    size_t *len = (size_t *)lc_gc_alloc_atomic((size_t)ns * sizeof *len);
1810    const char *p = t + 1;
1811    for (long long k = 0; k < ns; k++) {
1812        const char *q = strchr(p, '/');
1813        seg[k] = p;
1814        len[k] = q ? (size_t)(q - p) : strlen(p);
1815        p = q ? q + 1 : p + len[k];
1816    }
1817#define LOGOS_SEG(k, name) (len[k] == strlen(name) && strncmp(seg[k], name, len[k]) == 0)
1818    const char *host = NULL, *port = NULL, *scheme = "ws";
1819    size_t host_len = 0, port_len = 0;
1820    int v6 = 0;
1821    long long i = 0;
1822    while (i < ns) {
1823        if (LOGOS_SEG(i, "ip4") || LOGOS_SEG(i, "dns") || LOGOS_SEG(i, "dns4") || LOGOS_SEG(i, "dns6")
1824            || LOGOS_SEG(i, "dnsaddr") || LOGOS_SEG(i, "ip6")) {
1825            if (i + 1 >= ns || len[i + 1] == 0) return NULL;
1826            v6 = LOGOS_SEG(i, "ip6");
1827            host = seg[i + 1];
1828            host_len = len[i + 1];
1829            i += 2;
1830        } else if (LOGOS_SEG(i, "tcp")) {
1831            if (i + 1 >= ns || len[i + 1] == 0 || !logos_is_u16(seg[i + 1], len[i + 1])) return NULL;
1832            port = seg[i + 1];
1833            port_len = len[i + 1];
1834            i += 2;
1835        } else if (LOGOS_SEG(i, "ws")) {
1836            scheme = "ws";
1837            i += 1;
1838        } else if (LOGOS_SEG(i, "wss") || LOGOS_SEG(i, "tls")) {
1839            scheme = "wss";
1840            i += 1;
1841        } else if (LOGOS_SEG(i, "p2p")) {
1842            i += 2;
1843        } else if (LOGOS_SEG(i, "p2p-circuit")) {
1844            i += 1;
1845        } else {
1846            return NULL;
1847        }
1848    }
1849#undef LOGOS_SEG
1850    if (!host || !port) return NULL;
1851    size_t n = strlen(scheme) + host_len + port_len + 8;
1852    char *url = (char *)lc_gc_alloc_atomic(n);
1853    snprintf(url, n, v6 ? "%s://[%.*s]:%.*s" : "%s://%.*s:%.*s", scheme, (int)host_len, host, (int)port_len, port);
1854    return url;
1855}
1856
1857/* `canonical_topic` — the relay topic an address names: its `ws://` URL, else the address trimmed. */
1858char *logos_net_topic(const char *addr) {
1859    char *url = logos_multiaddr_url(addr);
1860    return url ? url : logos_trim(addr);
1861}
1862
1863typedef struct { char *topic; char *from; long long value; int batch; } logos_net_msg;
1864typedef struct { logos_net_msg *at; long long len, cap; } logos_net_queue;
1865static logos_net_queue logos_net_outbox, logos_net_received;
1866static char *logos_net_inbox = NULL;
1867
1868static void logos_net_put(logos_net_queue *q, logos_net_msg m) {
1869    if (q->len == q->cap) {
1870        q->cap = q->cap ? q->cap * 2 : 8;
1871        q->at = (logos_net_msg *)lc_gc_realloc(q->at, (size_t)q->cap * sizeof *q->at);
1872    }
1873    q->at[q->len++] = m;
1874}
1875
1876/* `Listen on <topic>` — `topic` (canonical) becomes this node's inbox. */
1877void logos_net_listen(char *topic) { logos_net_inbox = topic; }
1878
1879/* `Send`/`Stream` to `topic` (canonical): `how` is 0 for a message, 1 for a batch that is the sequence
1880   `value`, 2 for a batch of the one value `value`. */
1881void logos_net_send(const char *topic, long long value, int how) {
1882    if (how == 2) {
1883        logos_seq *one = logos_seq_new();
1884        logos_seq_push_inplace(one, value);
1885        value = (long long)one;
1886    }
1887    logos_net_msg m = { lc_gc_strdup(topic), lc_gc_strdup(logos_net_inbox ? logos_net_inbox : ""), value, how != 0 };
1888    logos_net_put(&logos_net_outbox, m);
1889}
1890
1891/* `Await [stream] from <want>` (canonical) — the first message (`batch` 0) or batch (`batch` 1) `want` sent to
1892   this node's inbox. Without an inbox the program stops with `logos_await_without_listen_refusal`. */
1893long long logos_net_await(const char *want, int batch) {
1894    if (!logos_net_inbox) logos_fatal(logos_await_without_listen_refusal);
1895    for (long long i = 0; i < logos_net_outbox.len; i++) {
1896        if (strcmp(logos_net_outbox.at[i].topic, logos_net_inbox) == 0) logos_net_put(&logos_net_received, logos_net_outbox.at[i]);
1897    }
1898    logos_net_outbox.len = 0;
1899    for (long long i = 0; i < logos_net_received.len; i++) {
1900        logos_net_msg m = logos_net_received.at[i];
1901        if (m.batch == batch && strcmp(m.from, want) == 0) {
1902            memmove(&logos_net_received.at[i], &logos_net_received.at[i + 1], (size_t)(logos_net_received.len - i - 1) * sizeof m);
1903            logos_net_received.len--;
1904            return m.value;
1905        }
1906    }
1907    fflush(stdout);
1908    for (;;) pause();
1909}
1910"#,
1911);