Skip to main content

logicaffeine_compile/codegen_c/
from_program.rs

1//! ★ Phase 1 — the `CompiledProgram → C` Op-walker (bytecode-IR-as-source-of-truth re-base) ★
2//!
3//! The SYSTEMS-tier C backend that hangs off the bytecode VM, mirroring `codegen_riscv`'s `Emitter`: walk the
4//! `CompiledProgram`'s flat `Op` stream and lower each op to C over a register file, with `Jump`/`JumpIfFalse`/
5//! `JumpIfTrue` reconstructed as `goto` on the same CFG (a relooper recovering `while`/`if` for gcc's
6//! vectorizer is a later perf pass; correctness first). User functions are separate regions (delimited by
7//! `entry_pc`), each emitted as a C function under the VM's register-windowing call convention. Because it
8//! lowers the bytecode, it inherits the whole optimizer lattice — the perf win the plan predicts.
9//!
10//! **Uniform register file.** Every register is one `int64_t` slot. Scalars of every kind ride it: an `Int`
11//! holds its value, a `Bool` holds 0/1, a `Float` holds its IEEE-754 *bit pattern* (recovered with `as_f64`),
12//! a `Text` holds a `char*` reinterpreted as an integer, and a `Seq` holds a heap-boxed `Seq_i64*`/`Seq_f64*`/
13//! `Seq_str*`/`Seq_bool*` (the proven `codegen_c` value-typed runtime, boxed so its handle fits a slot). A
14//! per-register `Kind`, inferred by a forward pass mirroring `codegen_riscv::infer_kinds`, drives how an op
15//! combines its operands (int vs float), how a collection op picks its typed runtime call, and how `Show`
16//! renders — exactly the proven fabric/wpe model where "float rides in the i64 regs as bit patterns."
17//!
18//! Grown op-by-op behind `c_bytecode_lock` (`program_outcome(P) == c_bytecode_run(P)`); a not-yet-lowered op
19//! (or a kind this pass does not model) is a LOUD `Err` (a typed refusal, never wrong code), exactly like
20//! `codegen_riscv`'s `Unsupported`. The AST-based `codegen_c` stays the shipping path until this Op-walker
21//! covers the corpus, then `compile_to_c` swaps to it and the AST entanglement is deleted.
22use std::fmt::Write;
23
24use super::runtime::{
25    c_net_runtime, c_runtime, c_vfs_runtime, C_CRDT_RUNTIME, C_GC_COLLECTOR, C_TCP_RUNTIME,
26};
27use super::words;
28use crate::analysis::{PolicyCondition, PolicyRegistry};
29use crate::intern::Interner;
30use crate::interpreter::RuntimeValue;
31use crate::vm::{BoundaryType, CompiledProgram, Constant, Op, SlotKind};
32
33mod exactness;
34mod numbers;
35mod tower;
36
37/// The rounds a region's exact webs and number joins take to hold still before the region is refused: each round
38/// only widens what a variable holds, and no chain of widenings is this long.
39const SETTLE_ROUNDS: usize = 64;
40
41/// The kind carried in an `int64_t` register slot. Grown milestone by milestone; a kind this pass does not
42/// model is `Unknown` and any kind-sensitive op on it is a typed refusal.
43#[derive(Clone, Copy, PartialEq, Eq, Debug)]
44enum Kind {
45    Int,
46    Bool,
47    Float,
48    Text,
49    /// An optional value — the result of `Pop` (`Some(x)` when the seq was non-empty, else `nothing`). A boxed
50    /// `int64_t[2]` = `[present, value]`; `Show` renders the inner value (by the interned element kind) or the
51    /// literal `nothing`, exactly as the VM's `RuntimeValue::Nothing`. The `u16` indexes `opt_types` (the inner
52    /// element kind), so `Show` knows how to render a present payload.
53    Optional(u16),
54    /// An exact integer, held where the program can make an `Int` outgrow i64 ([`Emitter::exactness`]). It rides
55    /// its register tagged: an even word is the value shifted left once (any integer within 63 bits, so the zero
56    /// word is 0), an odd word a boxed `LInt*` with its low bit set. The `lc_x*` operations stay on the word
57    /// while the value fits it and go through the runtime's `lint_*` tower when it does not — the VM's promotion.
58    BigInt,
59    /// An exact rational (LOGOS `Rational`/`Decimal`), the C mirror of `LogosRational` — a heap-boxed `LDec*`
60    /// (`{ is_rat, num, den, scale }` over `LBig`) reinterpreted as the register's integer. `7 / 2` stays `7/2`
61    /// exact (never the floored `3`); arithmetic routes through the runtime's `ldec_add`/`sub`/`mul`/`div`, and
62    /// `Show`/interpolation render it via `ldec_to_string`. Boxed exactly like `Text`/`Struct` (a pointer in the
63    /// uniform i64 slot), so it composes with `Move`/`Let`/`Call` for free.
64    Rational,
65    /// A heap-boxed sequence handle, by scalar element kind.
66    SeqInt,
67    SeqFloat,
68    SeqText,
69    SeqBool,
70    /// A heap-boxed sequence of values of a kind with no scalar cell of its own — tuples, records, enums, other
71    /// collections — a `Seq_i64` whose cells are the elements' register encodings (a heap pointer rides the
72    /// uniform i64 slot). The `u16` interns the element kind (`Emitter::seq_elems`), so an element read out of
73    /// it — by index, by iteration, by pop — is the value it was.
74    SeqOf(u16),
75    /// A sequence created empty whose element kind is not yet known (refined by a later push) — refused.
76    SeqUnknown,
77    /// A heap-boxed insertion-ordered dedup'd set handle (`Set_i64*`/`Set_str*`), by scalar element kind
78    /// (Int/Text only — the runtime's two self-describing set kinds, matching the RISC-V/WASM tiers).
79    SetInt,
80    /// A set of machine words of the given width: a `Set_i64` of their zero-extended bits, shown unsigned.
81    SetWord(u8),
82    SetText,
83    /// A set created empty whose element kind is not yet known (refined by a later `SetAdd`) — refused.
84    SetUnknown,
85    /// A counter CRDT handle (a `logos_crdt*` = per-replica count map) riding the uniform i64 slot. Produced by
86    /// `NewCrdt` for a G-Counter (`ConvergentCount`, kind 4) or a PN-Counter (`Tally`, kind 5). It lives in a
87    /// struct field whose DECLARED type is `Int`; a `GetField` on such a field SUMS the map to an `Int` (matching
88    /// the VM's `Show c's points` = the counter total), and `CrdtMerge`/`CrdtJoin` take the per-replica MAX
89    /// (idempotent — a redelivered state never double-counts). Mirrors `codegen_riscv::Kind::CrdtCounter`.
90    CrdtCounter,
91    /// A user-struct handle (a heap buffer of one `int64_t` cell per field, in declaration order). The `u16`
92    /// indexes `prog.struct_types`, so the field layout stays reachable while `Kind` remains `Copy`.
93    Struct(u16),
94    /// A user-struct handle whose declared type DID NOT resolve statically (an unmodeled field type left the
95    /// struct out of `prog.struct_types` — e.g. a `Tally` whose `n: a Counter` field names an undeclared type).
96    /// The VM still runs such a struct dynamically (its `CrdtBump` on a `Nothing` cell yields a plain `Int`), so
97    /// the walker synthesizes the field layout from the bytecode: the `u16` indexes the Emitter's `synth_structs`
98    /// interning table (name + field-name-in-cell-order), collected by scanning the field ops on the struct.
99    StructSynth(u16),
100    /// A user-enum (inductive) handle — a heap buffer `[variant-tag, payload…]` (tag = the variant's index in
101    /// the declaration). The `u16` indexes `prog.enum_types`.
102    Enum(u16),
103    /// An enum the program never declares — `Option`, `Result`, a library enum outside `prog.enum_types` — laid
104    /// out like a declared one: a heap `[tag, payload…]` buffer. Its constructors are known only by name, which is
105    /// how the VM matches them too (`ind.constructor == name`), so each type's constructors get their tags in one
106    /// program-wide order (`Emitter::ctor_tags`). The `u16` interns the type with the payload kinds known for each
107    /// constructor (`Emitter::inductive_types`), so a bound payload shows and computes as what it is.
108    Inductive(u16),
109    /// `nothing` — the unit value (a `Unit` result's payload, a field before its first write), riding the slot
110    /// as `0` and shown as `nothing`.
111    Nothing,
112    /// An anonymous tuple handle — a heap buffer of one `int64_t` cell per position (positional, like a struct
113    /// with no field names). The `u16` indexes the Emitter's `tuple_types` interning table: tuples are
114    /// structural/unnamed, so — unlike `Struct`/`Enum` — there is no program-level type table to point at.
115    Tuple(u16),
116    /// A heap-boxed hash map handle. The `u16` indexes the Emitter's `map_types` interning table of (key kind,
117    /// value kind): the KEY is a runtime-hashable scalar (Int→i64 / Text→str) and the VALUE may be ANY kind
118    /// (it rides the i64/str cell as its register encoding), so the true value kind is remembered for `Index`.
119    Map(u16),
120    /// A map created empty whose key/value kinds are not yet known (refined by the first `SetIndex`) — an
121    /// unpopulated one boxes a `Map_i64_i64` and renders `{}`.
122    MapUnknown,
123    /// A peer handle (`a PeerAgent at …`): its canonical relay topic, a `char*`, shown as `<peer topic>`.
124    Peer,
125    /// A calendar date (`today`), riding the slot as its day count since the epoch, shown `YYYY-MM-DD`.
126    Date,
127    /// An instant (`now`), riding the slot as its nanoseconds since the epoch, shown `YYYY-MM-DD HH:MM`.
128    Moment,
129    /// A time of day, riding the slot as its nanoseconds since midnight.
130    Time,
131    /// A calendar span, riding the slot as its months in the high 32 bits and its days in the low 32.
132    Span,
133    /// A wrapping machine word of the given width in bits (8, 16, 32 or 64): the ring ℤ/2ⁿ, riding the slot
134    /// zero-extended, shown as its unsigned value.
135    Word(u8),
136    /// A value of the exact extension (an infinitesimal, a hyperreal, an algebraic number, an ordinal, … and every
137    /// list of them): an `LTower` cell around a handle into the linked value runtime, riding the slot as a pointer
138    /// (see `tower`).
139    Tower,
140    /// A value that cannot exist: an element of a sequence nothing ever puts an element in. Reading one fails
141    /// (an index into an empty sequence) or never happens (a loop over one), so every op that would use it is
142    /// unreachable.
143    Never,
144    Unknown,
145}
146
147impl Kind {
148    /// The element kind of a sequence/set kind of scalar elements (`Unknown` otherwise; a `SeqOf`'s element
149    /// kind is interned, see [`Emitter::elem_kind`]).
150    fn elem(self) -> Kind {
151        match self {
152            Kind::SeqInt | Kind::SetInt => Kind::Int,
153            Kind::SetWord(bits) => Kind::Word(bits),
154            Kind::SeqFloat => Kind::Float,
155            Kind::SeqText | Kind::SetText => Kind::Text,
156            Kind::SeqBool => Kind::Bool,
157            _ => Kind::Unknown,
158        }
159    }
160    /// The set kind whose element is `self` (`SetUnknown` for an element with no self-describing set kind).
161    fn set_of(self) -> Kind {
162        match self {
163            Kind::Int => Kind::SetInt,
164            Kind::Word(bits) => Kind::SetWord(bits),
165            Kind::Text => Kind::SetText,
166            _ => Kind::SetUnknown,
167        }
168    }
169    /// The C `Seq_*` struct type for a sequence kind.
170    fn seq_c_type(self) -> Option<&'static str> {
171        match self {
172            Kind::SeqInt => Some("Seq_i64"),
173            Kind::SeqFloat => Some("Seq_f64"),
174            Kind::SeqText => Some("Seq_str"),
175            Kind::SeqBool => Some("Seq_bool"),
176            // A seq of non-scalar values is a `Seq_i64` of their register encodings.
177            Kind::SeqOf(_) => Some("Seq_i64"),
178            _ => None,
179        }
180    }
181    /// The C `Set_*` struct type for a set kind.
182    fn set_c_type(self) -> Option<&'static str> {
183        match self {
184            Kind::SetInt | Kind::SetWord(_) => Some("Set_i64"),
185            Kind::SetText => Some("Set_str"),
186            _ => None,
187        }
188    }
189    /// The C function-name infix (`i64`/`f64`/`str`/`bool`) for a sequence OR set kind.
190    fn seq_infix(self) -> Option<&'static str> {
191        match self {
192            Kind::SeqInt | Kind::SetInt | Kind::SetWord(_) | Kind::SeqOf(_) => Some("i64"),
193            Kind::SeqFloat => Some("f64"),
194            Kind::SeqText | Kind::SetText => Some("str"),
195            Kind::SeqBool => Some("bool"),
196            _ => None,
197        }
198    }
199}
200
201/// True for a set kind whose element type is known (so its boxed `Set_*` can be allocated).
202fn is_concrete_set(k: Kind) -> bool {
203    matches!(k, Kind::SetInt | Kind::SetWord(_) | Kind::SetText)
204}
205
206/// The concrete set kind to operate on: a `SetUnknown` (an empty, never-added-to set) was boxed as a
207/// `Set_i64`, so it operates as `SetInt`; a non-set is `None`.
208fn resolve_set(k: Kind) -> Option<Kind> {
209    match k {
210        Kind::SetInt | Kind::SetUnknown => Some(Kind::SetInt),
211        Kind::SetWord(bits) => Some(Kind::SetWord(bits)),
212        Kind::SetText => Some(Kind::SetText),
213        _ => None,
214    }
215}
216
217/// The sequence kind to operate on: a sequence made empty whose element kind never became known was boxed
218/// as a `Seq_i64`, so it operates as `SeqInt`; every other kind is itself.
219fn seq_kind_of(k: Kind) -> Kind {
220    if k == Kind::SeqUnknown {
221        Kind::SeqInt
222    } else {
223        k
224    }
225}
226
227/// True for any sequence kind, its elements known or not.
228fn is_sequence(k: Kind) -> bool {
229    k.seq_c_type().is_some() || k == Kind::SeqUnknown
230}
231
232/// True for a sequence kind whose element type is known (so its boxed `Seq_*` can be allocated).
233fn is_concrete_seq(k: Kind) -> bool {
234    matches!(
235        k,
236        Kind::SeqInt | Kind::SeqFloat | Kind::SeqText | Kind::SeqBool | Kind::SeqOf(_)
237    )
238}
239
240// ── World-class maps: any hashable key (the runtime types Int→i64 / Text→str), any VALUE. ──────────────────
241// A value of ANY kind rides the map's storage cell as its uniform i64 register encoding — an integer as an exact tagged word, a
242// Float as its bits, a Bool as 0/1, a Text as its `char*`, and a Seq/Set/Struct/Enum/Tuple/Map as its heap
243// pointer. So the C runtime's `Map_<key>_<i64|str>` (str cell for a Text value, i64 cell for everything else)
244// stores it losslessly, and `Index` recovers it with the interned logical value kind — insert/get/length/
245// contains work for every value type. `Show` of a whole map renders only the runtime-renderable value kinds
246// (Int, Text); a Float/Bool/composite value refuses `Show` (its i64 cell would print as an integer), never
247// miscompiling. The (key,value) kinds are interned so `Kind::Map(u16)` stays `Copy`.
248
249/// The C storage cell (`i64`/`str`) for a map KEY — only Int/Text are hashable by the runtime's typed maps.
250fn map_key_storage(k: Kind) -> Option<&'static str> {
251    match k {
252        Kind::Int | Kind::Word(_) => Some("i64"),
253        Kind::Text => Some("str"),
254        _ => None,
255    }
256}
257/// The C storage cell for a map VALUE — `str` for a Text value, `i64` for every other concrete kind (its
258/// register encoding rides the i64 cell); `None` only for a not-yet-known value kind.
259fn map_val_storage(k: Kind) -> Option<&'static str> {
260    match k {
261        Kind::Unknown => None,
262        Kind::Text => Some("str"),
263        _ => Some("i64"),
264    }
265}
266/// The runtime `Map_<key>_<val>` struct type for a (key,value) kind pair, or `None` if unsupported.
267fn map_type_name(key: Kind, val: Kind) -> Option<String> {
268    Some(format!(
269        "Map_{}_{}",
270        map_key_storage(key)?,
271        map_val_storage(val)?
272    ))
273}
274/// The runtime function-name prefix `map_<key>_<val>` for a (key,value) kind pair.
275fn map_fn(key: Kind, val: Kind) -> Option<String> {
276    Some(format!(
277        "map_{}_{}",
278        map_key_storage(key)?,
279        map_val_storage(val)?
280    ))
281}
282/// A map handle register cast to its boxed `Map_<key>_<val>*`.
283fn map_ptr_kv(reg: u16, key: Kind, val: Kind) -> Option<String> {
284    Some(format!(
285        "(({}*)(intptr_t)r[{reg}])",
286        map_type_name(key, val)?
287    ))
288}
289/// A register holding a map KEY, as the runtime function's C argument (Int rides its i64 slot, Text is its `char*`).
290fn map_key_arg(reg: u16, key: Kind) -> String {
291    match key {
292        Kind::Text => format!("(const char*)(intptr_t)r[{reg}]"),
293        _ => format!("r[{reg}]"),
294    }
295}
296/// A register holding a map VALUE, as the runtime function's C argument — a Text value is its `char*`, any
297/// other value uses its i64 cell. Machine integers are tagged to the map's stable exact encoding;
298/// exact integers, Float bits, Bool values, and heap pointers are stored verbatim.
299fn map_val_store(reg: u16, val: Kind) -> String {
300    match val {
301        Kind::Text => format!("(char*)(intptr_t)r[{reg}]"),
302        Kind::Int => format!("lc_xof(r[{reg}])"),
303        _ => format!("r[{reg}]"),
304    }
305}
306/// A `map_*_get(...)` result expression turned back into the i64 register value for the value kind.
307fn map_val_load(get: &str, val: Kind) -> String {
308    match val {
309        Kind::Text => format!("(int64_t)(intptr_t)({get})"),
310        _ => get.to_string(),
311    }
312}
313fn is_concrete_map(k: Kind) -> bool {
314    matches!(k, Kind::Map(_))
315}
316
317/// Map a declared boundary type to the C register kind (scalar + sequence subset — anything else is left
318/// `Unknown` and soundly refused if an op needs its kind).
319/// `true` if `pc` falls inside any inline-closure hole `[s, e)` — its op belongs to an embedded closure body
320/// emitted as its own region, so the enclosing region skips it (control jumps over it at runtime).
321fn in_hole(pc: usize, holes: &[(usize, usize)]) -> bool {
322    holes.iter().any(|&(s, e)| pc >= s && pc < e)
323}
324
325/// A region `[start, end)` of the walker, around its holes, as the variable analysis reads it.
326struct WalkerRegion<'e, 'a> {
327    emitter: &'e Emitter<'a>,
328    start: usize,
329    end: usize,
330    holes: &'e [(usize, usize)],
331}
332
333impl crate::vm::variables::Flow for WalkerRegion<'_, '_> {
334    fn pcs(&self) -> Vec<usize> {
335        (self.start..self.end).filter(|pc| !in_hole(*pc, self.holes)).collect()
336    }
337
338    fn successors(&self, pc: usize) -> Vec<usize> {
339        self.emitter.successors(pc, self.start, self.end, self.holes)
340    }
341
342    fn written(&self, op: &Op, f: &mut dyn FnMut(u16)) {
343        self.emitter.for_each_written(op, f);
344    }
345
346    fn read(&self, op: &Op, f: &mut dyn FnMut(u16)) {
347        op.for_each_read_reg(|func| self.emitter.prog.functions[func as usize].captures.len(), f);
348    }
349}
350
351/// If `entry` is an INLINE closure body, return its exclusive end. An inline closure is physically embedded
352/// mid-region: the op just before its entry is a forward `Jump` over the (Return-terminated) body — the
353/// compiler's jump-over — so its extent is `[entry, jump_target)`. Mirrors `codegen_riscv`'s `is_inline`.
354fn is_inline_closure(prog: &CompiledProgram, entry: usize) -> Option<usize> {
355    if entry >= 1 {
356        if let Op::Jump { target } = prog.code[entry - 1] {
357            if target > entry
358                && target <= prog.code.len()
359                && matches!(
360                    prog.code.get(target - 1),
361                    Some(Op::Return { .. }) | Some(Op::ReturnNothing)
362                )
363            {
364                return Some(target);
365            }
366        }
367    }
368    None
369}
370
371fn boundary_to_kind_c(ty: &Option<BoundaryType>) -> Kind {
372    match ty {
373        Some(BoundaryType::Int) => Kind::Int,
374        Some(BoundaryType::Word32) => Kind::Word(32),
375        Some(BoundaryType::Word64) => Kind::Word(64),
376        Some(BoundaryType::Bool) => Kind::Bool,
377        Some(BoundaryType::Float) => Kind::Float,
378        Some(BoundaryType::Text) => Kind::Text,
379        Some(BoundaryType::Builtin(name)) if name == "Rational" => Kind::Rational,
380        Some(BoundaryType::Seq(elem)) => match **elem {
381            BoundaryType::Int => Kind::SeqInt,
382            BoundaryType::Float => Kind::SeqFloat,
383            BoundaryType::Text => Kind::SeqText,
384            BoundaryType::Bool => Kind::SeqBool,
385            _ => Kind::SeqUnknown,
386        },
387        Some(BoundaryType::Set(elem)) => match **elem {
388            BoundaryType::Int => Kind::SetInt,
389            BoundaryType::Word32 => Kind::SetWord(32),
390            BoundaryType::Word64 => Kind::SetWord(64),
391            BoundaryType::Text => Kind::SetText,
392            _ => Kind::SetUnknown,
393        },
394        _ => Kind::Unknown,
395    }
396}
397
398/// The result kind of a binary arithmetic op: a `Float` operand promotes the other (so `Int + Float` is
399/// `Float`); two equal kinds pass through; an `Unknown` yields to a known partner; anything else is `Unknown`.
400fn join(a: Kind, b: Kind) -> Kind {
401    if a == b {
402        a
403    } else if a == Kind::Tower || b == Kind::Tower {
404        // A value of the extension holds every number it meets in the runtime's value model.
405        Kind::Tower
406    } else if a == Kind::Never {
407        b
408    } else if b == Kind::Never {
409        a
410    } else if a == Kind::Float || b == Kind::Float {
411        Kind::Float
412    } else if a == Kind::Rational || b == Kind::Rational {
413        // Exact rational promotes its integer partner (`Rational + Int` stays exact), mirroring Float promotion.
414        Kind::Rational
415    } else if a == Kind::BigInt || b == Kind::BigInt {
416        // A bignum operand keeps the result arbitrary-precision (`BigInt + Int` stays exact).
417        Kind::BigInt
418    } else if a == Kind::Unknown {
419        b
420    } else if b == Kind::Unknown {
421        a
422    } else {
423        Kind::Unknown
424    }
425}
426
427/// The kind of a result no larger than one of its operands (`bound`): the operands' join, except that an exact join
428/// is a machine integer unless that operand is exact.
429fn bounded_by(joined: Kind, bound: Kind) -> Kind {
430    if joined == Kind::BigInt && bound != Kind::BigInt {
431        Kind::Int
432    } else {
433        joined
434    }
435}
436
437/// The kind of register `reg` in a pre-op kind snapshot.
438fn kind_at(kinds: &[Kind], reg: u16) -> Kind {
439    *kinds.get(reg as usize).unwrap_or(&Kind::Unknown)
440}
441
442/// A register read as a `double` — a `Float` slot is bit-cast, an exact integer converted, any other numeric
443/// slot is a value cast.
444fn as_double(reg: u16, k: Kind) -> String {
445    match k {
446        Kind::Float => format!("as_f64(r[{reg}])"),
447        Kind::BigInt => format!("lint_to_f64(lc_xget(r[{reg}]))"),
448        _ => format!("(double)r[{reg}]"),
449    }
450}
451
452/// A register read as a `const char*` (a `Text` slot holds the pointer reinterpreted as an integer).
453fn as_cstr(reg: u16) -> String {
454    format!("(const char*)(intptr_t)r[{reg}]")
455}
456
457/// A register read as an `LDec` (by value, for the runtime's by-value `ldec_*` arithmetic): a `Rational` slot
458/// dereferences its boxed `LDec*`; an `Int` promotes losslessly via `ldec_from_i64`. Any other kind is refused.
459fn as_ldec(reg: u16, k: Kind) -> Result<String, String> {
460    match k {
461        Kind::Rational => Ok(format!("(*(LDec*)(intptr_t)r[{reg}])")),
462        Kind::Int => Ok(format!("ldec_from_i64(r[{reg}])")),
463        Kind::BigInt => Ok(format!("ldec_from_lint(lc_xget(r[{reg}]))")),
464        other => Err(format!(
465            "expected a Rational/Int operand for exact arithmetic, got {other:?}"
466        )),
467    }
468}
469
470/// A register read as an exact integer's tagged word (for the `lc_x*` operations): a `BigInt` slot holds one;
471/// a machine integer is tagged losslessly.
472fn as_exact(reg: u16, k: Kind) -> Result<String, String> {
473    match k {
474        Kind::BigInt => Ok(format!("r[{reg}]")),
475        Kind::Int | Kind::Bool => Ok(format!("lc_xof(r[{reg}])")),
476        other => Err(format!(
477            "expected a BigInt/Int operand for exact-integer arithmetic, got {other:?}"
478        )),
479    }
480}
481
482/// A C test that register `reg` (an exact divisor of kind `k`) holds zero.
483fn exact_zero_test(reg: u16, k: Kind) -> String {
484    match k {
485        Kind::Rational => format!("((LDec*)(intptr_t)r[{reg}])->a.len == 0"),
486        _ => format!("r[{reg}] == 0"),
487    }
488}
489
490/// [`sample_value`] for a divisor: a number of kind `k` that is not zero, so a division answers about the kinds.
491fn sample_divisor(k: Kind) -> Option<RuntimeValue> {
492    Some(match k {
493        Kind::Int | Kind::BigInt => RuntimeValue::Int(1),
494        Kind::Float => RuntimeValue::Float(1.0),
495        Kind::Word(bits) => RuntimeValue::Word(match bits {
496            8 => logicaffeine_base::WordVal::W8(logicaffeine_base::Word8(1)),
497            16 => logicaffeine_base::WordVal::W16(logicaffeine_base::Word16(1)),
498            32 => logicaffeine_base::WordVal::W32(logicaffeine_base::Word32(1)),
499            _ => logicaffeine_base::WordVal::W64(logicaffeine_base::Word64(1)),
500        }),
501        other => return sample_value(other),
502    })
503}
504
505/// A value of kind `k` the VM's own functions can be handed at generation time, to learn what the VM says
506/// about that kind (a refusal names the operand's type). `None` for a kind with no such value.
507fn sample_value(k: Kind) -> Option<RuntimeValue> {
508    use std::rc::Rc;
509    Some(match k {
510        Kind::Int | Kind::BigInt => RuntimeValue::Int(0),
511        Kind::Rational => RuntimeValue::from_rational(
512            logicaffeine_base::Rational::from_ratio_i64(1, 2).expect("a nonzero denominator"),
513        ),
514        Kind::Float => RuntimeValue::Float(0.0),
515        Kind::Bool => RuntimeValue::Bool(false),
516        Kind::Text => RuntimeValue::Text(Rc::new(String::new())),
517        Kind::SeqInt
518        | Kind::SeqFloat
519        | Kind::SeqText
520        | Kind::SeqBool
521        | Kind::SeqOf(_)
522        | Kind::SeqUnknown => RuntimeValue::List(Rc::new(std::cell::RefCell::new(
523            crate::interpreter::ListRepr::from_values(Vec::new()),
524        ))),
525        Kind::Tuple(_) => RuntimeValue::Tuple(Rc::new(Vec::new())),
526        Kind::Map(_) | Kind::MapUnknown => RuntimeValue::Map(Rc::new(std::cell::RefCell::new(
527            crate::interpreter::MapStorage::default(),
528        ))),
529        Kind::Peer => RuntimeValue::Peer(Rc::new(String::new())),
530        Kind::Word(bits) => RuntimeValue::Word(match bits {
531            8 => logicaffeine_base::WordVal::W8(logicaffeine_base::Word8(0)),
532            16 => logicaffeine_base::WordVal::W16(logicaffeine_base::Word16(0)),
533            32 => logicaffeine_base::WordVal::W32(logicaffeine_base::Word32(0)),
534            _ => logicaffeine_base::WordVal::W64(logicaffeine_base::Word64(0)),
535        }),
536        _ => return None,
537    })
538}
539
540/// A register coerced to a `const char*` for text concatenation — a `Text` is the pointer itself; any other
541/// scalar is formatted to a fresh string exactly as `Show` renders it (the VM coerces operands of `combined
542/// with` / interpolation to text this way).
543fn coerce_cstr(reg: u16, k: Kind) -> String {
544    match k {
545        Kind::Text => as_cstr(reg),
546        Kind::Bool => format!("(r[{reg}] ? \"true\" : \"false\")"),
547        Kind::Float => format!("logos_fmt_f64(as_f64(r[{reg}]))"),
548        Kind::Rational => format!("ldec_to_string((LDec*)(intptr_t)r[{reg}])"),
549        Kind::BigInt => format!("lint_to_string(lc_xget(r[{reg}]))"),
550        Kind::Tower => format!("ltower_to_string((LTower)(intptr_t)r[{reg}])"),
551        _ => format!("logos_dyn_sprintf(\"%\" PRId64, r[{reg}])"),
552    }
553}
554
555/// A register (holding an element of kind `elem`) read as the C value pushed into a typed `Seq_*`.
556fn as_elem_push(reg: u16, elem: Kind) -> String {
557    match elem {
558        Kind::Float => format!("as_f64(r[{reg}])"),
559        Kind::Text => format!("(char*)(intptr_t)r[{reg}]"),
560        Kind::Bool => format!("(bool)r[{reg}]"),
561        _ => format!("r[{reg}]"),
562    }
563}
564
565/// A C expression yielding an element of kind `elem`, wrapped back into an `int64_t` register value.
566fn elem_to_reg(expr: &str, elem: Kind) -> String {
567    match elem {
568        Kind::Float => format!("as_bits({expr})"),
569        Kind::Text => format!("(int64_t)(intptr_t)({expr})"),
570        _ => expr.to_string(),
571    }
572}
573
574/// Escape a Rust string as a C string-literal body.
575fn c_escape(s: &str) -> String {
576    let mut out = String::with_capacity(s.len() + 2);
577    for c in s.chars() {
578        match c {
579            '\\' => out.push_str("\\\\"),
580            '"' => out.push_str("\\\""),
581            '\n' => out.push_str("\\n"),
582            '\t' => out.push_str("\\t"),
583            '\r' => out.push_str("\\r"),
584            c if (c as u32) < 0x20 => out.push_str(&format!("\\{:03o}", c as u32)),
585            c => out.push(c),
586        }
587    }
588    out
589}
590
591/// The walker's own C helpers, emitted once after the runtime: the bit casts, the scalar-arithmetic helpers
592/// (integer power, floor division rounding toward −∞, the VM's exact `is approximately` tolerance), set
593/// copies, UTF-8 character walking, and every check that fails a run — each failing in the VM's own words,
594/// which [`words`] takes from the VM's functions at generation time.
595fn walker_preamble() -> String {
596    let utf8 = words::invalid_utf8();
597    WALKER_PREAMBLE
598        .replace("@FLOOR_DIVISION_BY_ZERO@", &words::floor_division_by_zero())
599        .replace(
600            "@REAL_FLOOR_DIVISION_BY_ZERO@",
601            &words::real_floor_division_by_zero(),
602        )
603        .replace("@EXACT_DIVISION_BY_ZERO@", &words::exact_division_by_zero())
604        .replace("@NEGATIVE_EXPONENT@", &words::negative_exponent())
605        .replace("@EXPONENT_TOO_LARGE@", &words::exponent_too_large())
606        .replace("@BIG_EXPONENT@", &words::big_exponent_refused())
607        .replace("@DIVISION_BY_ZERO@", &words::division_by_zero())
608        .replace("@MODULO_BY_ZERO@", &words::modulo_by_zero())
609        .replace("@UTF8_1@", &utf8[0])
610        .replace("@UTF8_2@", &utf8[1])
611        .replace("@UTF8_3@", &utf8[2])
612        .replace("@UTF8_INCOMPLETE@", &utf8[3])
613        .replace("@NOT_AN_INT@", &words::not_an_int())
614        .replace("@NOT_A_CHARACTER@", &words::not_a_character())
615        .replace("@CANNOT_READ_FILE@", &words::cannot_read_file())
616        .replace("@RANDOM_BOUNDS@", &words::random_bounds_refused())
617}
618
619const WALKER_PREAMBLE: &str = "\
620#include <errno.h>
621static inline double as_f64(int64_t b){ double d; __builtin_memcpy(&d,&b,8); return d; }
622static inline int64_t as_bits(double d){ int64_t b; __builtin_memcpy(&b,&d,8); return b; }
623static inline int64_t ipow64(int64_t b, int64_t e){ int64_t r=1; if(e<0) logos_fatalf(@NEGATIVE_EXPONENT@); while(e){ if(e&1) r*=b; e>>=1; if(e) b*=b; } return r; }
624static inline int64_t fldiv64(int64_t a, int64_t b){ if(b==0) logos_fatalf(@FLOOR_DIVISION_BY_ZERO@); if(b==-1) return (int64_t)(0-(uint64_t)a); int64_t q=a/b, r=a%b; if(r!=0 && ((r<0)!=(b<0))) q--; return q; }
625static inline double lc_rfldiv(double a, double b){ if(b==0.0) logos_fatalf(@REAL_FLOOR_DIVISION_BY_ZERO@); return floor(a/b); }
626static inline void lc_exact_divisor(int zero){ if(zero) logos_fatalf(@EXACT_DIVISION_BY_ZERO@); }
627static inline int lc_approx_eq(double a, double b){ if(a==b) return 1; double d=(a-b)<0?(b-a):(a-b); double x=a<0?-a:a, y=b<0?-b:b, m=x>y?x:y, t=1e-9*m; if(t<1e-12) t=1e-12; return d<=t; }
628/* Each enum cell buffer is a traced object; its children live exactly as long as a reachable parent. */
629__attribute__((unused)) static int64_t *lc_enum_alloc(size_t n){
630    if(n > SIZE_MAX / sizeof(int64_t)) logos_fatal(\"memory allocation failed\");
631    return (int64_t*)lc_gc_alloc(n * sizeof(int64_t));
632}
633static Set_i64 lc_set_i64_copy(Set_i64 *s){ Set_i64 r=set_i64_new(); for(size_t oi=0;oi<s->ord_len;oi++){ size_t si=s->order[oi]; if(s->state[si]) set_i64_add(&r,s->keys[si]); } return r; }
634static Set_str lc_set_str_copy(Set_str *s){ Set_str r=set_str_new(); for(size_t oi=0;oi<s->ord_len;oi++){ size_t si=s->order[oi]; if(s->state[si]) set_str_add(&r,s->keys[si]); } return r; }
635__attribute__((unused)) static Map_i64_i64 lc_map_i64_i64_copy(Map_i64_i64 *m){ Map_i64_i64 r=map_i64_i64_new(); for(size_t oi=0;oi<m->ord_len;oi++){ size_t si=m->order[oi]; if(m->state[si]) map_i64_i64_set(&r,m->keys[si],m->vals[si]); } return r; }
636__attribute__((unused)) static Map_i64_str lc_map_i64_str_copy(Map_i64_str *m){ Map_i64_str r=map_i64_str_new(); for(size_t oi=0;oi<m->ord_len;oi++){ size_t si=m->order[oi]; if(m->state[si]) map_i64_str_set(&r,m->keys[si],m->vals[si]); } return r; }
637__attribute__((unused)) static Map_str_i64 lc_map_str_i64_copy(Map_str_i64 *m){ Map_str_i64 r=map_str_i64_new(); for(size_t oi=0;oi<m->ord_len;oi++){ size_t si=m->order[oi]; if(m->state[si]) map_str_i64_set(&r,m->keys[si],m->vals[si]); } return r; }
638__attribute__((unused)) static Map_str_str lc_map_str_str_copy(Map_str_str *m){ Map_str_str r=map_str_str_new(); for(size_t oi=0;oi<m->ord_len;oi++){ size_t si=m->order[oi]; if(m->state[si]) map_str_str_set(&r,m->keys[si],m->vals[si]); } return r; }
639/* A list of integers as the list of the rationals they equal, each item a boxed `LDec`. */
640__attribute__((unused)) static Seq_i64 *lc_seq_i64_as_ldec(Seq_i64 *s){
641    Seq_i64 *r=(Seq_i64*)lc_gc_alloc(sizeof(Seq_i64)); *r=seq_i64_new();
642    for(size_t i=0;i<s->len;i++){ LDec *d=(LDec*)lc_gc_alloc(sizeof(LDec)); *d=ldec_from_i64(s->data[i]); seq_i64_push(r,(int64_t)(intptr_t)d); }
643    return r;
644}
645/* The byte length of the UTF-8 character that starts with `lead`. */
646static inline size_t lc_utf8_len(unsigned char lead){ return lead<0x80?1:lead<0xE0?2:lead<0xF0?3:4; }
647/* Every ASCII character as its own one-character Text, so reading one out of a text allocates nothing. */
648static char lc_ascii_texts[128][2];
649static inline char *lc_ascii_text(unsigned char c){ lc_ascii_texts[c][0]=(char)c; lc_ascii_texts[c][1]=0; return lc_ascii_texts[c]; }
650/* `item i of text`: the i-th character (not byte), as its own Text. */
651static char *lc_text_item(const char *s, int64_t i){
652    size_t chars=0; for(const unsigned char *p=(const unsigned char*)s; *p; p++) if((*p&0xC0)!=0x80) chars++;
653    size_t k=logos_idx0(i, chars), at=0;
654    const unsigned char *p=(const unsigned char*)s;
655    while(at<k){ p+=lc_utf8_len(*p); at++; }
656    if(*p<0x80) return lc_ascii_text(*p);
657    size_t n=lc_utf8_len(*p); char *c=(char*)lc_gc_alloc_atomic(n+1); memcpy(c,p,n); c[n]=0; return c;
658}
659__attribute__((noreturn)) static void lc_utf8_fail(int error_len, size_t at){
660    switch(error_len){
661    case 1: logos_fatalf(@UTF8_1@, (int64_t)at);
662    case 2: logos_fatalf(@UTF8_2@, (int64_t)at);
663    case 3: logos_fatalf(@UTF8_3@, (int64_t)at);
664    default: logos_fatalf(@UTF8_INCOMPLETE@, (int64_t)at);
665    }
666}
667/* `text_from_bytes(bytes)`: each Int's low byte, validated as UTF-8 exactly as Rust's `str::from_utf8` validates
668   it, so bytes that are not UTF-8 fail at the same offset with the same sentence. */
669static char *lc_text_from_bytes(Seq_i64 *b){
670    size_t n=b->len; unsigned char *v=(unsigned char*)lc_gc_alloc_atomic(n+1);
671    for(size_t k=0;k<n;k++) v[k]=(unsigned char)(b->data[k]&0xff);
672    v[n]=0;
673    size_t i=0;
674    while(i<n){
675        size_t at=i; unsigned char c=v[i];
676        if(c<0x80){ i++; continue; }
677        if(c>=0xC2 && c<=0xDF){
678            if(++i>=n) lc_utf8_fail(0,at);
679            if((v[i]&0xC0)!=0x80) lc_utf8_fail(1,at);
680        } else if(c>=0xE0 && c<=0xEF){
681            if(++i>=n) lc_utf8_fail(0,at);
682            unsigned char d=v[i];
683            int ok=(c==0xE0)?(d>=0xA0&&d<=0xBF):(c==0xED)?(d>=0x80&&d<=0x9F):(d>=0x80&&d<=0xBF);
684            if(!ok) lc_utf8_fail(1,at);
685            if(++i>=n) lc_utf8_fail(0,at);
686            if((v[i]&0xC0)!=0x80) lc_utf8_fail(2,at);
687        } else if(c>=0xF0 && c<=0xF4){
688            if(++i>=n) lc_utf8_fail(0,at);
689            unsigned char d=v[i];
690            int ok=(c==0xF0)?(d>=0x90&&d<=0xBF):(c==0xF4)?(d>=0x80&&d<=0x8F):(d>=0x80&&d<=0xBF);
691            if(!ok) lc_utf8_fail(1,at);
692            if(++i>=n) lc_utf8_fail(0,at);
693            if((v[i]&0xC0)!=0x80) lc_utf8_fail(2,at);
694            if(++i>=n) lc_utf8_fail(0,at);
695            if((v[i]&0xC0)!=0x80) lc_utf8_fail(3,at);
696        } else lc_utf8_fail(1,at);
697        i++;
698    }
699    return (char*)v;
700}
701/* An exact integer rides its register as a tagged word: even, the value shifted left once (any integer within 63
702   bits); odd, a boxed `LInt*` with its low bit set. Each operation stays on the word while its result fits one, and
703   goes through the `lint_*` tower when it does not. */
704#define LC_XLIM ((int64_t)1 << 62)
705static inline LInt lc_xget(int64_t w){ return (w & 1) ? *(LInt*)(intptr_t)(w ^ 1) : lint_small(w >> 1); }
706static inline int64_t lc_xput(LInt v){
707    if(!v.big && v.small >= -LC_XLIM && v.small < LC_XLIM) return (int64_t)((uint64_t)v.small << 1);
708    LInt *b=(LInt*)lc_gc_alloc(sizeof(LInt)); *b=v; return (int64_t)(intptr_t)b | 1;
709}
710static inline int64_t lc_xof(int64_t v){ return (v >= -LC_XLIM && v < LC_XLIM) ? (int64_t)((uint64_t)v << 1) : lc_xput(lint_small(v)); }
711/* The exact integer as a machine one, where only a machine integer can go. */
712__attribute__((unused)) static inline int64_t lc_xnarrow(int64_t w){ return (w & 1) ? lint_expect_i64(*(LInt*)(intptr_t)(w ^ 1)) : (w >> 1); }
713/* The low 64 bits of an exact integer, reduced modulo 2^64 whatever its width and sign: what a word built from it holds. */
714__attribute__((unused)) static inline uint64_t lc_xword_bits(int64_t w){ return (w & 1) ? lint_word_bits(*(LInt*)(intptr_t)(w ^ 1)) : (uint64_t)(w >> 1); }
715/* The exact integer as an index, refused past a machine word with the VM's sentence for that index (`why`). */
716__attribute__((unused)) static inline int64_t lc_xindex(int64_t w, const char *why){ return (w & 1) ? lint_index_i64(*(LInt*)(intptr_t)(w ^ 1), why) : (w >> 1); }
717/* Whether the exact integer is within i64 — what the VM holds as an `Int` — and its value when it is. */
718__attribute__((unused)) static int lc_xi64(int64_t w, int64_t *v){
719    if(!(w & 1)){ *v=w >> 1; return 1; }
720    LInt x=*(LInt*)(intptr_t)(w ^ 1);
721    if(!x.big){ *v=x.small; return 1; }
722    LBig lo=lbig_from_i64(INT64_MIN), hi=lbig_from_i64(INT64_MAX);
723    int in=lbig_cmp(x.big, &lo) >= 0 && lbig_cmp(x.big, &hi) <= 0; lbig_free(&lo); lbig_free(&hi);
724    if(in) *v=lint_expect_i64(x);
725    return in;
726}
727__attribute__((unused)) static inline int64_t lc_xadd(int64_t a, int64_t b){ int64_t s; if(!((a|b)&1) && !__builtin_add_overflow(a,b,&s)) return s; return lc_xput(lint_add(lc_xget(a), lc_xget(b))); }
728__attribute__((unused)) static inline int64_t lc_xsub(int64_t a, int64_t b){ int64_t s; if(!((a|b)&1) && !__builtin_sub_overflow(a,b,&s)) return s; return lc_xput(lint_sub(lc_xget(a), lc_xget(b))); }
729__attribute__((unused)) static inline int64_t lc_xmul(int64_t a, int64_t b){ int64_t s; if(!((a|b)&1) && !__builtin_mul_overflow(a>>1,b,&s)) return s; return lc_xput(lint_mul(lc_xget(a), lc_xget(b))); }
730__attribute__((unused)) static inline int lc_xcmp(int64_t a, int64_t b){ if(!((a|b)&1)) return (a>b)-(a<b); return lint_cmp(lc_xget(a), lc_xget(b)); }
731__attribute__((unused)) static int64_t lc_xdiv(int64_t a, int64_t b){
732    if(b==0) logos_fatalf(@DIVISION_BY_ZERO@);
733    if(!((a|b)&1)) return lc_xof((a>>1)/(b>>1));
734    return lc_xput(lint_div(lc_xget(a), lc_xget(b)));
735}
736__attribute__((unused)) static int64_t lc_xmod(int64_t a, int64_t b){
737    if(b==0) logos_fatalf(@MODULO_BY_ZERO@);
738    if(!((a|b)&1)) return (int64_t)((uint64_t)((a>>1)%(b>>1)) << 1);
739    return lc_xput(lint_mod(lc_xget(a), lc_xget(b)));
740}
741__attribute__((unused)) static int64_t lc_xfloordiv(int64_t a, int64_t b){
742    if(b==0) logos_fatalf(@FLOOR_DIVISION_BY_ZERO@);
743    if(!((a|b)&1)){ int64_t x=a>>1, y=b>>1, q=x/y, m=x%y; if(m!=0 && ((m<0)!=(y<0))) q--; return lc_xof(q); }
744    LInt x=lc_xget(a), y=lc_xget(b), q=lint_div(x, y), m=lint_mod(x, y);
745    int ms=lint_cmp(m, lint_small(0)), ys=lint_cmp(y, lint_small(0));
746    if(ms!=0 && ((ms<0)!=(ys<0))){
747        LInt previous=q;
748        q=lint_sub(q, lint_small(1));
749        if(previous.big){ lbig_free(previous.big); lc_gc_free(previous.big); }
750    }
751    if(m.big){ lbig_free(m.big); lc_gc_free(m.big); }
752    return lc_xput(q);
753}
754/* `b ** e` on machine words: 1 when the power outgrows the word, else 0 with the power in `*out`. An exponent the VM
755   refuses fails the run in its words. */
756__attribute__((unused)) static inline int lc_ipow_ovf(int64_t b, int64_t e, int64_t *out){
757    if(e<0) logos_fatalf(@NEGATIVE_EXPONENT@);
758    if(e>(int64_t)UINT32_MAX) logos_fatalf(@EXPONENT_TOO_LARGE@);
759    int64_t p=1; uint64_t k=(uint64_t)e;
760    while(k){
761        if((k&1) && __builtin_mul_overflow(p, b, &p)) return 1;
762        k>>=1;
763        if(k && __builtin_mul_overflow(b, b, &b)) return 1;
764    }
765    *out=p; return 0;
766}
767/* `a ** e`: a machine base stays on the word while the power fits one. */
768__attribute__((unused)) static int64_t lc_xpow(int64_t a, int64_t e){
769    if(a & 1){
770        if(e<0 || e>(int64_t)UINT32_MAX) logos_fatalf(@BIG_EXPONENT@);
771    } else {
772        if(e<0) logos_fatalf(@NEGATIVE_EXPONENT@);
773        if(e>(int64_t)UINT32_MAX) logos_fatalf(@EXPONENT_TOO_LARGE@);
774        int64_t p=1, b=a>>1; uint64_t k=(uint64_t)e; int fits=1;
775        while(k){
776            if((k&1) && __builtin_mul_overflow(p, b, &p)){ fits=0; break; }
777            k>>=1;
778            if(k && __builtin_mul_overflow(b, b, &b)){ fits=0; break; }
779        }
780        if(fits) return lc_xof(p);
781    }
782    LBig b=lint_as_big(lc_xget(a)), p=lbig_pow(&b, (uint64_t)e); lbig_free(&b); return lc_xput(lint_norm(p));
783}
784/* The bitwise operations over exact integers: the machine's on two words, the runtime's `lint_*` past them. */
785__attribute__((unused)) static int64_t lc_xbitand(int64_t a, int64_t b){ if(!((a|b)&1)) return (a>>1) & (b>>1); return lint_bitand(lc_xget(a), lc_xget(b)); }
786__attribute__((unused)) static int64_t lc_xbitor(int64_t a, int64_t b){ if(!((a|b)&1)) return (a>>1) | (b>>1); return lint_bitor(lc_xget(a), lc_xget(b)); }
787__attribute__((unused)) static int64_t lc_xbitxor(int64_t a, int64_t b){ if(!((a|b)&1)) return (a>>1) ^ (b>>1); return lint_bitxor(lc_xget(a), lc_xget(b)); }
788__attribute__((unused)) static int64_t lc_xshl(int64_t a, int64_t b){ if(!((a|b)&1)) return (int64_t)((uint64_t)(a>>1) << ((uint32_t)(b>>1) & 63u)); return lint_shl(lc_xget(a), lc_xget(b)); }
789__attribute__((unused)) static int64_t lc_xshr(int64_t a, int64_t b){ if(!((a|b)&1)) return (a>>1) >> ((uint32_t)(b>>1) & 63u); return lint_shr(lc_xget(a), lc_xget(b)); }
790/* `parseInt(text)`: the text trimmed as Rust trims it, read as Rust reads an i64 — an optional sign, then only
791   digits, within range — or the VM's failure naming the text as given. */
792__attribute__((unused)) static int64_t lc_parse_int(const char *s){
793    char *t=logos_trim(s); const char *p=t; int neg=0;
794    if(*p=='+'||*p=='-'){ neg=(*p=='-'); p++; }
795    if(!*p) logos_fatalf(@NOT_AN_INT@, s);
796    uint64_t v=0, limit=neg?(uint64_t)INT64_MAX+1u:(uint64_t)INT64_MAX;
797    for(; *p; p++){
798        if(*p<'0'||*p>'9') logos_fatalf(@NOT_AN_INT@, s);
799        uint64_t d=(uint64_t)(*p-'0');
800        if(v>(limit-d)/10) logos_fatalf(@NOT_AN_INT@, s);
801        v=v*10+d;
802    }
803    return neg?(int64_t)(0-v):(int64_t)v;
804}
805/* A text a register grows by appending: the characters are preceded by this head, which keeps what a plain C
806   string would have to rescan for — its byte length, its capacity, its character count, whether it is ASCII.
807   Only a register whose owned flag is set holds one (it built it, and nothing else holds it), so only such a
808   register ever reads the head or grows the text in place. */
809typedef struct { size_t len, cap, chars; int ascii; } lc_text_head;
810static inline lc_text_head *lc_head(int64_t text){ return ((lc_text_head*)(intptr_t)text) - 1; }
811/* `text + tail`, appended in place when the register owns its text (`owned`), otherwise into a fresh growable
812   text the register will own — amortized linear, however long the text grows. */
813static int64_t lc_text_append(int64_t text, int owned, const char *tail){
814    size_t tl=strlen(tail), tc=0; int ta=1;
815    for(const unsigned char *p=(const unsigned char*)tail; *p; p++){ if((*p&0xC0)!=0x80) tc++; if(*p>=0x80) ta=0; }
816    lc_text_head *h;
817    if(owned){ h=lc_head(text); }
818    else {
819        const char *s=(const char*)(intptr_t)text; size_t sl=strlen(s), sc=0; int sa=1;
820        for(const unsigned char *p=(const unsigned char*)s; *p; p++){ if((*p&0xC0)!=0x80) sc++; if(*p>=0x80) sa=0; }
821        size_t cap=(sl+tl)*2+16;
822        h=(lc_text_head*)lc_gc_alloc_atomic(sizeof(lc_text_head)+cap+1);
823        h->len=sl; h->cap=cap; h->chars=sc; h->ascii=sa;
824        memcpy((char*)(h+1), s, sl+1);
825    }
826    if(h->len+tl>h->cap){ size_t cap=(h->len+tl)*2; h=(lc_text_head*)lc_gc_realloc_atomic(h, sizeof(lc_text_head)+cap+1); h->cap=cap; }
827    memcpy((char*)(h+1)+h->len, tail, tl+1);
828    h->len+=tl; h->chars+=tc; h->ascii=h->ascii&&ta;
829    return (int64_t)(intptr_t)(char*)(h+1);
830}
831/* Append an Int without allocating through logos_dyn_sprintf. The unsigned
832   subtraction preserves INT64_MIN's magnitude without signed overflow. */
833static int64_t lc_text_append_i64(int64_t text, int owned, int64_t value){
834    char digits[21]; size_t at=sizeof digits;
835    uint64_t magnitude=value<0 ? (uint64_t)0-(uint64_t)value : (uint64_t)value;
836    do {
837        digits[--at]=(char)('0'+magnitude%10);
838        magnitude/=10;
839    } while(magnitude);
840    if(value<0) digits[--at]='-';
841    lc_text_head *h;
842    if(owned) h=lc_head(text);
843    else {
844        const char *s=(const char*)(intptr_t)text; size_t sl=strlen(s), sc=0; int sa=1;
845        for(const unsigned char *p=(const unsigned char*)s; *p; p++){
846            if((*p&0xC0)!=0x80) sc++;
847            if(*p>=0x80) sa=0;
848        }
849        size_t cap=(sl+sizeof digits-at)*2+16;
850        h=(lc_text_head*)lc_gc_alloc_atomic(sizeof(lc_text_head)+cap+1);
851        h->len=sl; h->cap=cap; h->chars=sc; h->ascii=sa;
852        memcpy((char*)(h+1), s, sl+1);
853    }
854    size_t len=sizeof digits-at;
855    if(h->len+len>h->cap){
856        size_t cap=(h->len+len)*2;
857        h=(lc_text_head*)lc_gc_realloc_atomic(h, sizeof(lc_text_head)+cap+1);
858        h->cap=cap;
859    }
860    memcpy((char*)(h+1)+h->len, digits+at, len);
861    h->len+=len; h->chars+=len; ((char*)(h+1))[h->len]=0;
862    return (int64_t)(intptr_t)(char*)(h+1);
863}
864/* `length of text` — from the head when the register owns its growing text. */
865static inline int64_t lc_text_len(int64_t text, int owned){ return owned ? (int64_t)lc_head(text)->len : (int64_t)strlen((const char*)(intptr_t)text); }
866/* `item i of text` — in constant time when the register owns an ASCII growing text. */
867static inline char *lc_text_item_of(int64_t text, int owned, int64_t i){
868    if(owned){ lc_text_head *h=lc_head(text); if(h->ascii && i>=1 && (uint64_t)i<=(uint64_t)h->len) return lc_ascii_text((unsigned char)((const char*)(intptr_t)text)[i-1]); }
869    return lc_text_item((const char*)(intptr_t)text, i);
870}
871/* `chr(code)`: the character with that Unicode scalar value, or the VM's failure for a code that names none
872   (a surrogate, or past U+10FFFF — judged on the low 32 bits, as `char::from_u32(code as u32)` judges it). */
873__attribute__((unused)) static char *lc_chr(int64_t code){
874    uint32_t c=(uint32_t)code;
875    if(c>0x10FFFF || (c>=0xD800 && c<=0xDFFF)) logos_fatalf(@NOT_A_CHARACTER@, code);
876    return logos_chr(code);
877}
878/* `tryReadBytes(path)`: the file's bytes from the real file system, `Ok` (tag `ok`) with a Seq of Int, or
879   `Err` (tag `err`) with the facade's sentence for why it could not be read. */
880__attribute__((unused)) static int64_t lc_try_read_bytes(const char *path, int64_t ok, int64_t err){
881    int64_t *res=(int64_t*)lc_gc_calloc(2, sizeof(int64_t));
882    FILE *f=fopen(path, \"rb\");
883    Seq_i64 *bytes=(Seq_i64*)lc_gc_alloc(sizeof(Seq_i64)); *bytes=seq_i64_new();
884    int fail=0;
885    if(!f) fail=errno;
886    else {
887        unsigned char buf[4096]; size_t n;
888        while((n=fread(buf, 1, sizeof buf, f))>0) for(size_t i=0;i<n;i++) seq_i64_push(bytes, (int64_t)buf[i]);
889        if(ferror(f)) fail=errno;
890        fclose(f);
891    }
892    if(fail){ res[0]=err; res[1]=(int64_t)(intptr_t)logos_dyn_sprintf(@CANNOT_READ_FILE@, path, strerror(fail), fail); }
893    else { res[0]=ok; res[1]=(int64_t)(intptr_t)bytes; }
894    return (int64_t)(intptr_t)res;
895}
896/* The text of the environment variable `name`, `\"\"` when it is unset: with whether it is set, the two
897   halves the VM expands `get(name)` into. */
898__attribute__((unused)) static char *lc_env_value(const char *name){
899    const char *v=getenv(name);
900    return lc_gc_strdup(v ? v : \"\");
901}
902/* The program's argument vector, as `main` received it. */
903static int lc_argc; static char **lc_argv;
904/* A growing string, for showing a value made of other values. */
905typedef struct { char *s; size_t len, cap; } lc_sb;
906__attribute__((unused)) static void lc_sb_add(lc_sb *b, const char *t){
907    size_t n=strlen(t);
908    if(b->len+n+1>b->cap){ size_t c=b->cap?b->cap*2:64; while(c<b->len+n+1) c*=2; b->s=(char*)lc_gc_realloc_atomic(b->s,c); b->cap=c; }
909    memcpy(b->s+b->len,t,n+1); b->len+=n;
910}
911";
912
913/// An in-flight `for-each` iterator frame (innermost last on the emitter's `iter_stack`). `IterNext`/`IterPop`
914/// carry no iterable — they operate on the top frame, exactly as the VM's iterator stack does.
915struct IterFrame {
916    /// The sequence kind (`SeqInt`/…), so `IterNext` casts to the right `Seq_*` and yields the right element;
917    /// `Text` for a walk over a text's characters.
918    seq_kind: Kind,
919    /// This frame walks a borrowed UTF-8 byte view rather than a boxed Seq.
920    text_bytes: bool,
921    /// The C ids of the loop's snapshot of the iterated handle (`_itv{id}`) and its position (`_it{id}`).
922    id: usize,
923}
924
925/// Lower a whole `CompiledProgram` to a self-contained C translation unit. Returns a typed refusal (`Err`) for
926/// any op/const/kind not yet lowered — the caller falls back to the AST emitter.
927pub fn compile_program_to_c(prog: &CompiledProgram) -> Result<String, String> {
928    compile_program_to_c_with_policy(prog, None)
929}
930
931/// The Op-walker with an optional policy context — the `(interner, PolicyRegistry)` a `CheckPolicy` op needs
932/// to resolve its predicate/capability name into a condition (the bytecode carries only the name, exactly as
933/// the VM consults its `policy_ctx`). A program with no `Check` statement passes `None` and emits byte-identical
934/// C to before. This is the seam `compile_to_c_via_bytecode` uses so runtime authorization lowers natively.
935pub fn compile_program_to_c_with_policy<'a>(
936    prog: &'a CompiledProgram,
937    policy_ctx: Option<(&'a Interner, &'a PolicyRegistry)>,
938) -> Result<String, String> {
939    compile_program_to_c_with_offloads(prog, policy_ctx, &[])
940}
941
942/// A function whose calls a device may answer: its index in the program, the device region that answers it, and how
943/// each parameter and the result cross the boundary.
944#[derive(Debug, Clone, PartialEq, Eq)]
945pub struct Offload {
946    pub func: usize,
947    pub region: u32,
948    pub params: Vec<SlotKind>,
949    pub ret: SlotKind,
950}
951
952/// The walker's C for a program with an offload table, and each offloaded function it keeps on the host with the
953/// reason.
954#[derive(Debug, Clone, PartialEq, Eq)]
955pub struct OffloadedC {
956    pub c: String,
957    pub kept: Vec<(usize, String)>,
958}
959
960/// The Op-walker with an offload table ([`compile_program_to_c_offloaded`]); an empty table is
961/// [`compile_program_to_c_with_policy`].
962pub fn compile_program_to_c_with_offloads<'a>(
963    prog: &'a CompiledProgram,
964    policy_ctx: Option<(&'a Interner, &'a PolicyRegistry)>,
965    offloads: &[Offload],
966) -> Result<String, String> {
967    compile_program_to_c_offloaded(prog, policy_ctx, offloads).map(|o| o.c)
968}
969
970/// The Op-walker with an offload table. Each function the table names asks its device first, at its entry, so every
971/// caller's call is offered: `logos_dev_call(region, args, &out)` with the parameters in their ports, whose DONE answer
972/// is the call's result and whose DECLINED answer runs the body the walker emits for it anyway. A function a port
973/// cannot serve — a parameter or result whose settled kind the port does not carry — stays on the host, reported in
974/// `kept`. The driver's definition is the caller's to append.
975pub fn compile_program_to_c_offloaded<'a>(
976    prog: &'a CompiledProgram,
977    policy_ctx: Option<(&'a Interner, &'a PolicyRegistry)>,
978    offloads: &[Offload],
979) -> Result<OffloadedC, String> {
980    // Every function a `MakeClosure` builds is a closure body (hidden closure-ptr param + capture prologue).
981    let closure_bodies: std::collections::BTreeSet<usize> = prog
982        .code
983        .iter()
984        .filter_map(|op| match op {
985            Op::MakeClosure { func, .. } => Some(*func as usize),
986            _ => None,
987        })
988        .collect();
989    let mut em = Emitter {
990        prog,
991        fn_ret: Vec::new(),
992        global_kinds: Vec::new(),
993        out: String::new(),
994        iter_stack: Vec::new(),
995        tmp: 0,
996        tuple_types: std::cell::RefCell::new(Vec::new()),
997        map_types: std::cell::RefCell::new(Vec::new()),
998        opt_types: std::cell::RefCell::new(Vec::new()),
999        inductive_types: std::cell::RefCell::new(Vec::new()),
1000        seq_elems: std::cell::RefCell::new(Vec::new()),
1001        param_kinds: Vec::new(),
1002        current_fn: None,
1003        net_message: std::cell::Cell::new(Kind::Unknown),
1004        net_stream: std::cell::Cell::new(Kind::Unknown),
1005        ctor_tags: ctor_tags(prog),
1006        exact_int: std::cell::Cell::new(false),
1007        closure_bodies,
1008        counter_fields: std::collections::HashSet::new(),
1009        synth_structs: std::cell::RefCell::new(Vec::new()),
1010        policy_ctx,
1011        owned: std::collections::BTreeSet::new(),
1012        handle_kinds: std::collections::BTreeMap::new(),
1013        region_span: (0, 0),
1014        region_targets: std::collections::HashSet::new(),
1015        live_out: Vec::new(),
1016        exact_defs: std::collections::HashSet::new(),
1017        speculative_defs: std::collections::HashSet::new(),
1018        exact_outs: std::collections::HashSet::new(),
1019        entry_speculative: std::collections::HashMap::new(),
1020        on_words: std::cell::Cell::new(false),
1021        promoted: std::cell::RefCell::new([
1022            std::collections::HashSet::new(),
1023            std::collections::HashSet::new(),
1024        ]),
1025        text_byte_calls: std::collections::HashSet::new(),
1026        text_byte_iters: std::collections::HashSet::new(),
1027        label: 'L',
1028        deopt: std::collections::BTreeSet::new(),
1029        arrivals: std::collections::BTreeSet::new(),
1030        word_twins: std::collections::HashSet::new(),
1031        word_entry: false,
1032        twin_arrival: false,
1033        held_body: String::new(),
1034        held_owns: false,
1035        held_entries: Vec::new(),
1036        held_start: None,
1037        offloads: offloads.to_vec(),
1038        kept: Vec::new(),
1039        tower: false,
1040        tower_from: std::cell::RefCell::new([
1041            std::collections::HashMap::new(),
1042            std::collections::HashMap::new(),
1043        ]),
1044        tower_entries: std::cell::RefCell::new(std::collections::HashMap::new()),
1045        number_from: std::cell::RefCell::new([
1046            std::collections::HashMap::new(),
1047            std::collections::HashMap::new(),
1048        ]),
1049        number_entries: std::cell::RefCell::new(std::collections::HashMap::new()),
1050    };
1051    let fn_ret: Vec<Kind> = prog
1052        .functions
1053        .iter()
1054        .map(|f| em.boundary_kind(&f.return_type))
1055        .collect();
1056    em.fn_ret = fn_ret;
1057    em.compile()?;
1058    Ok(OffloadedC {
1059        c: em.out,
1060        kept: em.kept,
1061    })
1062}
1063
1064struct Emitter<'a> {
1065    prog: &'a CompiledProgram,
1066    fn_ret: Vec<Kind>,
1067    /// The inferred kind of each module-level global (indexed like `prog.globals`), derived from its
1068    /// `GlobalSet` sites so a `GlobalGet` yields the right register kind.
1069    global_kinds: Vec<Kind>,
1070    out: String,
1071    /// Active `for-each` iterators (innermost last), reset per region.
1072    iter_stack: Vec<IterFrame>,
1073    /// Monotonic fresh-temp counter (unique C locals across the whole unit).
1074    tmp: usize,
1075    /// Interning table for anonymous tuple layouts: each entry is a tuple's per-position kinds, and a
1076    /// `Kind::Tuple(idx)` points here. Interior-mutable because it is filled during the `&self` `infer_kinds`
1077    /// fixpoint (as `NewTuple` ops are seen) and read back in `lower_op`.
1078    tuple_types: std::cell::RefCell<Vec<Vec<Kind>>>,
1079    /// Interning table for map layouts: each entry is a map's (key kind, value kind), and a `Kind::Map(idx)`
1080    /// points here — the value may be ANY kind (it rides the i64/str cell), so the pair is remembered so a
1081    /// later `Index`/`Show` recovers the true value kind.
1082    map_types: std::cell::RefCell<Vec<(Kind, Kind)>>,
1083    /// Interning table for `Optional` inner element kinds: a `Kind::Optional(idx)` points here so `Show` renders
1084    /// a present payload by the right scalar kind. Interior-mutable (filled during the `&self` infer fixpoint).
1085    opt_types: std::cell::RefCell<Vec<Kind>>,
1086    /// Interning table for the enums the program never declares (`Kind::Inductive`): the type's name and, per
1087    /// constructor in tag order, the payload kinds known for it — `None` for a constructor the value can never
1088    /// be (a `none` is never a `Some`). Interior-mutable (filled during the infer fixpoint).
1089    inductive_types: std::cell::RefCell<Vec<(String, Vec<(String, Option<Vec<Kind>>)>)>>,
1090    /// Interning table for the element kinds of `Kind::SeqOf` sequences.
1091    seq_elems: std::cell::RefCell<Vec<Kind>>,
1092    /// Every function's parameter kinds, settled over the whole program ([`Emitter::refine_signatures`]).
1093    param_kinds: Vec<Vec<Kind>>,
1094    /// The function whose body is being emitted (`None` in Main).
1095    current_fn: Option<usize>,
1096    /// The kind of every message the program `Send`s, and of every sequence it `Stream`s — one offline node,
1097    /// so an `Await` receives exactly what the program sent ([`Emitter::refine_signatures`]).
1098    net_message: std::cell::Cell<Kind>,
1099    net_stream: std::cell::Cell<Kind>,
1100    /// Per undeclared enum type, its constructors in tag order — one program-wide order, so every site that
1101    /// builds, tests or binds a constructor agrees on its tag. `Option` is `Some, None` and `Result` is `Ok, Err`;
1102    /// any other type lists its constructors as the program first builds them.
1103    ctor_tags: std::collections::HashMap<String, Vec<String>>,
1104    /// Set while emitting/inferring a CLOSURE BODY: its integer `Add`/`Sub`/`Mul`/`Pow` are exact
1105    /// unconditionally (result → `BigInt`). Bignum promotion does NOT flow through the closure boundary in
1106    /// the shared `bigint_promote`/`int_exceeds_i64` analyses (the Rust backend PANICS, AST-C wraps), so an
1107    /// unbounded closure param that overflows would wrap; being exact-by-default inside the rare, perf-insensitive
1108    /// closure body closes that hole with no perf cost to the hot non-closure paths. Interior-mutable Cell because
1109    /// `infer_kinds` runs under `&self`.
1110    exact_int: std::cell::Cell<bool>,
1111    /// Function indices that are CLOSURE BODIES (referenced by a `MakeClosure`). Each is emitted with a hidden
1112    /// trailing `int64_t clo` param + a prologue that loads its captured values (and present flags) from the
1113    /// closure object, and every `CallValue` dispatches into this set by the closure's runtime body index.
1114    closure_bodies: std::collections::BTreeSet<usize>,
1115    /// `(struct name, cell index)` of every field that holds a LIVE counter CRDT — a `NewCrdt` kind-4/5 value
1116    /// flowed into it via a `StructInsert`. The declared field type is `Int`, so this side-set is how
1117    /// `GetField`/`CrdtMerge` know to SUM / per-replica-MAX rather than treat the cell as a plain integer. Keyed
1118    /// by struct NAME (unique) so a synthesized-layout struct and a `prog.struct_types` struct share one key
1119    /// space. Mirrors `codegen_riscv`'s `counter_fields`. Filled by `derive_counter_fields` in `compile()`.
1120    counter_fields: std::collections::HashSet<(String, usize)>,
1121    /// Interning table for SYNTHESIZED struct layouts (a `Kind::StructSynth(idx)` points here): each entry is
1122    /// `(struct name, [(field name, field kind) in cell order])` for a struct whose declared type did not resolve
1123    /// into `prog.struct_types`. Filled during the `&self` `infer_kinds`/`derive` passes as `NewStruct` on an
1124    /// unresolved type name is seen: field names grow as field ops on the struct are encountered, and each field
1125    /// kind is monotonically refined from the value flowed into it (`StructInsert` / `CrdtBump` → the cell's kind),
1126    /// so a later `GetField`/`Show` renders it correctly (an unresolved `Tally.n` bumped from `Nothing` is `Int`).
1127    synth_structs: std::cell::RefCell<Vec<(String, Vec<(String, Kind)>)>>,
1128    /// The `(interner, PolicyRegistry)` a `CheckPolicy` op consults to resolve its predicate/capability name
1129    /// into a `PolicyCondition` (the bytecode carries only the interned name, so the registry — the same one
1130    /// the VM's `policy_ctx` uses — is required to compile the condition). `None` when the front-end supplied
1131    /// no policies: a `CheckPolicy` then lowers to the VM's "Security Check requires policies" trap.
1132    policy_ctx: Option<(&'a Interner, &'a PolicyRegistry)>,
1133    /// The current region's registers that some op mutates as the register's own collection
1134    /// ([`Mutation::Owned`]). Each has an owned flag, `own[r]`: 1 while the register is the only holder of its
1135    /// collection, so a change happens in place; 0 while the collection may be held elsewhere too, so a change
1136    /// copies it first — the VM's copy-on-write, decided without reference counts. Every region entry clears
1137    /// them: a parameter, a global, an element read out of a collection may all be shared.
1138    owned: std::collections::BTreeSet<u16>,
1139    /// The kind of handle each register of the current region holds, where every handle the region writes to it is
1140    /// of that one kind: a sequence such a register holds alone can be emptied in place for the next one.
1141    handle_kinds: std::collections::BTreeMap<u16, Kind>,
1142    /// The current region's ops, by absolute pc, and the pcs a jump can land on — what the dead-after scan
1143    /// ([`Emitter::dead_after`]) walks.
1144    region_span: (usize, usize),
1145    region_targets: std::collections::HashSet<usize>,
1146    /// Exact operation PCs selected by the conservative byte-view recognizer:
1147    /// TextBytes calls become pointer aliases and their IterPrepare operations
1148    /// walk those pointers directly.
1149    text_byte_calls: std::collections::HashSet<usize>,
1150    text_byte_iters: std::collections::HashSet<usize>,
1151    /// The registers live after each op of the current region (by `pc - start`): read on some path from there
1152    /// before being written.
1153    live_out: Vec<std::collections::BTreeSet<u16>>,
1154    /// The ops (by pc) whose integer result is held exact ([`Emitter::exactness`]), settled with the signatures, and
1155    /// those among them exact only because they can grow: a region runs these on machine words until one overflows.
1156    exact_defs: std::collections::HashSet<usize>,
1157    speculative_defs: std::collections::HashSet<usize>,
1158    /// The registers an op writing more than one leaves exact, by (pc, register): a call's in-out arguments, a
1159    /// swap's two sides. Held exact, a swap side that receives a machine word takes it tagged.
1160    exact_outs: std::collections::HashSet<(usize, u16)>,
1161    /// Per function, the parameters an exact variable starts from only because it grows: machine words on entry.
1162    entry_speculative: std::collections::HashMap<usize, std::collections::BTreeSet<u16>>,
1163    /// Set while the region's kinds on machine words are inferred or emitted: the speculative ops are not exact.
1164    on_words: std::cell::Cell<bool>,
1165    /// The exact ops whose own lowering makes a machine integer, in the region held exact (`[0]`) and on machine
1166    /// words (`[1]`): their result is tagged once it lands.
1167    promoted: std::cell::RefCell<[std::collections::HashSet<usize>; 2]>,
1168    /// The prefix of the labels of the version of the region being emitted: `L` on machine words, `X` held exact.
1169    label: char,
1170    /// The ops of the region on machine words that move into the region held exact when they overflow, and those
1171    /// whose exact result moves it there when it does not fit a word.
1172    deopt: std::collections::BTreeSet<usize>,
1173    arrivals: std::collections::BTreeSet<usize>,
1174    /// The functions whose body is their word entry ([`Emitter::takes_words`]), `fn_N_w`: a caller on machine words
1175    /// hands it machine words, and its exact result comes back a machine word, or the exact value it escaped to, marked
1176    /// so; `fn_N` is a wrapper taking tagged words ([`Emitter::emit_tagged_entry`]).
1177    word_twins: std::collections::HashSet<usize>,
1178    /// Set while a word entry's body is emitted.
1179    word_entry: bool,
1180    /// Set by a call on machine words through a word entry, whose escape test stands in for the arrival's.
1181    twin_arrival: bool,
1182    /// A word entry's region held exact, emitted apart as `fn_N_x` ([`Emitter::emit_held_function`]) so the word
1183    /// entry's frame holds only what its machine words need: its text, whether it keeps owned flags, and each way in
1184    /// (a resume id, the loops it hands over, the op it resumes at).
1185    held_body: String,
1186    held_owns: bool,
1187    held_entries: Vec<(usize, Vec<usize>, usize, Vec<u16>)>,
1188    /// How a word entry's tagged entry enters its region held exact when a parameter arrives boxed: the way in, and
1189    /// the registers live where the region starts.
1190    held_start: Option<(usize, Vec<u16>)>,
1191    /// The functions a device may answer ([`compile_program_to_c_offloaded`]), and those kept on the host, with why.
1192    offloads: Vec<Offload>,
1193    kept: Vec<(usize, String)>,
1194    /// Whether the program computes with the exact extension ([`crate::vm::tower::reaches`]): it links the value
1195    /// runtime and its regions' exact webs are settled as their kinds are.
1196    tower: bool,
1197    /// The natural kind each definition of an exact web makes, by version (`[0]` held exact, `[1]` on machine
1198    /// words) and pc, where it is not a cell: the value it writes is converted into one as it lands.
1199    tower_from: std::cell::RefCell<[std::collections::HashMap<usize, Kind>; 2]>,
1200    /// Per region (by its first pc), the parameters of an exact web that arrive as another kind, with that kind:
1201    /// the region converts each into a cell as it begins.
1202    tower_entries: std::cell::RefCell<std::collections::HashMap<usize, Vec<(u16, Kind)>>>,
1203    /// Each definition a number join lands as another kind ([`numbers`]), by version and pc: the kind it computes and
1204    /// the kind it lands as.
1205    number_from: std::cell::RefCell<[std::collections::HashMap<usize, (Kind, Kind)>; 2]>,
1206    /// Per region (by its first pc): the parameters a number join converts as the region begins (the kind each arrives
1207    /// as and the kind it is converted to), and one definition of each variable no machine form holds.
1208    number_entries: std::cell::RefCell<
1209        std::collections::HashMap<usize, (Vec<(u16, Kind, Kind)>, Vec<crate::vm::variables::Site>)>,
1210    >,
1211}
1212
1213impl<'a> Emitter<'a> {
1214    /// Whether function `fi`'s integer arithmetic runs on the exact `lint_*` tower (see `exact_int`): a closure
1215    /// body, and any function that returns a value without declaring its type — a function value the optimizer
1216    /// turned into a direct call is still the lambda it was, and nothing bounds what it returns.
1217    fn computes_exactly(&self, fi: usize) -> bool {
1218        if self.closure_bodies.contains(&fi) {
1219            return true;
1220        }
1221        let f = &self.prog.functions[fi];
1222        if f.return_type.is_some() {
1223            return false;
1224        }
1225        let end = self
1226            .prog
1227            .functions
1228            .iter()
1229            .map(|g| g.entry_pc)
1230            .filter(|&e| e > f.entry_pc)
1231            .min()
1232            .unwrap_or(self.prog.code.len());
1233        self.prog.code[f.entry_pc..end]
1234            .iter()
1235            .any(|op| matches!(op, Op::Return { .. }))
1236    }
1237
1238    /// Whether function `fi`'s body is a word entry, `fn_N_w`: an exact parameter or an exact result it runs on
1239    /// machine words until a value outgrows one. Its exact parameters take machine words with no tag to test or
1240    /// strip, and it returns a machine word, or the exact value it moved to marked `escaped` ([`Emitter::ret`]), so a
1241    /// call between two regions on machine words boxes nothing; its region held exact is a function of its own
1242    /// ([`Emitter::emit_held_function`]). A function value's body, a function whose
1243    /// `mutable` parameter is the caller's variable, and one a device may answer keep the one entry.
1244    fn takes_words(&self, fi: usize) -> bool {
1245        let exact_param = self.param_kinds[fi].contains(&Kind::BigInt);
1246        let tower = self.param_kinds[fi].contains(&Kind::Tower) || self.fn_ret[fi] == Kind::Tower;
1247        (exact_param || self.fn_ret[fi] == Kind::BigInt)
1248            && !tower
1249            && !self.computes_exactly(fi)
1250            && self.inout_regs(fi).is_empty()
1251            && !self.offloads.iter().any(|o| o.func == fi)
1252    }
1253
1254    /// The statement returning `value` from the function being emitted: from a word entry, with whether it escaped to
1255    /// an exact value.
1256    fn ret(&self, value: &str, escaped: bool) -> String {
1257        if self.word_entry {
1258            format!("return (lc_wret){{ {value}, {} }};", u8::from(escaped))
1259        } else {
1260            format!("return {value};")
1261        }
1262    }
1263
1264    /// Intern a tuple's per-position kinds, returning the index a `Kind::Tuple` carries (dedup by value, so
1265    /// two `(Int, Text)` tuples share one entry).
1266    fn intern_tuple(&self, ks: Vec<Kind>) -> u16 {
1267        let mut t = self.tuple_types.borrow_mut();
1268        if let Some(i) = t.iter().position(|e| e == &ks) {
1269            return i as u16;
1270        }
1271        t.push(ks);
1272        (t.len() - 1) as u16
1273    }
1274
1275    /// The per-position kinds of the tuple layout `idx`.
1276    fn tuple_positions(&self, idx: u16) -> Vec<Kind> {
1277        self.tuple_types
1278            .borrow()
1279            .get(idx as usize)
1280            .cloned()
1281            .unwrap_or_default()
1282    }
1283
1284    /// Intern an `Optional`'s inner element kind, returning the index a `Kind::Optional` carries.
1285    fn intern_opt(&self, inner: Kind) -> u16 {
1286        let mut t = self.opt_types.borrow_mut();
1287        if let Some(i) = t.iter().position(|e| e == &inner) {
1288            return i as u16;
1289        }
1290        t.push(inner);
1291        (t.len() - 1) as u16
1292    }
1293
1294    /// The inner element kind of the `Optional` layout `idx`.
1295    fn opt_inner(&self, idx: u16) -> Kind {
1296        self.opt_types
1297            .borrow()
1298            .get(idx as usize)
1299            .copied()
1300            .unwrap_or(Kind::Unknown)
1301    }
1302
1303    /// The element kind of a sequence or set kind — a scalar's, or the interned element of a `SeqOf`.
1304    fn elem_kind(&self, k: Kind) -> Kind {
1305        match k {
1306            Kind::SeqOf(ei) => self
1307                .seq_elems
1308                .borrow()
1309                .get(ei as usize)
1310                .copied()
1311                .unwrap_or(Kind::Unknown),
1312            // A sequence made empty that nothing ever puts an element in holds none.
1313            Kind::SeqUnknown | Kind::SetUnknown => Kind::Never,
1314            // An exact list's items are exact values.
1315            Kind::Tower => Kind::Tower,
1316            other => other.elem(),
1317        }
1318    }
1319
1320    /// The sequence kind whose elements are of kind `elem`: a scalar's own, a `SeqOf` for any other known kind,
1321    /// `SeqUnknown` for an unknown element.
1322    fn seq_kind_for(&self, elem: Kind) -> Kind {
1323        match elem {
1324            Kind::Int => Kind::SeqInt,
1325            Kind::Float => Kind::SeqFloat,
1326            Kind::Text => Kind::SeqText,
1327            Kind::Bool => Kind::SeqBool,
1328            Kind::Unknown => Kind::SeqUnknown,
1329            other => {
1330                let mut t = self.seq_elems.borrow_mut();
1331                let i = match t.iter().position(|e| *e == other) {
1332                    Some(i) => i,
1333                    None => {
1334                        t.push(other);
1335                        t.len() - 1
1336                    }
1337                };
1338                Kind::SeqOf(i as u16)
1339            }
1340        }
1341    }
1342
1343    /// The tag of constructor `ctor` of the undeclared enum `ty`, in the program-wide order; `None` for a
1344    /// constructor the program never builds (a test for it is always false).
1345    fn ctor_tag(&self, ty: &str, ctor: &str) -> Option<usize> {
1346        self.ctor_tags.get(ty)?.iter().position(|c| c == ctor)
1347    }
1348
1349    /// The `Kind::Inductive` of undeclared enum `ty` that can be only the constructors `known` lists, each with
1350    /// the payload kinds given for it; interned by value.
1351    fn inductive_kind(&self, ty: &str, known: &[(&str, Vec<Kind>)]) -> Kind {
1352        let ctors: Vec<(String, Option<Vec<Kind>>)> = self
1353            .ctor_tags
1354            .get(ty)
1355            .map(|cs| cs.as_slice())
1356            .unwrap_or(&[])
1357            .iter()
1358            .map(|c| {
1359                (
1360                    c.clone(),
1361                    known.iter().find(|(k, _)| k == c).map(|(_, ks)| ks.clone()),
1362                )
1363            })
1364            .collect();
1365        self.intern_inductive(ty.to_string(), ctors)
1366    }
1367
1368    fn intern_inductive(&self, ty: String, ctors: Vec<(String, Option<Vec<Kind>>)>) -> Kind {
1369        let mut t = self.inductive_types.borrow_mut();
1370        let entry = (ty, ctors);
1371        let idx = match t.iter().position(|e| *e == entry) {
1372            Some(i) => i,
1373            None => {
1374                t.push(entry);
1375                t.len() - 1
1376            }
1377        };
1378        Kind::Inductive(idx as u16)
1379    }
1380
1381    /// The undeclared enum layout `idx`: its type name and, per constructor in tag order, the payload kinds of
1382    /// each constructor the value can be.
1383    fn inductive(&self, idx: u16) -> (String, Vec<(String, Option<Vec<Kind>>)>) {
1384        self.inductive_types
1385            .borrow()
1386            .get(idx as usize)
1387            .cloned()
1388            .unwrap_or_default()
1389    }
1390
1391    /// The payload kind at `index` of constructor `ctor` of the undeclared enum layout `idx`.
1392    fn inductive_payload(&self, idx: u16, ctor: &str, index: usize) -> Kind {
1393        let (_, ctors) = self.inductive(idx);
1394        ctors
1395            .iter()
1396            .find(|(c, _)| c == ctor)
1397            .and_then(|(_, ks)| ks.as_ref()?.get(index).copied())
1398            .unwrap_or(Kind::Unknown)
1399    }
1400
1401    /// Two layouts of one undeclared enum as one: the value can be any constructor either can be, and a
1402    /// payload kind known on one side and unknown on the other is known; `None` when they disagree on a kind
1403    /// both know.
1404    fn unify_inductive(&self, a: u16, b: u16) -> Option<Kind> {
1405        let (ta, ca) = self.inductive(a);
1406        let (tb, cb) = self.inductive(b);
1407        if ta != tb || ca.len() != cb.len() {
1408            return None;
1409        }
1410        let mut ctors = Vec::with_capacity(ca.len());
1411        for ((name, ka), (_, kb)) in ca.iter().zip(cb.iter()) {
1412            let merged = match (ka, kb) {
1413                (None, None) => None,
1414                (Some(k), None) | (None, Some(k)) => Some(k.clone()),
1415                (Some(ka), Some(kb)) => {
1416                    let n = ka.len().max(kb.len());
1417                    let mut ks = Vec::with_capacity(n);
1418                    for i in 0..n {
1419                        let (x, y) = (
1420                            ka.get(i).copied().unwrap_or(Kind::Unknown),
1421                            kb.get(i).copied().unwrap_or(Kind::Unknown),
1422                        );
1423                        ks.push(match (x, y) {
1424                            (Kind::Unknown, y) => y,
1425                            (x, Kind::Unknown) => x,
1426                            (x, y) if x == y => x,
1427                            _ => return None,
1428                        });
1429                    }
1430                    Some(ks)
1431                }
1432            };
1433            ctors.push((name.clone(), merged));
1434        }
1435        Some(self.intern_inductive(ta, ctors))
1436    }
1437
1438    /// Intern a map's (key, value) kinds, returning the index a `Kind::Map` carries.
1439    fn intern_map(&self, key: Kind, val: Kind) -> u16 {
1440        let mut t = self.map_types.borrow_mut();
1441        if let Some(i) = t.iter().position(|e| *e == (key, val)) {
1442            return i as u16;
1443        }
1444        t.push((key, val));
1445        (t.len() - 1) as u16
1446    }
1447    /// The (key, value) kinds of the map layout `idx`.
1448    fn map_pair(&self, idx: u16) -> (Kind, Kind) {
1449        self.map_types
1450            .borrow()
1451            .get(idx as usize)
1452            .copied()
1453            .unwrap_or((Kind::Int, Kind::Int))
1454    }
1455    /// Refine a (key, value) kind pair into a `Kind::Map`: `MapUnknown` while a side is still unknown (an empty
1456    /// map defaults to `Map_i64_i64` and renders `{}`); `Kind::Unknown` (→ a clean refusal) for a key the
1457    /// runtime cannot hash by value (anything but an Int, a word or a Text); otherwise an interned concrete `Map`.
1458    fn map_of(&self, key: Kind, val: Kind) -> Kind {
1459        // A map may cross function/global and word/exact region boundaries.
1460        // Its integer cells therefore always use the exact register encoding,
1461        // including values that currently fit a machine word.
1462        let key = if key == Kind::BigInt { Kind::Int } else { key };
1463        let val = if val == Kind::Int { Kind::BigInt } else { val };
1464        if key == Kind::Unknown || val == Kind::Unknown {
1465            return Kind::MapUnknown;
1466        }
1467        if map_key_storage(key).is_none() {
1468            return Kind::Unknown;
1469        }
1470        Kind::Map(self.intern_map(key, val))
1471    }
1472    /// The (key, value) kinds to operate on: an unrefined empty map (`MapUnknown`) acts as `Map_i64_i64`.
1473    fn resolve_map_kv(&self, k: Kind) -> Option<(Kind, Kind)> {
1474        match k {
1475            Kind::Map(idx) => Some(self.map_pair(idx)),
1476            Kind::MapUnknown => Some((Kind::Int, Kind::BigInt)),
1477            _ => None,
1478        }
1479    }
1480
1481    /// The compile-time integer that register `reg` holds at `pc`, when the most recent write to it in the
1482    /// region `[start, pc)` is a `LoadConst` of an `Int`. Positional tuple access (`Let (a, b) be …` lowers to
1483    /// `Index t 0` / `Index t 1`) always feeds the index from a fresh constant, so this recovers the position
1484    /// needed to pick the heterogeneous element's kind. Returns `None` for a non-constant index.
1485    fn const_int_at(&self, start: usize, pc: usize, reg: u16) -> Option<i64> {
1486        for p in (start..pc).rev() {
1487            if let Op::LoadConst { dst, idx } = &self.prog.code[p] {
1488                if *dst == reg {
1489                    return match &self.prog.constants[*idx as usize] {
1490                        Constant::Int(v) => Some(*v),
1491                        _ => None,
1492                    };
1493                }
1494            }
1495        }
1496        None
1497    }
1498
1499    /// The compile-time Text that register `reg` holds at `pc`, when the most recent write to it in `[start, pc)`
1500    /// is a `LoadConst` of a `Text` (a field name the bytecode indexes a record by).
1501    fn const_text_at(&self, start: usize, pc: usize, reg: u16) -> Option<String> {
1502        for p in (start..pc).rev() {
1503            let mut writes = false;
1504            self.for_each_written(&self.prog.code[p], |w| writes |= w == reg);
1505            if writes {
1506                return match &self.prog.code[p] {
1507                    Op::LoadConst { idx, .. } => match &self.prog.constants[*idx as usize] {
1508                        Constant::Text(t) => Some(t.clone()),
1509                        _ => None,
1510                    },
1511                    _ => None,
1512                };
1513            }
1514        }
1515        None
1516    }
1517
1518    /// The cell of the field named `name` in a record of kind `k`.
1519    fn field_cell_of_name(&self, k: Kind, name: &str) -> Option<usize> {
1520        match k {
1521            Kind::Struct(si) => self
1522                .prog
1523                .struct_types
1524                .get(si as usize)?
1525                .fields
1526                .iter()
1527                .position(|(n, _)| n == name),
1528            Kind::StructSynth(idx) => self
1529                .synth_structs
1530                .borrow()
1531                .get(idx as usize)?
1532                .1
1533                .iter()
1534                .position(|(n, _)| n == name),
1535            _ => None,
1536        }
1537    }
1538
1539    /// The result kind of an integer `Add`/`Sub`/`Mul`/`Pow`: exact inside a function value's body
1540    /// (`exact_int`), otherwise the numeric join of the operands — exact when either is. Which other results are
1541    /// exact is [`Emitter::exactness`]'s to say.
1542    fn arith_result_kind(&self, lk: Kind, rk: Kind) -> Kind {
1543        let j = join(lk, rk);
1544        if self.exact_int.get() && matches!(j, Kind::Int | Kind::BigInt) {
1545            Kind::BigInt
1546        } else {
1547            j
1548        }
1549    }
1550
1551    /// `r[dst] = func(lhs, rhs)` over exact integers, each operand read as its tagged word.
1552    fn emit_exact_bin(
1553        &mut self,
1554        dst: u16,
1555        lhs: u16,
1556        rhs: u16,
1557        lk: Kind,
1558        rk: Kind,
1559        func: &str,
1560    ) -> Result<(), String> {
1561        let (l, r) = (as_exact(lhs, lk)?, as_exact(rhs, rk)?);
1562        self.line(&format!("r[{dst}] = {func}({l}, {r});"));
1563        Ok(())
1564    }
1565
1566    /// `r[dst] = func(lhs, rhs)` over exact integers, whose result fits a machine integer unless it is exact
1567    /// itself (`result`): a quotient, a remainder, a bitwise combination.
1568    fn emit_exact_bounded(
1569        &mut self,
1570        dst: u16,
1571        lhs: u16,
1572        rhs: u16,
1573        kinds: &[Kind],
1574        result: Kind,
1575        func: &str,
1576    ) -> Result<(), String> {
1577        let (l, r) = (
1578            as_exact(lhs, kind_at(kinds, lhs))?,
1579            as_exact(rhs, kind_at(kinds, rhs))?,
1580        );
1581        let value = format!("{func}({l}, {r})");
1582        let value = match (result, func) {
1583            (Kind::BigInt, _) => value,
1584            (_, "lc_xdiv" | "lc_xmod" | "lc_xfloordiv") => format!("lc_xnarrow({value})"),
1585            _ => value,
1586        };
1587        self.line(&format!("r[{dst}] = {value};"));
1588        Ok(())
1589    }
1590
1591    /// Whether an integer op over `operands` producing a `result` runs on exact integers: an operand or the
1592    /// result is exact, and nothing about them is a Real, a rational or a word.
1593    fn exact_integers(&self, operands: &[u16], kinds: &[Kind], result: Kind) -> bool {
1594        let ks: Vec<Kind> = operands.iter().map(|r| kind_at(kinds, *r)).collect();
1595        (result == Kind::BigInt || ks.contains(&Kind::BigInt))
1596            && ks
1597                .iter()
1598                .all(|k| matches!(k, Kind::Int | Kind::BigInt | Kind::Bool))
1599    }
1600
1601    /// Write one indented C line.
1602    fn line(&mut self, s: &str) {
1603        self.out.push_str("    ");
1604        self.out.push_str(s);
1605        self.out.push('\n');
1606    }
1607
1608    fn compile(&mut self) -> Result<(), String> {
1609        self.out.push_str(C_GC_COLLECTOR);
1610        self.out.push_str(&c_runtime());
1611        self.out.push('\n');
1612        // The exact extension's values live in the linked value runtime, declared only for a program that uses it.
1613        self.tower = crate::vm::tower::reaches(self.prog);
1614        if self.tower {
1615            self.out.push_str(super::tower::RUNTIME);
1616            self.out.push('\n');
1617        }
1618        // The single-replica CRDT lattice (counter map) and the offline loopback networking transport are
1619        // emitted ONLY when the program uses them, so a non-CRDT / non-net program stays byte-identical to a
1620        // world without them (mirroring the AST-C tier's conditional inclusion). Scan the flat op stream —
1621        // Main + every function body live in `code`.
1622        let uses_crdt = self.prog.code.iter().any(op_is_crdt);
1623        let uses_net = self.prog.code.iter().any(op_is_net);
1624        let uses_tcp = self.prog.code.iter().any(op_is_tcp);
1625        if uses_crdt {
1626            self.out.push_str(C_CRDT_RUNTIME);
1627            self.out.push('\n');
1628        }
1629        if uses_net {
1630            self.out.push_str(&c_net_runtime());
1631            self.out.push('\n');
1632        }
1633        // The sandboxed file system — emitted only when the program reads, writes or mounts a file.
1634        if self.prog.code.iter().any(op_is_file) {
1635            self.out.push_str(&c_vfs_runtime());
1636            self.out.push('\n');
1637        }
1638        // The real-socket TCP client runtime — emitted only when the program contains a `Tcp` op, so a
1639        // non-TCP program stays byte-identical to a world without it (mirroring the CRDT/net gates).
1640        if uses_tcp {
1641            self.out.push_str(C_TCP_RUNTIME);
1642            self.out.push('\n');
1643        }
1644        self.out.push_str(&walker_preamble());
1645        self.out.push('\n');
1646
1647        // ── Inline-closure-aware region layout: an embedded closure body is entered only via `CallValue` and
1648        // sits mid-region behind a jump-over, so each is emitted as its own region and the enclosing block gets a
1649        // HOLE there (control jumps over it). Mirrors `codegen_riscv::regions`. ──
1650        let inline_extents: Vec<(usize, usize)> = self
1651            .prog
1652            .functions
1653            .iter()
1654            .filter_map(|f| is_inline_closure(self.prog, f.entry_pc).map(|e| (f.entry_pc, e)))
1655            .collect();
1656        let mut top_starts: Vec<usize> = vec![0];
1657        for f in &self.prog.functions {
1658            if is_inline_closure(self.prog, f.entry_pc).is_none() {
1659                top_starts.push(f.entry_pc);
1660            }
1661        }
1662        top_starts.sort_unstable();
1663        top_starts.dedup();
1664        let code_len = self.prog.code.len();
1665        let block_end = |s: usize| -> usize {
1666            top_starts
1667                .iter()
1668                .copied()
1669                .find(|&x| x > s)
1670                .unwrap_or(code_len)
1671        };
1672        // Holes = inline closures embedded STRICTLY INSIDE span `[s, e)` (`hs > s`), so an inline-closure region
1673        // does not subtract its OWN extent (which begins exactly at `s`) — only nested closures are holes.
1674        let holes_in = |s: usize, e: usize| -> Vec<(usize, usize)> {
1675            inline_extents
1676                .iter()
1677                .copied()
1678                .filter(|&(hs, _)| hs > s && hs < e)
1679                .collect()
1680        };
1681        let main_end = block_end(0);
1682        if main_end == 0 {
1683            return Err("no Main region".into());
1684        }
1685        // Every region — Main and each function — as `(function, start, end, holes)`.
1686        let mut regions: Vec<(Option<usize>, usize, usize, Vec<(usize, usize)>)> =
1687            vec![(None, 0, main_end, holes_in(0, main_end))];
1688        for (fi, f) in self.prog.functions.iter().enumerate() {
1689            let end =
1690                is_inline_closure(self.prog, f.entry_pc).unwrap_or_else(|| block_end(f.entry_pc));
1691            regions.push((Some(fi), f.entry_pc, end, holes_in(f.entry_pc, end)));
1692        }
1693        // Module-level globals: a file-scope `int64_t g[N]` (zero-initialized) both Main and the functions reach.
1694        // Their kinds are settled before the signatures, so the exactness decision sees a global's reads as the
1695        // integers they are, and again after, so a global an exact integer is stored into reads back exact.
1696        self.param_kinds = self.declared_param_kinds();
1697        self.derive_global_kinds(main_end);
1698        self.refine_signatures(&regions);
1699        self.derive_global_kinds(main_end);
1700        // Which struct fields hold a LIVE counter CRDT (a `NewCrdt` value flowed in) — the side-set `GetField`/
1701        // `CrdtMerge` consult to SUM / per-replica-MAX instead of treating the `Int` cell as a plain integer.
1702        self.derive_counter_fields(main_end);
1703        self.word_twins = (0..self.prog.functions.len())
1704            .filter(|fi| self.takes_words(*fi))
1705            .collect();
1706        // The seam between the fixed runtime above and the program's own C below, the one the AST generator writes
1707        // ([`super::USER_CODE_SENTINEL`]): a reader of either C output finds the program part the same way.
1708        self.out.push_str(super::USER_CODE_SENTINEL);
1709        self.out.push('\n');
1710        if !self.prog.globals.is_empty() {
1711            writeln!(
1712                self.out,
1713                "static int64_t g[{}] = {{0}};",
1714                self.prog.globals.len()
1715            )
1716            .unwrap();
1717            // Each global's owned flag: set while the global is the only holder of its collection.
1718            writeln!(
1719                self.out,
1720                "static unsigned char gown[{}] = {{0}};",
1721                self.prog.globals.len()
1722            )
1723            .unwrap();
1724        }
1725
1726        // The device an offloaded function asks first; the caller appends its driver.
1727        if !self.offloads.is_empty() {
1728            writeln!(
1729                self.out,
1730                "int logos_dev_call(int region, const int64_t *args, int64_t *out);"
1731            )
1732            .unwrap();
1733        }
1734        // A word entry's result: a machine word, or the exact value it escaped to, tagged, with `escaped` set — two
1735        // words, returned in registers.
1736        if !self.word_twins.is_empty() {
1737            writeln!(
1738                self.out,
1739                "typedef struct {{ int64_t v; int64_t escaped; }} lc_wret;"
1740            )
1741            .unwrap();
1742            // What a word entry hands its region held exact: every register, its owned flags, and each loop's
1743            // snapshot and position, per thread.
1744            let regs = self
1745                .word_twins
1746                .iter()
1747                .map(|fi| self.prog.functions[*fi].register_count.max(1))
1748                .max()
1749                .unwrap_or(1);
1750            let loops = self
1751                .word_twins
1752                .iter()
1753                .map(|fi| {
1754                    let f = &self.prog.functions[*fi];
1755                    let end = is_inline_closure(self.prog, f.entry_pc)
1756                        .unwrap_or_else(|| block_end(f.entry_pc));
1757                    self.prog.code[f.entry_pc..end]
1758                        .iter()
1759                        .filter(|op| matches!(op, Op::IterPrepare { .. }))
1760                        .count()
1761                })
1762                .max()
1763                .unwrap_or(0);
1764            writeln!(self.out, "static __thread int64_t lc_held_regs[{regs}];").unwrap();
1765            writeln!(
1766                self.out,
1767                "static __thread unsigned char lc_held_owns[{regs}];"
1768            )
1769            .unwrap();
1770            writeln!(
1771                self.out,
1772                "static __thread int64_t lc_held_loops[{}];",
1773                2 * loops + 2
1774            )
1775            .unwrap();
1776        }
1777        // Forward-declare every function; a closure body takes a hidden trailing closure-object pointer arg.
1778        for (fi, f) in self.prog.functions.iter().enumerate() {
1779            let mut params: Vec<String> =
1780                (0..f.param_count).map(|i| self.param_decl(fi, i)).collect();
1781            if self.closure_bodies.contains(&fi) {
1782                params.push("int64_t clo".to_string());
1783            }
1784            // Carry sole ownership beside the return value. Scalar callers
1785            // pass NULL; collection callers pass the result register's flag.
1786            params.push("unsigned char *__ret_own".to_string());
1787            let sig = params.join(", ");
1788            writeln!(self.out, "static int64_t fn_{fi}({sig});").unwrap();
1789            if self.word_twins.contains(&fi) {
1790                writeln!(self.out, "static lc_wret fn_{fi}_w({sig});").unwrap();
1791                // The region held exact runs only once a value outgrows a word: cold, so every path into it
1792                // leaves the word entry's own code.
1793                writeln!(
1794                    self.out,
1795                    "__attribute__((cold, noinline)) static lc_wret fn_{fi}_x(int __resume, int __owned, unsigned char *__ret_own);"
1796                )
1797                .unwrap();
1798            }
1799        }
1800        self.out.push('\n');
1801        self.emit_defining_reader()?;
1802
1803        // Emit each function body over its (hole-subtracted) span.
1804        for fi in 0..self.prog.functions.len() {
1805            let f = &self.prog.functions[fi];
1806            let entry = f.entry_pc;
1807            let end = is_inline_closure(self.prog, entry).unwrap_or_else(|| block_end(entry));
1808            let (param_count, register_count) = (f.param_count, f.register_count);
1809            let captures = f.captures.clone();
1810            let holes = holes_in(entry, end);
1811            self.word_entry = self.word_twins.contains(&fi);
1812            let body = self.emit_function(
1813                fi,
1814                entry,
1815                end,
1816                param_count,
1817                register_count,
1818                &holes,
1819                &captures,
1820            );
1821            let word_entry = std::mem::take(&mut self.word_entry);
1822            body.map_err(|e| format!("fn_{fi}: {e}"))?;
1823            if word_entry {
1824                self.emit_held_function(fi, register_count);
1825                self.emit_tagged_entry(fi);
1826            }
1827        }
1828
1829        // Emit Main over [0, first top-level) minus the inline-closure holes. Main is not a closure body — its
1830        // integer arithmetic stays fast raw i64 (exact only where a constant overflow is proven).
1831        self.exact_int.set(false);
1832        writeln!(self.out, "int main(int argc, char **argv) {{").unwrap();
1833        writeln!(self.out, "    lc_argc = argc; lc_argv = argv;").unwrap();
1834        writeln!(
1835            self.out,
1836            "    int64_t r[{}] = {{0}};",
1837            self.prog.register_count.max(1)
1838        )
1839        .unwrap();
1840        writeln!(self.out, "    (void)r;").unwrap();
1841        let main_holes = holes_in(0, main_end);
1842        self.emit_region(0, main_end, false, None, &main_holes)
1843            .map_err(|e| format!("Main: {e}"))?;
1844        writeln!(self.out, "    return 0;\n}}").unwrap();
1845        Ok(())
1846    }
1847
1848    /// The register kinds function `fi`'s body starts with: its parameters' settled kinds (`param_kinds`),
1849    /// every other register unknown.
1850    fn seed_of(&self, fi: usize) -> Vec<Kind> {
1851        let f = &self.prog.functions[fi];
1852        let mut seed = vec![Kind::Unknown; f.register_count.max(1)];
1853        for (i, k) in self.param_kinds[fi].iter().enumerate() {
1854            if i < seed.len() {
1855                seed[i] = *k;
1856            }
1857        }
1858        seed
1859    }
1860
1861    /// The register kinds a region starts with: a function's parameters' settled kinds, nothing in Main.
1862    fn seed_of_region(&self, func: Option<usize>) -> Vec<Kind> {
1863        match func {
1864            Some(fi) => self.seed_of(fi),
1865            None => vec![Kind::Unknown; self.prog.register_count.max(1)],
1866        }
1867    }
1868
1869    /// The register kinds a region starts with on machine words: its entry kinds, an exact parameter a machine word
1870    /// (checked at entry, see [`Emitter::emit_region`]).
1871    fn words_seed_of_region(&self, func: Option<usize>) -> Vec<Kind> {
1872        self.seed_of_region(func)
1873            .into_iter()
1874            .map(|k| if k == Kind::BigInt { Kind::Int } else { k })
1875            .collect()
1876    }
1877
1878    /// Whether `op` hands its destination a value exact where it comes from: a function's exact result, an exact
1879    /// global, or an integer map value.
1880    fn arrives_exact(&self, op: &Op, kinds: &[Kind]) -> bool {
1881        match op {
1882            Op::Index { collection, .. } | Op::IndexUnchecked { collection, .. } => self
1883                .resolve_map_kv(kind_at(kinds, *collection))
1884                .is_some_and(|(_, value)| value == Kind::BigInt),
1885            Op::GetField { obj, field, .. } => self
1886                .resolve_field_any(kind_at(kinds, *obj), *field)
1887                .is_some_and(|(cell, _)| {
1888                    self.field_storage_kind(kind_at(kinds, *obj), cell) == Some(Kind::BigInt)
1889                }),
1890            Op::Call { func, .. } => self.fn_ret.get(*func as usize) == Some(&Kind::BigInt),
1891            Op::CallValue { .. } => self
1892                .closure_bodies
1893                .iter()
1894                .any(|b| self.fn_ret.get(*b) == Some(&Kind::BigInt)),
1895            Op::GlobalGet { idx, .. } => {
1896                self.global_kinds.get(*idx as usize) == Some(&Kind::BigInt)
1897            }
1898            _ => false,
1899        }
1900    }
1901
1902    /// The register kinds a region held exact starts with: its entry kinds, and the parameters an exact variable
1903    /// starts from, exact.
1904    fn exact_seed_of_region(&self, func: Option<usize>) -> Vec<Kind> {
1905        let mut seed = self.seed_of_region(func);
1906        if let Some(regs) = func.and_then(|fi| self.entry_speculative.get(&fi)) {
1907            for reg in regs {
1908                if let Some(k @ (Kind::Int | Kind::Bool)) = seed.get_mut(*reg as usize) {
1909                    *k = Kind::BigInt;
1910                }
1911            }
1912        }
1913        seed
1914    }
1915
1916    /// Whether the op at `pc` makes its integer exact in the version being inferred or emitted: every exact op held
1917    /// exact, on machine words only the ones the exact tower computes.
1918    fn held_exact(&self, pc: usize) -> bool {
1919        self.exact_defs.contains(&pc)
1920            && !(self.on_words.get() && self.speculative_defs.contains(&pc))
1921    }
1922
1923    /// Every function's parameter kinds as its declaration gives them.
1924    fn declared_param_kinds(&self) -> Vec<Vec<Kind>> {
1925        self.prog
1926            .functions
1927            .iter()
1928            .map(|f| {
1929                f.param_types
1930                    .iter()
1931                    .map(|t| self.boundary_kind(t))
1932                    .collect()
1933            })
1934            .collect()
1935    }
1936
1937    /// Settle every function's parameter kinds and result kind over the whole program, to a fixpoint. A declared
1938    /// type seeds each; what the program actually passes and returns refines what the boundary under-types or
1939    /// leaves out — a tuple handed to a `Pair` parameter (which the boundary records as a `Seq`), an exact
1940    /// integer handed to an `Int` one, a lambda's undeclared result, what an `Option`'s payload turns out to be.
1941    fn refine_signatures(
1942        &mut self,
1943        regions: &[(Option<usize>, usize, usize, Vec<(usize, usize)>)],
1944    ) {
1945        let nf = self.prog.functions.len();
1946        self.param_kinds = self.declared_param_kinds();
1947        let declared_params = self.param_kinds.clone();
1948        // A function no path of which returns a value — a native the program declares and no body implements —
1949        // hands every caller `nothing`, whatever it declares.
1950        for (func, start, end, holes) in regions {
1951            if let Some(fi) = func {
1952                if !(*start..*end).any(|pc| {
1953                    !in_hole(pc, holes) && matches!(self.prog.code[pc], Op::Return { .. })
1954                }) {
1955                    self.fn_ret[*fi] = Kind::Nothing;
1956                }
1957            }
1958        }
1959        let declared_ret: Vec<Kind> = self.fn_ret.clone();
1960        for _ in 0..16 {
1961            let before = (
1962                self.param_kinds.clone(),
1963                self.fn_ret.clone(),
1964                self.net_message.get(),
1965                self.net_stream.get(),
1966                self.exact_defs.len(),
1967                self.speculative_defs.len(),
1968                self.exact_outs.len(),
1969                self.entry_speculative.clone(),
1970            );
1971            let mut passed: Vec<Vec<Option<Kind>>> = (0..nf)
1972                .map(|fi| vec![None; self.param_kinds[fi].len()])
1973                .collect();
1974            let (mut sent, mut streamed): (Option<Kind>, Option<Kind>) = (None, None);
1975            for (func, start, end, holes) in regions {
1976                self.exact_int
1977                    .set(func.is_some_and(|fi| self.computes_exactly(fi)));
1978                // The integers the region can make outgrow i64 — its own, and the parameters they start from — read
1979                // off its kinds on machine words, where only what the exact tower computes is exact.
1980                self.on_words.set(true);
1981                let on_words =
1982                    self.infer_kinds(*start, *end, self.words_seed_of_region(*func), holes);
1983                self.on_words.set(false);
1984                let exact = self.exactness(
1985                    *start,
1986                    *end,
1987                    holes,
1988                    &on_words,
1989                    &self.seed_of_region(*func),
1990                    *func,
1991                );
1992                self.speculative_defs
1993                    .retain(|pc| !(*start..*end).contains(pc));
1994                self.speculative_defs
1995                    .extend(exact.speculative.iter().copied());
1996                self.exact_defs.extend(exact.defs);
1997                self.exact_outs.extend(exact.outs);
1998                if let Some(fi) = func {
1999                    self.entry_speculative.insert(*fi, exact.entry_speculative);
2000                }
2001                // The signatures take the region held exact: what it passes and returns.
2002                let per_pc =
2003                    self.infer_kinds(*start, *end, self.exact_seed_of_region(*func), holes);
2004                self.exact_int.set(false);
2005                for pc in *start..*end {
2006                    if in_hole(pc, holes) {
2007                        continue;
2008                    }
2009                    match &self.prog.code[pc] {
2010                        Op::Call {
2011                            func: callee,
2012                            args_start,
2013                            arg_count,
2014                            ..
2015                        } => {
2016                            for i in 0..*arg_count as usize {
2017                                let k = kind_at(&per_pc[pc - start], args_start + i as u16);
2018                                if let Some(slot) = passed[*callee as usize].get_mut(i) {
2019                                    *slot = Some(match *slot {
2020                                        None => k,
2021                                        Some(prev) => self.join_passed(prev, k),
2022                                    });
2023                                }
2024                            }
2025                        }
2026                        Op::Return { src } => {
2027                            if let Some(fi) = func {
2028                                let k = kind_at(&per_pc[pc - start], *src);
2029                                self.refine_return(*fi, declared_ret[*fi], k);
2030                            }
2031                        }
2032                        _ => {}
2033                    }
2034                    // A return hands each `mutable` parameter back to the caller: what it holds there is passed to
2035                    // the parameter as much as what a caller lends it.
2036                    if let (Some(fi), Op::Return { .. } | Op::ReturnNothing) =
2037                        (func, &self.prog.code[pc])
2038                    {
2039                        for p in self.inout_regs(*fi) {
2040                            let k = kind_at(&per_pc[pc - start], p);
2041                            if let Some(slot) = passed[*fi].get_mut(p as usize) {
2042                                *slot = Some(match *slot {
2043                                    None => k,
2044                                    Some(prev) => self.join_passed(prev, k),
2045                                });
2046                            }
2047                        }
2048                    }
2049                    match &self.prog.code[pc] {
2050                        Op::NetSend { msg, .. } => {
2051                            let k = kind_at(&per_pc[pc - start], *msg);
2052                            sent = Some(sent.map_or(k, |s| self.join_passed(s, k)));
2053                        }
2054                        Op::NetStream { values, .. } => {
2055                            let k = kind_at(&per_pc[pc - start], *values);
2056                            streamed = Some(streamed.map_or(k, |s| self.join_passed(s, k)));
2057                        }
2058                        _ => {}
2059                    }
2060                }
2061            }
2062            for fi in 0..nf {
2063                for i in 0..passed[fi].len() {
2064                    if let Some(got) = passed[fi][i] {
2065                        self.param_kinds[fi][i] =
2066                            self.refine_param(declared_params[fi][i], self.param_kinds[fi][i], got);
2067                    }
2068                }
2069            }
2070            self.net_message.set(sent.unwrap_or(Kind::Unknown));
2071            self.net_stream.set(streamed.unwrap_or(Kind::Unknown));
2072            if (
2073                self.param_kinds.clone(),
2074                self.fn_ret.clone(),
2075                self.net_message.get(),
2076                self.net_stream.get(),
2077                self.exact_defs.len(),
2078                self.speculative_defs.len(),
2079                self.exact_outs.len(),
2080                self.entry_speculative.clone(),
2081            ) == before
2082            {
2083                break;
2084            }
2085        }
2086    }
2087
2088    /// Two kinds a parameter is handed at different call sites, as one: equal kinds, an exact integer over an
2089    /// `Int`, two layouts of one undeclared enum; anything else is not one kind.
2090    fn join_passed(&self, a: Kind, b: Kind) -> Kind {
2091        match (a, b) {
2092            (a, b) if a == b => a,
2093            (Kind::Tower, _) | (_, Kind::Tower) => Kind::Tower,
2094            (Kind::Int, Kind::BigInt) | (Kind::BigInt, Kind::Int) => Kind::BigInt,
2095            (Kind::Inductive(x), Kind::Inductive(y)) => {
2096                self.unify_inductive(x, y).unwrap_or(Kind::Unknown)
2097            }
2098            _ => Kind::Unknown,
2099        }
2100    }
2101
2102    /// A parameter's kind, given its declared kind, its current kind and the kind every call site hands it: an
2103    /// undeclared parameter takes what it is handed; an `Int` handed an exact integer is exact; a `Seq`
2104    /// handed a tuple of that element everywhere is that tuple; an `Option`/`Result` learns its payloads. What
2105    /// is learned is never given back.
2106    fn refine_param(&self, declared: Kind, current: Kind, passed: Kind) -> Kind {
2107        let refined = match (declared, passed) {
2108            // A parameter handed a value of the extension holds its cell, whatever it declares.
2109            (_, Kind::Tower) => Kind::Tower,
2110            (Kind::Unknown, p) if p != Kind::Unknown => p,
2111            (Kind::Int, Kind::BigInt) => Kind::BigInt,
2112            (d, Kind::Tuple(ti)) if d.seq_c_type().is_some() || d == Kind::SeqUnknown => {
2113                let elem = self.elem_kind(d);
2114                if self
2115                    .tuple_positions(ti)
2116                    .iter()
2117                    .all(|k| *k == elem || elem == Kind::Unknown)
2118                {
2119                    passed
2120                } else {
2121                    declared
2122                }
2123            }
2124            (Kind::Inductive(d), Kind::Inductive(p)) => {
2125                self.unify_inductive(d, p).unwrap_or(declared)
2126            }
2127            _ => declared,
2128        };
2129        match (current, refined) {
2130            (Kind::BigInt, Kind::Int) => Kind::BigInt,
2131            (c, r) if r == declared && c != declared => c,
2132            _ => refined,
2133        }
2134    }
2135
2136    /// Fold one `Return` of function `fi`, returning a value of kind `k`, into the function's result kind: a
2137    /// boxed kind the boundary under-types (a `Tuple` declared as a `Seq`, an exact integer or rational declared
2138    /// `Int`, a popped value), an undeclared result, an `Option`/`Result` learning its payloads.
2139    fn refine_return(&mut self, fi: usize, declared: Kind, k: Kind) {
2140        let current = self.fn_ret[fi];
2141        self.fn_ret[fi] = match (current, k) {
2142            (_, Kind::Unknown) => current,
2143            // A function that hands back a value of the extension answers its cell, whatever it declares.
2144            (Kind::Tower, _) | (_, Kind::Tower) => Kind::Tower,
2145            (Kind::Inductive(c), Kind::Inductive(r)) => {
2146                self.unify_inductive(c, r).unwrap_or(current)
2147            }
2148            (Kind::Int, Kind::BigInt) | (Kind::BigInt, Kind::Int) => Kind::BigInt,
2149            (c, r) if c == r => c,
2150            (c, r @ (Kind::Tuple(_) | Kind::BigInt | Kind::Rational | Kind::Optional(_)))
2151                if c == declared =>
2152            {
2153                r
2154            }
2155            (Kind::Unknown, r) if declared == Kind::Unknown => r,
2156            (c, r) if declared == Kind::Unknown => join(c, r),
2157            (c, _) => c,
2158        };
2159    }
2160
2161    /// Infer each global's kind from its `GlobalSet` sites, scanning Main + every function region (a couple of
2162    /// passes so a global assigned from another global settles).
2163    fn derive_global_kinds(&mut self, main_end: usize) {
2164        let n = self.prog.globals.len();
2165        if n == 0 {
2166            return;
2167        }
2168        // A global the program declares a type for is that type — an `Int` one held exact once an exact integer is
2169        // stored in it, as a parameter is; any other takes the kind of what is stored in it — never the `nothing` a
2170        // global is emptied to while its collection is changed in a register.
2171        self.global_kinds = (0..n)
2172            .map(|i| self.boundary_kind(self.prog.global_types.get(i).unwrap_or(&None)))
2173            .collect();
2174        let declared: Vec<Kind> = self.global_kinds.clone();
2175        // A global any region stores a value of the exact extension into holds one, whatever else it holds.
2176        let takes = |idx: usize, k: Kind| {
2177            k == Kind::Tower
2178                || match declared.get(idx).copied().unwrap_or(Kind::Unknown) {
2179                    Kind::Unknown => true,
2180                    Kind::Int => matches!(k, Kind::Int | Kind::BigInt),
2181                    _ => false,
2182                }
2183        };
2184        let regions = self.regions_with_seeds(main_end);
2185        for _ in 0..2 {
2186            let mut updates: Vec<(usize, Kind)> = Vec::new();
2187            for (start, end, seed) in &regions {
2188                let per_pc = self.infer_kinds(*start, *end, seed.clone(), &[]);
2189                for pc in *start..*end {
2190                    if let Op::GlobalSet { idx, src } = &self.prog.code[pc] {
2191                        let k = per_pc[pc - start]
2192                            .get(*src as usize)
2193                            .copied()
2194                            .unwrap_or(Kind::Unknown);
2195                        if !matches!(k, Kind::Unknown | Kind::Nothing) && takes(*idx as usize, k) {
2196                            updates.push((*idx as usize, k));
2197                        }
2198                    }
2199                }
2200            }
2201            for (idx, k) in updates {
2202                if let Some(slot) = self.global_kinds.get_mut(idx) {
2203                    *slot = match (*slot, k) {
2204                        (Kind::Tower, _) | (_, Kind::Tower) => Kind::Tower,
2205                        (Kind::BigInt, Kind::Int) | (Kind::Int, Kind::BigInt) => Kind::BigInt,
2206                        _ => k,
2207                    };
2208                }
2209            }
2210        }
2211    }
2212
2213    /// The `(start, end, seed)` of Main and every function region — the same partition `derive_global_kinds` /
2214    /// `derive_counter_fields` scan.
2215    fn regions_with_seeds(&self, main_end: usize) -> Vec<(usize, usize, Vec<Kind>)> {
2216        let mut regions: Vec<(usize, usize, Vec<Kind>)> = vec![(
2217            0,
2218            main_end,
2219            vec![Kind::Unknown; self.prog.register_count.max(1)],
2220        )];
2221        let mut starts: Vec<usize> = self.prog.functions.iter().map(|f| f.entry_pc).collect();
2222        starts.sort_unstable();
2223        for (fi, f) in self.prog.functions.iter().enumerate() {
2224            let end = starts
2225                .iter()
2226                .copied()
2227                .find(|&s| s > f.entry_pc)
2228                .unwrap_or(self.prog.code.len());
2229            regions.push((f.entry_pc, end, self.seed_of(fi)));
2230        }
2231        regions
2232    }
2233
2234    /// Discover which struct fields hold a LIVE counter CRDT: a `StructInsert` whose stored value is a
2235    /// `Kind::CrdtCounter` (a `NewCrdt` kind-4/5 result) marks `(struct name, cell)` as a counter field — so
2236    /// `GetField` SUMS the per-replica map and `CrdtMerge`/`CrdtJoin` take the per-replica MAX. A field that
2237    /// is NEVER assigned a `NewCrdt` value (e.g. `sync_only`'s `Tally.n`, only ever `CrdtBump`ed from a
2238    /// `Nothing` cell) is NOT a counter here — matching the VM's `crdt_counter_bump(Nothing, n) = Int(n)`,
2239    /// where the cell stays a plain integer. Mirrors `codegen_riscv::derive_counter_fields`, keyed by name so
2240    /// the synthesized-layout structs and the `prog.struct_types` structs share one key space.
2241    fn derive_counter_fields(&mut self, main_end: usize) {
2242        let regions = self.regions_with_seeds(main_end);
2243        let mut found: std::collections::HashSet<(String, usize)> =
2244            std::collections::HashSet::new();
2245        for (start, end, seed) in &regions {
2246            let per_pc = self.infer_kinds(*start, *end, seed.clone(), &[]);
2247            for pc in *start..*end {
2248                if let Op::StructInsert { obj, field, value } = &self.prog.code[pc] {
2249                    let cur = &per_pc[pc - start];
2250                    let vk = cur.get(*value as usize).copied().unwrap_or(Kind::Unknown);
2251                    if vk == Kind::CrdtCounter {
2252                        let ok = cur.get(*obj as usize).copied().unwrap_or(Kind::Unknown);
2253                        if let (Some(name), Some((cell, _))) = (
2254                            self.struct_name_of_kind(ok),
2255                            self.resolve_field_any(ok, *field),
2256                        ) {
2257                            found.insert((name, cell));
2258                        }
2259                    }
2260                }
2261            }
2262        }
2263        self.counter_fields = found;
2264    }
2265
2266    fn emit_function(
2267        &mut self,
2268        fi: usize,
2269        entry: usize,
2270        end: usize,
2271        param_count: u16,
2272        register_count: usize,
2273        holes: &[(usize, usize)],
2274        captures: &[(crate::intern::Symbol, Option<u16>)],
2275    ) -> Result<(), String> {
2276        let is_closure = self.closure_bodies.contains(&fi);
2277        if is_closure && !self.inout_regs(fi).is_empty() {
2278            return Err("a function value's body with a `mutable` parameter".into());
2279        }
2280        // Inside a function value's body, integer arithmetic is exact (`lint_*`) — see `exact_int`.
2281        self.exact_int.set(self.computes_exactly(fi));
2282        let mut params: Vec<String> = (0..param_count).map(|i| self.param_decl(fi, i)).collect();
2283        if is_closure {
2284            params.push("int64_t clo".to_string());
2285        }
2286        params.push("unsigned char *__ret_own".to_string());
2287        let sig = params.join(", ");
2288        if self.word_entry {
2289            writeln!(self.out, "static lc_wret fn_{fi}_w({sig}) {{").unwrap();
2290        } else {
2291            writeln!(self.out, "static int64_t fn_{fi}({sig}) {{").unwrap();
2292        }
2293        writeln!(
2294            self.out,
2295            "    int64_t r[{}] = {{0}};",
2296            register_count.max(1)
2297        )
2298        .unwrap();
2299        writeln!(self.out, "    (void)r;").unwrap();
2300        writeln!(self.out, "    if (__ret_own) *__ret_own = 0;").unwrap();
2301        let inout = self.inout_regs(fi);
2302        for i in 0..param_count {
2303            let deref = if inout.contains(&i) { "*" } else { "" };
2304            writeln!(self.out, "    r[{i}] = {deref}p{i};").unwrap();
2305        }
2306        // A closure body loads its captured values into slots `params..params+cap` and the present flags into
2307        // `params+cap..params+2*cap` from the closure object — the layout the VM's `CallValue` binding sets up.
2308        if is_closure && !captures.is_empty() {
2309            let cap = captures.len();
2310            writeln!(self.out, "    int64_t *_clo = (int64_t*)(intptr_t)clo;").unwrap();
2311            for k in 0..cap {
2312                writeln!(
2313                    self.out,
2314                    "    r[{}] = _clo[{}];",
2315                    param_count as usize + k,
2316                    1 + k
2317                )
2318                .unwrap();
2319                writeln!(
2320                    self.out,
2321                    "    r[{}] = _clo[{}];",
2322                    param_count as usize + cap + k,
2323                    1 + cap + k
2324                )
2325                .unwrap();
2326            }
2327        } else if is_closure {
2328            writeln!(self.out, "    (void)clo;").unwrap();
2329        }
2330        self.dispatch_to_device(fi);
2331        self.current_fn = Some(fi);
2332        self.emit_region(entry, end, true, Some(fi), holes)?;
2333        self.current_fn = None;
2334        let fall = self.ret("0", false);
2335        writeln!(self.out, "    {fall}\n}}\n").unwrap();
2336        Ok(())
2337    }
2338
2339    /// The region held exact of word entry `fi`, as a function of its own: its registers, owned flags and loops arrive
2340    /// through the spill buffers, `__resume` names the way in, and each way in tags what the region takes tagged and
2341    /// hands its loops over before it jumps to the op it resumes at. A word entry whose region never holds an integer
2342    /// exact has nothing to resume.
2343    fn emit_held_function(&mut self, fi: usize, register_count: usize) {
2344        let n = register_count.max(1);
2345        let body = std::mem::take(&mut self.held_body);
2346        let entries = std::mem::take(&mut self.held_entries);
2347        let owns = std::mem::take(&mut self.held_owns);
2348        writeln!(
2349            self.out,
2350            "static lc_wret fn_{fi}_x(int __resume, int __owned, unsigned char *__ret_own) {{"
2351        )
2352        .unwrap();
2353        if entries.is_empty() {
2354            writeln!(
2355                self.out,
2356                "    (void)__resume; (void)__owned; (void)__ret_own;"
2357            )
2358            .unwrap();
2359            writeln!(self.out, "    return (lc_wret){{ 0, 0 }};\n}}\n").unwrap();
2360            return;
2361        }
2362        writeln!(self.out, "    int64_t r[{n}];").unwrap();
2363        writeln!(self.out, "    memcpy(r, lc_held_regs, sizeof r);").unwrap();
2364        if owns {
2365            writeln!(self.out, "    unsigned char own[{n}]; (void)own;").unwrap();
2366            writeln!(
2367                self.out,
2368                "    if (__owned) memcpy(own, lc_held_owns, sizeof own); else memset(own, 0, sizeof own);"
2369            )
2370            .unwrap();
2371        } else {
2372            writeln!(self.out, "    (void)__owned;").unwrap();
2373        }
2374        writeln!(self.out, "    switch (__resume) {{").unwrap();
2375        for (id, ..) in &entries {
2376            writeln!(self.out, "    case {id}: goto R{id};").unwrap();
2377        }
2378        writeln!(self.out, "    default: abort();\n    }}").unwrap();
2379        self.out.push_str(&body);
2380        for (id, loops, resume, tags) in &entries {
2381            writeln!(self.out, "R{id}:;").unwrap();
2382            for reg in tags {
2383                writeln!(self.out, "    r[{reg}] = lc_xof(r[{reg}]);").unwrap();
2384            }
2385            for (k, h) in loops.iter().enumerate() {
2386                writeln!(
2387                    self.out,
2388                    "    _itv{h} = (__typeof__(_itv{h}))(intptr_t)lc_held_loops[{}]; _it{h} = (size_t)lc_held_loops[{}];",
2389                    2 * k,
2390                    2 * k + 1
2391                )
2392                .unwrap();
2393            }
2394            writeln!(self.out, "    goto X{resume};").unwrap();
2395        }
2396        writeln!(self.out, "}}\n").unwrap();
2397    }
2398
2399    /// The tagged entry of a function whose body is its word entry: `fn_N` takes its exact parameters as tagged words,
2400    /// as every caller off machine words passes them. Small ones enter the word entry untagged; a boxed one enters the
2401    /// region held exact (`fn_N_x`) at its start, handed the parameters live there. A machine-word result is tagged on
2402    /// the way out.
2403    fn emit_tagged_entry(&mut self, fi: usize) {
2404        let params = self.param_kinds[fi].clone();
2405        let count = self.prog.functions[fi].param_count as usize;
2406        let mut decls: Vec<String> = (0..count as u16).map(|i| self.param_decl(fi, i)).collect();
2407        decls.push("unsigned char *__ret_own".to_string());
2408        let exact: Vec<usize> = (0..count)
2409            .filter(|i| params.get(*i) == Some(&Kind::BigInt))
2410            .collect();
2411        let mut words: Vec<String> = (0..count)
2412            .map(|i| {
2413                if exact.contains(&i) {
2414                    format!("p{i} >> 1")
2415                } else {
2416                    format!("p{i}")
2417                }
2418            })
2419            .collect();
2420        words.push("__ret_own".to_string());
2421        let start = self.held_start.take();
2422        writeln!(self.out, "static int64_t fn_{fi}({}) {{", decls.join(", ")).unwrap();
2423        writeln!(self.out, "    lc_wret _w;").unwrap();
2424        match (&start, exact.is_empty()) {
2425            (Some((id, live)), false) => {
2426                let tags: Vec<String> = exact.iter().map(|i| format!("p{i}")).collect();
2427                writeln!(self.out, "    if (({}) & 1) {{", tags.join(" | ")).unwrap();
2428                for reg in live {
2429                    let value = if (*reg as usize) < count {
2430                        format!("p{reg}")
2431                    } else {
2432                        "0".to_string()
2433                    };
2434                    writeln!(self.out, "        lc_held_regs[{reg}] = {value};").unwrap();
2435                }
2436                writeln!(self.out, "        _w = fn_{fi}_x({id}, 0, __ret_own);").unwrap();
2437                writeln!(self.out, "    }} else {{").unwrap();
2438                writeln!(self.out, "        _w = fn_{fi}_w({});", words.join(", ")).unwrap();
2439                writeln!(self.out, "    }}").unwrap();
2440            }
2441            _ => {
2442                writeln!(self.out, "    _w = fn_{fi}_w({});", words.join(", ")).unwrap();
2443            }
2444        }
2445        if self.fn_ret[fi] == Kind::BigInt {
2446            writeln!(self.out, "    return _w.escaped ? _w.v : lc_xof(_w.v);").unwrap();
2447        } else {
2448            writeln!(self.out, "    return _w.v;").unwrap();
2449        }
2450        writeln!(self.out, "}}\n").unwrap();
2451    }
2452
2453    /// The registers `op` writes: its own, and for a call each argument register a `mutable` parameter leaves its
2454    /// final value in, which the VM keeps on return for the caller to copy back.
2455    fn for_each_written(&self, op: &Op, mut f: impl FnMut(u16)) {
2456        op.for_each_written_reg(&mut f);
2457        if let Op::Call {
2458            func, args_start, ..
2459        } = op
2460        {
2461            for p in self.inout_regs(*func as usize) {
2462                f(args_start + p);
2463            }
2464        }
2465    }
2466
2467    /// The parameters of function `fi` that are the caller's own variables (`mutable`, declared or inferred): the
2468    /// argument registers the VM keeps when the call returns, for the caller to copy back.
2469    fn inout_regs(&self, fi: usize) -> Vec<u16> {
2470        let f = &self.prog.functions[fi];
2471        f.mutable_param_regs
2472            .iter()
2473            .copied()
2474            .filter(|p| *p < f.param_count)
2475            .collect()
2476    }
2477
2478    /// The transient argument-window register whose collection a direct call
2479    /// consumes and returns, when that identity proof is unambiguous.  The VM
2480    /// compiler records exactly these inferred mutable borrows in
2481    /// `cow_exempt_regs`: an `EnsureOwned` at the call site isolates the value,
2482    /// the callee mutates that same allocation, and its result gives it back.
2483    fn consumed_borrow_arg(&self, func: u16, args_start: u16, arg_count: u16) -> Option<u16> {
2484        let f = self.prog.functions.get(func as usize)?;
2485        let mut params = f
2486            .cow_exempt_regs
2487            .iter()
2488            .copied()
2489            .filter(|p| *p < arg_count && *p < f.param_count);
2490        let param = params.next()?;
2491        // Multiple consumed collection parameters do not identify which one a
2492        // single return value carries.  Keep the conservative COW path.
2493        if params.next().is_some() {
2494            return None;
2495        }
2496        Some(args_start + param)
2497    }
2498
2499    fn consumed_borrow_transfer(&self, op: &Op) -> Option<(u16, u16)> {
2500        let Op::Call {
2501            dst,
2502            func,
2503            args_start,
2504            arg_count,
2505        } = op
2506        else {
2507            return None;
2508        };
2509        self.consumed_borrow_arg(*func, *args_start, *arg_count)
2510            .map(|arg| (*dst, arg))
2511    }
2512
2513    /// Whether `dst` is an untracked transient argument-window slot for the
2514    /// next direct call, at a parameter the callee cannot mutate in place.
2515    /// The VM compiler materializes arguments as `Move`s immediately before
2516    /// the call.  Moving a sole-owned collection into an untracked window used
2517    /// to clear the source's owner bit permanently, even though ordinary value
2518    /// parameters use COW and therefore only borrow the caller's allocation.
2519    fn feeds_readonly_call_arg(&self, pc: usize, dst: u16) -> bool {
2520        let (_, end) = self.region_span;
2521        for at in pc + 1..end.min(pc + 16) {
2522            if self.region_targets.contains(&at) {
2523                return false;
2524            }
2525            match &self.prog.code[at] {
2526                Op::Move { .. } => continue,
2527                Op::Call {
2528                    func,
2529                    args_start,
2530                    arg_count,
2531                    ..
2532                } => {
2533                    if dst < *args_start || dst >= args_start.saturating_add(*arg_count) {
2534                        return false;
2535                    }
2536                    let param = dst - *args_start;
2537                    let f = &self.prog.functions[*func as usize];
2538                    return !self.inout_regs(*func as usize).contains(&param)
2539                        && !f.cow_exempt_regs.contains(&param);
2540                }
2541                _ => return false,
2542            }
2543        }
2544        false
2545    }
2546
2547    /// The C declaration of parameter `i` of function `fi`: a value, or for an in-out parameter a pointer to the
2548    /// caller's argument register, which the callee loads on entry and stores back before it returns.
2549    fn param_decl(&self, fi: usize, i: u16) -> String {
2550        if self.inout_regs(fi).contains(&i) {
2551            format!("int64_t *p{i}")
2552        } else {
2553            format!("int64_t p{i}")
2554        }
2555    }
2556
2557    /// Before a return from the current function: each in-out parameter's final value stored into the caller's
2558    /// argument register, in the kind the caller settles it to after the call (the parameter's own): a machine
2559    /// integer an exact parameter holds is stored exact. An exact value a machine-integer parameter cannot carry is
2560    /// refused.
2561    fn store_inout_params(&mut self, kinds: &[Kind]) -> Result<(), String> {
2562        let Some(fi) = self.current_fn else {
2563            return Ok(());
2564        };
2565        for p in self.inout_regs(fi) {
2566            let settled = self.param_kinds[fi]
2567                .get(p as usize)
2568                .copied()
2569                .unwrap_or(Kind::Unknown);
2570            match (settled, kind_at(kinds, p)) {
2571                (Kind::BigInt, Kind::Int | Kind::Bool) => self.line(&format!("*p{p} = lc_xof(r[{p}]);")),
2572                (Kind::Int | Kind::Bool, Kind::BigInt) => {
2573                    return Err(format!("`mutable` parameter {p} is settled as {settled:?} and returns an exact integer"))
2574                }
2575                _ => self.line(&format!("*p{p} = r[{p}];")),
2576            }
2577        }
2578        Ok(())
2579    }
2580
2581    /// The device dispatch at the entry of function `fi`, when the offload table names it: its parameters marshaled
2582    /// into their ports by their settled kinds, the device asked, and its DONE answer re-boxed to the settled result
2583    /// kind and returned. An exact integer past i64 cannot cross an Int port, so that call runs on the host. A port
2584    /// a settled kind does not fit keeps the whole function on the host, reported. A function with an in-out
2585    /// parameter stays on the host: a port carries a value in, not the caller's variable.
2586    fn dispatch_to_device(&mut self, fi: usize) {
2587        let Some(offload) = self.offloads.iter().find(|o| o.func == fi).cloned() else {
2588            return;
2589        };
2590        if !self.inout_regs(fi).is_empty() {
2591            self.kept.push((
2592                fi,
2593                "a `mutable` parameter is the caller's variable, which a port does not carry"
2594                    .into(),
2595            ));
2596            return;
2597        }
2598        let arity = self.prog.functions[fi].param_count as usize;
2599        if offload.params.len() != arity {
2600            self.kept.push((
2601                fi,
2602                format!(
2603                    "the region takes {} ports and the function {arity} parameters",
2604                    offload.params.len()
2605                ),
2606            ));
2607            return;
2608        }
2609        let mut marshal = Vec::new();
2610        for (i, port) in offload.params.iter().enumerate() {
2611            let settled = self.param_kinds[fi]
2612                .get(i)
2613                .copied()
2614                .unwrap_or(Kind::Unknown);
2615            let cross = match (port, settled) {
2616                (SlotKind::Int, Kind::Int)
2617                | (SlotKind::Float, Kind::Float)
2618                | (SlotKind::Word64, Kind::Word(64)) => {
2619                    format!("_dv[{i}] = r[{i}];")
2620                }
2621                (SlotKind::Int, Kind::BigInt) => {
2622                    format!("if (!lc_xi64(r[{i}], &_dv[{i}])) _dm = 0;")
2623                }
2624                (SlotKind::Bool, Kind::Bool) => format!("_dv[{i}] = r[{i}] != 0;"),
2625                (SlotKind::Word32, Kind::Word(32)) => {
2626                    format!("_dv[{i}] = (int64_t)((uint64_t)r[{i}] & 0xFFFFFFFFu);")
2627                }
2628                _ => {
2629                    self.kept.push((fi, format!("parameter {i} is settled as {settled:?}, which the {port:?} port does not carry")));
2630                    return;
2631                }
2632            };
2633            marshal.push(cross);
2634        }
2635        let settled = self.fn_ret[fi];
2636        let answer = match (offload.ret, settled) {
2637            (SlotKind::Int, Kind::Int)
2638            | (SlotKind::Float, Kind::Float)
2639            | (SlotKind::Word64, Kind::Word(64)) => "_do".to_string(),
2640            (SlotKind::Int, Kind::BigInt) => "lc_xof(_do)".to_string(),
2641            (SlotKind::Bool, Kind::Bool) => "(_do != 0)".to_string(),
2642            (SlotKind::Word32, Kind::Word(32)) => {
2643                "(int64_t)((uint64_t)_do & 0xFFFFFFFFu)".to_string()
2644            }
2645            (SlotKind::CharText, Kind::Text) => "(int64_t)(intptr_t)lc_chr(_do)".to_string(),
2646            (port, _) => {
2647                self.kept.push((fi, format!("the result is settled as {settled:?}, which the {port:?} port does not carry")));
2648                return;
2649            }
2650        };
2651        self.line(&format!(
2652            "{{ int64_t _dv[{}], _do; int _dm = 1;",
2653            arity.max(1)
2654        ));
2655        for m in marshal {
2656            self.line(&format!("  {m}"));
2657        }
2658        self.line(&format!(
2659            "  if (_dm && logos_dev_call({}, _dv, &_do)) return {answer}; }}",
2660            offload.region
2661        ));
2662    }
2663
2664    /// Emit the ops of one region `[start, end)`, the body of function `func` or Main. `in_function` distinguishes a
2665    /// `Return` (yield a value) from a Main-level `Return`/`Halt`. Jump targets use absolute-pc labels.
2666    ///
2667    /// A region with exact integers runs twice over: on machine words (labels `L…`), and held exact (labels `X…`),
2668    /// entered only when a value does not fit a word. On machine words each operation that can overflow an exact
2669    /// integer checks for it (stub `D…`), each exact value that arrives — a function's result, a global — checks that
2670    /// it is a small one and takes its word (stub `A…`), and so does each exact parameter on entry (stub `N…`). A stub
2671    /// tags every value the region held exact holds as a machine word, hands each running loop its place, and resumes
2672    /// the region held exact at the op (which redoes it exactly) or after the arrival. So exactness costs a not-taken
2673    /// branch per such op until a value outgrows the word.
2674    fn emit_region(
2675        &mut self,
2676        start: usize,
2677        end: usize,
2678        in_function: bool,
2679        func: Option<usize>,
2680        holes: &[(usize, usize)],
2681    ) -> Result<(), String> {
2682        let mut is_target = std::collections::HashSet::new();
2683        for op in &self.prog.code[start..end] {
2684            if let Some(t) = jump_target(op) {
2685                is_target.insert(t);
2686            }
2687        }
2688        let entry_seed = self.seed_of_region(func);
2689        let register_count = entry_seed.len();
2690        self.on_words.set(true);
2691        let on_words = self.infer_kinds(start, end, self.words_seed_of_region(func), holes);
2692        self.on_words.set(false);
2693        let held = self.infer_kinds(start, end, self.exact_seed_of_region(func), holes);
2694        let reachable = self.reachable(start, end, holes, &on_words);
2695        let integer = |k: Kind| matches!(k, Kind::Int | Kind::Bool);
2696        let dst_of = |op: &Op| {
2697            let mut dst = None;
2698            op.for_each_written_reg(|w| dst = Some(w));
2699            dst
2700        };
2701        let words_to_exact = |pc: usize, d: u16| {
2702            integer(kind_at(&on_words[pc - start + 1], d))
2703                && kind_at(&held[pc - start + 1], d) == Kind::BigInt
2704        };
2705        let live_ops: Vec<usize> = (start..end)
2706            .filter(|pc| !in_hole(*pc, holes) && reachable.contains(pc))
2707            .collect();
2708        let deopt: std::collections::BTreeSet<usize> = live_ops
2709            .iter()
2710            .copied()
2711            .filter(|pc| self.speculative_defs.contains(pc) && overflows(&self.prog.code[*pc]))
2712            .filter(|pc| dst_of(&self.prog.code[*pc]).is_some_and(|d| words_to_exact(*pc, d)))
2713            .collect();
2714        let arrivals: std::collections::BTreeSet<usize> = live_ops
2715            .iter()
2716            .copied()
2717            .filter(|pc| self.arrives_exact(&self.prog.code[*pc], &on_words[*pc - start]))
2718            .filter(|pc| dst_of(&self.prog.code[*pc]).is_some_and(|d| words_to_exact(*pc, d)))
2719            .collect();
2720        let exact_params: Vec<u16> = (0..register_count as u16)
2721            .filter(|r| entry_seed[*r as usize] == Kind::BigInt)
2722            .collect();
2723        let owned_of = |per_pc: &[Vec<Kind>]| -> std::collections::BTreeSet<u16> {
2724            let mut owned = std::collections::BTreeSet::new();
2725            for pc in start..end {
2726                if in_hole(pc, holes) {
2727                    continue;
2728                }
2729                match (mutation(&self.prog.code[pc]), &self.prog.code[pc]) {
2730                    (Some(Mutation::Owned(target)), _) => {
2731                        owned.insert(target);
2732                    }
2733                    // A text a register grows by appending is the register's own, like a collection it changes.
2734                    (_, Op::AddAssign { dst, .. })
2735                        if kind_at(&per_pc[pc - start], *dst) == Kind::Text =>
2736                    {
2737                        owned.insert(*dst);
2738                    }
2739                    _ => {}
2740                }
2741                if let Some((dst, arg)) = self.consumed_borrow_transfer(&self.prog.code[pc]) {
2742                    owned.insert(dst);
2743                    owned.insert(arg);
2744                }
2745            }
2746            owned
2747        };
2748        let speculates = !deopt.is_empty() || !arrivals.is_empty() || !exact_params.is_empty();
2749        let (owned_words, owned_held) = (owned_of(&on_words), owned_of(&held));
2750        // An owned flag rides every handle the region writes to a register, in either version (a deopt keeps the
2751        // flags): the register's handles are of one kind only when every write of one, in both, is of that kind.
2752        let handle_kinds = {
2753            let mut seen: std::collections::BTreeMap<u16, Option<Kind>> =
2754                std::collections::BTreeMap::new();
2755            for per_pc in [&on_words, &held] {
2756                for pc in (start..end).filter(|pc| !in_hole(*pc, holes)) {
2757                    self.for_each_written(&self.prog.code[pc], |w| {
2758                        let k = kind_at(&per_pc[pc - start + 1], w);
2759                        if !carries_no_handle(k) {
2760                            let one = seen.entry(w).or_insert(Some(k));
2761                            if *one != Some(k) {
2762                                *one = None;
2763                            }
2764                        }
2765                    });
2766                }
2767            }
2768            seen.into_iter()
2769                .filter_map(|(r, k)| Some((r, k?)))
2770                .collect()
2771        };
2772        self.handle_kinds = handle_kinds;
2773        // A parameter of an exact web arrives as what its caller holds and becomes a cell as the region begins.
2774        let arriving = self.tower_entries.borrow().get(&start).cloned().unwrap_or_default();
2775        for (reg, k) in arriving {
2776            let cell = self.tower_of(&format!("r[{reg}]"), k)?;
2777            self.line(&format!("r[{reg}] = (int64_t)(intptr_t)({cell});"));
2778        }
2779        // A parameter whose variable a number join widens is converted as the region begins; a variable no machine
2780        // form holds refuses the region by name.
2781        let (widened, refused) = self.number_entries.borrow().get(&start).cloned().unwrap_or_default();
2782        if let Some(site) = refused.first() {
2783            let at = match site {
2784                crate::vm::variables::Site::Op(pc) => format!("pc {pc}"),
2785                crate::vm::variables::Site::Entry(reg) => format!("parameter register {reg}"),
2786            };
2787            return Err(format!(
2788                "{at}: a variable that holds an exact number and a real where control merges is not yet lowered"
2789            ));
2790        }
2791        for (reg, from, to) in widened {
2792            let v = self.convert_number(&format!("r[{reg}]"), from, to)?;
2793            self.line(&format!("r[{reg}] = {v};"));
2794        }
2795        self.region_span = (start, end);
2796        self.live_out = self.liveness(start, end, holes);
2797        // A word entry's region held exact runs in a function of its own, which declares its own flags.
2798        let hot_owns =
2799            !owned_words.is_empty() || (speculates && !self.word_entry && !owned_held.is_empty());
2800        if hot_owns {
2801            writeln!(
2802                self.out,
2803                "    unsigned char own[{}] = {{0}}; (void)own;",
2804                register_count.max(1)
2805            )
2806            .unwrap();
2807            // A `mutable` parameter is the caller's own value, isolated at the call: the function holds it alone.
2808            for p in self.current_fn.map(|fi| self.inout_regs(fi)).unwrap_or_default() {
2809                if owned_words.contains(&p) || owned_held.contains(&p) {
2810                    writeln!(self.out, "    own[{p}] = 1;").unwrap();
2811                }
2812            }
2813        }
2814
2815        // On machine words: an exact parameter takes its word, or the whole call runs held exact. A word entry's
2816        // parameters arrive as machine words; its tagged entry takes a boxed one into the region held exact itself.
2817        if !exact_params.is_empty() && !self.word_entry {
2818            let tags: Vec<String> = exact_params.iter().map(|r| format!("r[{r}]")).collect();
2819            self.line(&format!("if (({}) & 1) goto N{start};", tags.join(" | ")));
2820            for r in &exact_params {
2821                self.line(&format!("r[{r}] >>= 1;"));
2822            }
2823        }
2824        self.on_words.set(true);
2825        self.owned = owned_words;
2826        self.region_targets = is_target.clone();
2827        let (text_byte_calls, text_byte_iters) =
2828            self.text_byte_iteration_plan(start, end, holes, &reachable);
2829        self.text_byte_calls = text_byte_calls;
2830        self.text_byte_iters = text_byte_iters;
2831        if !self.text_byte_calls.is_empty() {
2832            crate::optimize::mark_fired(crate::optimization::Opt::TextBytesView);
2833        }
2834        self.deopt = deopt.clone();
2835        self.arrivals = arrivals.clone();
2836        let words_frames = self.emit_version(start, end, in_function, holes, &on_words, &reachable);
2837        self.on_words.set(false);
2838        self.deopt.clear();
2839        self.arrivals.clear();
2840        let words_frames = words_frames?;
2841        if !speculates {
2842            return Ok(());
2843        }
2844        self.line(&self.ret("0", false));
2845
2846        // Held exact: it opens with its own label, and every op a stub resumes it at has one.
2847        let next_of = |pc: usize| live_ops.iter().copied().find(|p| *p > pc);
2848        let first = live_ops.first().copied();
2849        let resumed: Vec<usize> = arrivals.iter().filter_map(|pc| next_of(*pc)).collect();
2850        let held_owns = hot_owns || !owned_held.is_empty();
2851        self.owned = owned_held;
2852        self.region_targets = is_target
2853            .iter()
2854            .chain(&deopt)
2855            .chain(&first)
2856            .chain(&resumed)
2857            .copied()
2858            .collect();
2859        let hot = self.word_entry.then(|| std::mem::take(&mut self.out));
2860        self.label = 'X';
2861        let held_frames = self.emit_version(start, end, in_function, holes, &held, &reachable);
2862        self.label = 'L';
2863        self.line(&self.ret("0", false));
2864        if let Some(hot) = hot {
2865            self.held_body = std::mem::replace(&mut self.out, hot);
2866            self.held_owns = held_owns;
2867        }
2868        let held_frames = held_frames?;
2869        // A word entry hands its registers, owned flags and loops to `fn_N_x` through the spill buffers, one element
2870        // at a time so its own registers never leave the machine's, and returns what that function returns.
2871        let split = func.filter(|_| self.word_entry);
2872
2873        let first = first.ok_or("a region with exact parameters and no op")?;
2874        if !exact_params.is_empty() {
2875            if split.is_none() {
2876                writeln!(self.out, "N{start}:;").unwrap();
2877            }
2878            let starts_exact: Vec<u16> = func
2879                .and_then(|fi| self.entry_speculative.get(&fi))
2880                .into_iter()
2881                .flatten()
2882                .copied()
2883                .collect();
2884            for r in starts_exact.iter().filter(|_| split.is_none()) {
2885                if !exact_params.contains(r) && integer(entry_seed[*r as usize]) {
2886                    self.line(&format!("r[{r}] = lc_xof(r[{r}]);"));
2887                }
2888            }
2889            if split.is_some() {
2890                let id = self.held_entries.len();
2891                let live: Vec<u16> = self.live_in(first).into_iter().collect();
2892                let tags: Vec<u16> = live
2893                    .iter()
2894                    .copied()
2895                    .filter(|r| {
2896                        !exact_params.contains(r)
2897                            && starts_exact.contains(r)
2898                            && integer(entry_seed[*r as usize])
2899                    })
2900                    .collect();
2901                self.held_entries.push((id, Vec::new(), first, tags));
2902                self.held_start = Some((id, live));
2903            } else {
2904                self.line(&format!("goto X{first};"));
2905            }
2906        }
2907        let stubs = deopt
2908            .iter()
2909            .map(|pc| ('D', *pc, *pc, None))
2910            .chain(arrivals.iter().map(|pc| {
2911                (
2912                    'A',
2913                    *pc,
2914                    next_of(*pc).unwrap_or(*pc),
2915                    dst_of(&self.prog.code[*pc]),
2916                )
2917            }));
2918        for (kind, pc, resume, arrived) in stubs.collect::<Vec<_>>() {
2919            writeln!(self.out, "{kind}{pc}:;").unwrap();
2920            // The kinds where the region held exact resumes: before the op it redoes, after the value that arrived.
2921            let at = if kind == 'D' {
2922                pc - start
2923            } else {
2924                pc - start + 1
2925            };
2926            // A word entry's stub touches only the registers live where the region resumes, so a register its machine
2927            // words leave dead stays dead on their path.
2928            let live = split.map(|_| self.live_in(resume));
2929            for reg in 0..register_count as u16 {
2930                if Some(reg) != arrived
2931                    && live.as_ref().is_none_or(|l| l.contains(&reg))
2932                    && kind_at(&held[at], reg) == Kind::BigInt
2933                    && integer(kind_at(&on_words[at], reg))
2934                {
2935                    self.line(&format!("r[{reg}] = lc_xof(r[{reg}]);"));
2936                }
2937            }
2938            let (fw, fh) = (&words_frames[&pc], &held_frames[&pc]);
2939            if fw.len() != fh.len() {
2940                return Err(format!(
2941                    "pc {pc}: the two versions of the region iterate {} and {} loops",
2942                    fw.len(),
2943                    fh.len()
2944                ));
2945            }
2946            if let Some(fi) = split {
2947                let (fw, fh) = (fw.clone(), fh.clone());
2948                self.hand_over(fi, resume, &fw, &fh, hot_owns);
2949                continue;
2950            }
2951            for (w, h) in fw.iter().zip(fh) {
2952                self.line(&format!("_itv{h} = _itv{w}; _it{h} = _it{w};"));
2953            }
2954            self.line(&format!("goto X{resume};"));
2955        }
2956        Ok(())
2957    }
2958
2959    /// The registers live where the op at `pc` of the current region begins: those it reads, and those live after it
2960    /// that it does not write.
2961    fn live_in(&self, pc: usize) -> std::collections::BTreeSet<u16> {
2962        let op = &self.prog.code[pc];
2963        let mut live = self.live_out[pc - self.region_span.0].clone();
2964        self.for_each_written(op, |w| {
2965            live.remove(&w);
2966        });
2967        op.for_each_read_reg(
2968            |f| self.prog.functions[f as usize].captures.len(),
2969            |r| {
2970                live.insert(r);
2971            },
2972        );
2973        live
2974    }
2975
2976    /// A word entry's way into its region held exact, resuming at `resume`: the registers live there and their owned
2977    /// flags into the spill buffers, each loop the machine words iterate (`words`) with the one the region held exact
2978    /// takes it over in (`held`), and the call to `fn_N_x` that runs the rest, whose result is the word entry's.
2979    fn hand_over(&mut self, fi: usize, resume: usize, words: &[usize], held: &[usize], owns: bool) {
2980        for reg in self.live_in(resume) {
2981            self.line(&format!("lc_held_regs[{reg}] = r[{reg}];"));
2982            if owns {
2983                self.line(&format!("lc_held_owns[{reg}] = own[{reg}];"));
2984            }
2985        }
2986        for (k, w) in words.iter().enumerate() {
2987            self.line(&format!(
2988                "lc_held_loops[{}] = (int64_t)(intptr_t)_itv{w}; lc_held_loops[{}] = (int64_t)_it{w};",
2989                2 * k,
2990                2 * k + 1
2991            ));
2992        }
2993        let id = self.held_entries.len();
2994        self.held_entries
2995            .push((id, held.to_vec(), resume, Vec::new()));
2996        self.line(&format!(
2997            "return fn_{fi}_x({id}, {}, __ret_own);",
2998            u8::from(owns)
2999        ));
3000    }
3001
3002    /// Emit one version of region `[start, end)` over its kinds `per_pc`, every op `reachable` reaches, under the
3003    /// current label prefix; returns the ids of the loops iterating at each op, innermost last.
3004    fn emit_version(
3005        &mut self,
3006        start: usize,
3007        end: usize,
3008        in_function: bool,
3009        holes: &[(usize, usize)],
3010        per_pc: &[Vec<Kind>],
3011        reachable: &std::collections::HashSet<usize>,
3012    ) -> Result<std::collections::HashMap<usize, Vec<usize>>, String> {
3013        self.iter_stack.clear();
3014        let mut frames = std::collections::HashMap::new();
3015        for pc in start..end {
3016            // An embedded inline-closure body is emitted as its own region — the enclosing block jumps over it —
3017            // and an op no path reaches (an arm for a constructor the value can never be) is emitted not at all.
3018            if in_hole(pc, holes) || !reachable.contains(&pc) {
3019                continue;
3020            }
3021            if self.region_targets.contains(&pc) {
3022                writeln!(self.out, "{}{pc}:;", self.label).unwrap();
3023            }
3024            // The bytecode tier's guarded AddAssign is already unchecked on its word version. On
3025            // measured sparse predicates, replacing its predictable branch with an add on every
3026            // iteration is slower; expose that profitability refusal instead of silently pretending
3027            // this Rust/direct-C transform was unavailable.
3028            if self.label == 'L'
3029                && crate::optimize::active_config()
3030                    .is_on(crate::optimization::Opt::BranchlessAccumulate)
3031            {
3032                if let (
3033                    Some(Op::JumpIfFalse { target, .. }),
3034                    Some(Op::LoadConst { dst: delta, idx }),
3035                    Some(Op::AddAssign { src, .. }),
3036                ) = (
3037                    self.prog.code.get(pc),
3038                    self.prog.code.get(pc + 1),
3039                    self.prog.code.get(pc + 2),
3040                ) {
3041                    if *target == pc + 3
3042                        && delta == src
3043                        && matches!(
3044                            self.prog.constants.get(*idx as usize),
3045                            Some(Constant::Int(_))
3046                        )
3047                    {
3048                        crate::optimize::mark_backend_decline(
3049                            crate::optimization::Opt::BranchlessAccumulate,
3050                            "branchlessaccumulate: retained the faster predictable bytecode branch",
3051                        );
3052                    }
3053                }
3054            }
3055            frames.insert(pc, self.iter_stack.iter().map(|f| f.id).collect());
3056            // `before` = operand kinds entering this op; `after` = kinds once its write lands (lets a
3057            // `NewEmptyList` read its own fixpoint-refined element kind).
3058            let before = per_pc[pc - start].clone();
3059            let after = per_pc[pc - start + 1].clone();
3060            let op = self.prog.code[pc].clone();
3061            self.try_emit_byte_compare_window(pc, end, &before);
3062            // A definition of an exact web lowers as what it computes, then lands as a cell.
3063            // A definition a number join lands lowers as what it computes, then lands converted; a collection changed
3064            // in place takes its item converted as it goes in.
3065            let version = self.on_words.get() as usize;
3066            let natural = self.tower_from.borrow()[version].get(&pc).copied();
3067            let number = self.number_from.borrow()[version].get(&pc).copied();
3068            let mut computed = after.clone();
3069            let mut landing = None;
3070            op.for_each_written_reg(|w| landing = Some(w));
3071            if let Some(k) = natural.or(number.map(|(from, _)| from)) {
3072                if let Some(slot) = landing.and_then(|w| computed.get_mut(w as usize)) {
3073                    *slot = k;
3074                }
3075            }
3076            let lowered = if self.tower { self.lower_tower(&op, &before, pc)? } else { None };
3077            if lowered.is_none() {
3078                self.lower_over_exact(&op, &before, &computed, in_function, pc)
3079                    .map_err(|e| format!("pc {pc}: {e}"))?;
3080            }
3081            if let (Some(k), Some(w), false) = (natural, landing, lowered == Some(true)) {
3082                let cell = self.tower_of(&format!("r[{w}]"), k).map_err(|e| format!("pc {pc}: {e}"))?;
3083                self.line(&format!("r[{w}] = (int64_t)(intptr_t)({cell});"));
3084            }
3085            if let (Some((from, to)), Some(w)) = (number, landing) {
3086                let v = self.convert_number(&format!("r[{w}]"), from, to).map_err(|e| format!("pc {pc}: {e}"))?;
3087                self.line(&format!("r[{w}] = {v};"));
3088            }
3089            // A call through a word entry tests its escape flag itself, and its machine word carries no tag.
3090            let tested = std::mem::take(&mut self.twin_arrival);
3091            if self.arrivals.contains(&pc) && !tested {
3092                // An exact value that arrived on machine words: a small one's word, or the region held exact.
3093                let mut dst = 0;
3094                op.for_each_written_reg(|w| dst = w);
3095                self.line(&format!("if (r[{dst}] & 1) goto A{pc};"));
3096                self.line(&format!("r[{dst}] >>= 1;"));
3097            }
3098            self.write_owned_flags(&op, &after, pc);
3099        }
3100        Ok(frames)
3101    }
3102
3103    /// Lower `op`, an operation of the region on machine words that moves into the region held exact (`D{pc}`) when
3104    /// its result overflows the word. A product by a constant is the machine's own, guarded by the range of the other
3105    /// operand it cannot overflow on — a test beside the value's path, so a `3 * k + 1` stays one `lea`. Anything else
3106    /// computes with the overflow check, a constant operand written in as an immediate: the range a sum or difference
3107    /// with a constant cannot overflow on ends within that constant of i64's ends, a bound no x86 compare takes as an
3108    /// immediate, so gcc would hold it in a register for the whole function, while the flag of the `add`/`sub` that
3109    /// computes the value costs nothing beside it.
3110    fn lower_checked(
3111        &mut self,
3112        op: &Op,
3113        kinds: &[Kind],
3114        after: &[Kind],
3115        in_function: bool,
3116        pc: usize,
3117    ) -> Result<(), String> {
3118        let word = |v: i128| {
3119            if v == i64::MIN as i128 {
3120                "INT64_MIN".to_string()
3121            } else {
3122                format!("{v}LL")
3123            }
3124        };
3125        let known = |reg: u16| self.const_operand(pc, reg).map(i128::from);
3126        let operand = |reg: u16| known(reg).map_or_else(|| format!("r[{reg}]"), word);
3127        let checked = |f: &str, dst: u16, a: u16, b: u16| {
3128            format!(
3129                "{{ int64_t _o; if ({f}({}, {}, &_o)) goto D{pc}; r[{dst}] = _o; }}",
3130                operand(a),
3131                operand(b)
3132            )
3133        };
3134        let line = match *op {
3135            Op::Add { dst, lhs, rhs } => checked("__builtin_add_overflow", dst, lhs, rhs),
3136            Op::Sub { dst, lhs, rhs } => checked("__builtin_sub_overflow", dst, lhs, rhs),
3137            Op::AddAssign { dst, src } => checked("__builtin_add_overflow", dst, dst, src),
3138            Op::Pow { dst, lhs, rhs } => checked("lc_ipow_ovf", dst, lhs, rhs),
3139            Op::Mul { dst, lhs, rhs } => {
3140                let by_constant = known(rhs)
3141                    .map(|c| (lhs, c))
3142                    .or_else(|| known(lhs).map(|c| (rhs, c)));
3143                let Some((x, c)) = by_constant else {
3144                    self.line(&checked("__builtin_mul_overflow", dst, lhs, rhs));
3145                    return Ok(());
3146                };
3147                let (lo, hi) = product_domain(c);
3148                let guard = match (lo == i64::MIN as i128, hi == i64::MAX as i128) {
3149                    (true, true) => None,
3150                    (true, false) => Some(format!("r[{x}] > {}", word(hi))),
3151                    (false, true) => Some(format!("r[{x}] < {}", word(lo))),
3152                    (false, false) => Some(format!(
3153                        "(uint64_t)r[{x}] - (uint64_t){} > {}ULL",
3154                        word(lo),
3155                        (hi - lo) as u64
3156                    )),
3157                };
3158                if let Some(g) = guard {
3159                    self.line(&format!("if ({g}) goto D{pc};"));
3160                }
3161                format!("r[{dst}] = r[{lhs}] * r[{rhs}];")
3162            }
3163            // A quotient outgrows the word only as `INT64_MIN / -1`.
3164            Op::Div { lhs, rhs, .. } | Op::FloorDiv { lhs, rhs, .. } => {
3165                self.line(&format!(
3166                    "if (r[{rhs}] == -1 && r[{lhs}] == INT64_MIN) goto D{pc};"
3167                ));
3168                return self.lower_op(op, kinds, after, in_function, pc);
3169            }
3170            _ => return Err(format!("an overflow check on {}", op.name())),
3171        };
3172        self.line(&line);
3173        Ok(())
3174    }
3175
3176    /// The integer register `reg` holds at `pc` when its one definition reaching `pc` is a constant: the last write to
3177    /// it before `pc` is a `LoadConst` of an `Int`, and no jump lands between them.
3178    fn const_operand(&self, pc: usize, reg: u16) -> Option<i64> {
3179        let (start, _) = self.region_span;
3180        let p = (start..pc).rev().find(|p| {
3181            let mut writes = false;
3182            self.for_each_written(&self.prog.code[*p], |w| writes |= w == reg);
3183            writes
3184        })?;
3185        if (p + 1..=pc).any(|q| self.region_targets.contains(&q)) {
3186            return None;
3187        }
3188        match &self.prog.code[p] {
3189            Op::LoadConst { idx, .. } => match &self.prog.constants[*idx as usize] {
3190                Constant::Int(v) => Some(*v),
3191                _ => None,
3192            },
3193            _ => None,
3194        }
3195    }
3196
3197    /// The operands of `op`, a numeric operator, that are popped values (an optional, present or `nothing`) the op
3198    /// reads as the number they hold, each with that number's kind and the VM's sentence for `nothing` there.
3199    fn optional_operands(&self, op: &Op, kinds: &[Kind]) -> Vec<(u16, Kind, String)> {
3200        use crate::ast::stmt::BinaryOpKind as B;
3201        let (kind, lhs, rhs) = match *op {
3202            Op::Add { lhs, rhs, .. } => (B::Add, lhs, rhs),
3203            Op::AddAssign { dst, src } => (B::Add, dst, src),
3204            Op::Sub { lhs, rhs, .. } => (B::Subtract, lhs, rhs),
3205            Op::Mul { lhs, rhs, .. } => (B::Multiply, lhs, rhs),
3206            Op::Div { lhs, rhs, .. } => (B::Divide, lhs, rhs),
3207            Op::ExactDiv { lhs, rhs, .. } => (B::ExactDivide, lhs, rhs),
3208            Op::FloorDiv { lhs, rhs, .. } => (B::FloorDivide, lhs, rhs),
3209            Op::Mod { lhs, rhs, .. } => (B::Modulo, lhs, rhs),
3210            Op::Pow { lhs, rhs, .. } => (B::Pow, lhs, rhs),
3211            Op::Lt { lhs, rhs, .. } => (B::Lt, lhs, rhs),
3212            Op::Gt { lhs, rhs, .. } => (B::Gt, lhs, rhs),
3213            Op::LtEq { lhs, rhs, .. } => (B::LtEq, lhs, rhs),
3214            Op::GtEq { lhs, rhs, .. } => (B::GtEq, lhs, rhs),
3215            Op::BitAnd { lhs, rhs, .. } => (B::BitAnd, lhs, rhs),
3216            Op::BitOr { lhs, rhs, .. } => (B::BitOr, lhs, rhs),
3217            Op::BitXor { lhs, rhs, .. } => (B::BitXor, lhs, rhs),
3218            Op::Shl { lhs, rhs, .. } => (B::Shl, lhs, rhs),
3219            Op::Shr { lhs, rhs, .. } => (B::Shr, lhs, rhs),
3220            _ => return Vec::new(),
3221        };
3222        let number = |k: Kind| matches!(k, Kind::Int | Kind::BigInt | Kind::Float | Kind::Rational);
3223        let held = |r: u16| match kind_at(kinds, r) {
3224            Kind::Optional(oi) => Some(self.opt_inner(oi)).filter(|k| number(*k)),
3225            _ => None,
3226        };
3227        let plain = |r: u16| held(r).unwrap_or_else(|| kind_at(kinds, r));
3228        let mut out: Vec<(u16, Kind, String)> = Vec::new();
3229        for (reg, other, nothing_left) in [(lhs, rhs, true), (rhs, lhs, false)] {
3230            let Some(inner) = held(reg) else { continue };
3231            if out.iter().any(|(r, _, _)| *r == reg) || !number(plain(other)) {
3232                continue;
3233            }
3234            let Some(sentence) =
3235                sample_value(plain(other)).and_then(|v| words::nothing_refused(kind, nothing_left, v))
3236            else {
3237                continue;
3238            };
3239            out.push((reg, inner, sentence));
3240        }
3241        out
3242    }
3243
3244    /// Lower `op` where exact integers meet it. An op that reads an exact operand as one sees it as it is; any
3245    /// other sees the machine integer it holds (loudly, if it holds none), and the register gets its exact value
3246    /// back after. A result the region holds exact that the op makes as a machine integer is tagged once it lands.
3247    fn lower_over_exact(
3248        &mut self,
3249        op: &Op,
3250        before: &[Kind],
3251        after: &[Kind],
3252        in_function: bool,
3253        pc: usize,
3254    ) -> Result<(), String> {
3255        // A popped value the op reads as a number holds it, or stops the program as the VM does over `nothing`.
3256        let presented = self.optional_operands(op, before);
3257        let mut opened = Vec::new();
3258        let mut present_kinds;
3259        let before = if presented.is_empty() {
3260            before
3261        } else {
3262            present_kinds = before.to_vec();
3263            for (reg, inner, sentence) in &presented {
3264                let n = self.fresh();
3265                self.line(&format!(
3266                    "int64_t _op{n} = r[{reg}]; if (!((int64_t*)(intptr_t)_op{n})[0]) logos_fatalf({sentence}); r[{reg}] = ((int64_t*)(intptr_t)_op{n})[1];"
3267                ));
3268                present_kinds[*reg as usize] = *inner;
3269                opened.push((*reg, n));
3270            }
3271            &present_kinds[..]
3272        };
3273        let lowered = if self.deopt.contains(&pc) {
3274            self.lower_checked(op, before, after, in_function, pc)
3275        } else {
3276            self.lower_over_present(op, before, after, in_function, pc)
3277        };
3278        let mut written = Vec::new();
3279        op.for_each_written_reg(|w| written.push(w));
3280        for (reg, n) in opened {
3281            if !written.contains(&reg) {
3282                self.line(&format!("r[{reg}] = _op{n};"));
3283            }
3284        }
3285        lowered
3286    }
3287
3288    /// [`Emitter::lower_over_exact`] once every popped value the op reads is presented as the number it holds.
3289    fn lower_over_present(
3290        &mut self,
3291        op: &Op,
3292        before: &[Kind],
3293        after: &[Kind],
3294        in_function: bool,
3295        pc: usize,
3296    ) -> Result<(), String> {
3297        let mut read = Vec::new();
3298        op.for_each_read_reg(
3299            |f| self.prog.functions[f as usize].captures.len(),
3300            |r| read.push(r),
3301        );
3302        read.sort_unstable();
3303        read.dedup();
3304        let narrowed: Vec<u16> = read
3305            .into_iter()
3306            .filter(|r| kind_at(before, *r) == Kind::BigInt && !reads_exact_reg(op, before, *r))
3307            .collect();
3308        let mut written = Vec::new();
3309        op.for_each_written_reg(|w| written.push(w));
3310        let tagged: Vec<u16> = if self.promoted.borrow()[self.on_words.get() as usize].contains(&pc)
3311            && !makes_exact(op)
3312            && !self.arrives_exact(op, before)
3313        {
3314            written
3315                .iter()
3316                .copied()
3317                .filter(|w| kind_at(after, *w) == Kind::BigInt)
3318                .collect()
3319        } else {
3320            Vec::new()
3321        };
3322        if narrowed.is_empty() && tagged.is_empty() {
3323            return self.lower_op(op, before, after, in_function, pc);
3324        }
3325        let mut kinds = before.to_vec();
3326        let mut saved = Vec::new();
3327        for reg in &narrowed {
3328            kinds[*reg as usize] = Kind::Int;
3329            let n = self.fresh();
3330            let narrow = match index_too_wide(op, before, *reg) {
3331                Some(why) => format!("lc_xindex(_xs{n}, {})", words::index_too_wide(why)),
3332                None => format!("lc_xnarrow(_xs{n})"),
3333            };
3334            self.line(&format!("int64_t _xs{n} = r[{reg}]; r[{reg}] = {narrow};"));
3335            saved.push((*reg, n));
3336        }
3337        let mut made = after.to_vec();
3338        for reg in &tagged {
3339            made[*reg as usize] = Kind::Int;
3340        }
3341        self.lower_op(op, &kinds, &made, in_function, pc)?;
3342        for (reg, n) in saved {
3343            if !written.contains(&reg) {
3344                self.line(&format!("r[{reg}] = _xs{n};"));
3345            }
3346        }
3347        for reg in tagged {
3348            self.line(&format!("r[{reg}] = lc_xof(r[{reg}]);"));
3349        }
3350        Ok(())
3351    }
3352
3353    /// The pcs of region `[start, end)` some path from its entry reaches. A branch on an arm test that cannot
3354    /// succeed — the value's kind rules the constructor out — follows only its failing edge.
3355    fn reachable(
3356        &self,
3357        start: usize,
3358        end: usize,
3359        holes: &[(usize, usize)],
3360        per_pc: &[Vec<Kind>],
3361    ) -> std::collections::HashSet<usize> {
3362        let mut seen = std::collections::HashSet::new();
3363        let mut work = vec![start];
3364        while let Some(pc) = work.pop() {
3365            if pc < start || pc >= end || !seen.insert(pc) {
3366                continue;
3367            }
3368            let op = &self.prog.code[pc];
3369            let next = {
3370                let mut n = pc + 1;
3371                while n < end && in_hole(n, holes) {
3372                    n += 1;
3373                }
3374                n
3375            };
3376            let never = |cond: u16| -> bool {
3377                (start..pc)
3378                    .rev()
3379                    .take_while(|p| !matches!(self.prog.code[*p], Op::Jump { .. }))
3380                    .find_map(|p| match &self.prog.code[p] {
3381                        Op::TestArm {
3382                            dst,
3383                            target,
3384                            variant,
3385                        } if *dst == cond => Some(
3386                            self.static_arm_test(kind_at(&per_pc[p - start], *target), *variant)
3387                                == Some(false),
3388                        ),
3389                        other => {
3390                            let mut writes = false;
3391                            self.for_each_written(other, |w| writes |= w == cond);
3392                            writes.then_some(false)
3393                        }
3394                    })
3395                    == Some(true)
3396            };
3397            let yields_nothing = |dst: u16| kind_at(&per_pc[pc - start + 1], dst) == Kind::Never;
3398            let mut writes_nothing = false;
3399            op.for_each_written_reg(|w| writes_nothing |= yields_nothing(w));
3400            match op {
3401                Op::Return { .. } | Op::ReturnNothing | Op::Halt | Op::FailWith { .. } => {}
3402                // A loop over a sequence that holds nothing never enters; any other op whose result cannot
3403                // exist — an index into such a sequence, an operation the VM refuses for its operands' kinds —
3404                // always fails, and nothing after it runs.
3405                Op::IterNext { dst, exit } if yields_nothing(*dst) => work.push(*exit),
3406                _ if writes_nothing => {}
3407                Op::Jump { target } => work.push(*target),
3408                Op::JumpIfFalse { cond, target } if never(*cond) => work.push(*target),
3409                Op::JumpIfTrue { cond, .. } if never(*cond) => work.push(next),
3410                _ => {
3411                    if let Some(t) = jump_target(op) {
3412                        work.push(t);
3413                    }
3414                    work.push(next);
3415                }
3416            }
3417        }
3418        seen
3419    }
3420
3421    /// What an arm test decides before the program runs, when the tested value's kind decides it: `Some(false)`
3422    /// for a constructor the value can never be, `Some(true)` for a record tested against its own type's name.
3423    fn static_arm_test(&self, tk: Kind, variant: u32) -> Option<bool> {
3424        let name = self.const_text(variant)?;
3425        match tk {
3426            Kind::Enum(ei) => self.variant_index(ei, variant).is_none().then_some(false),
3427            Kind::Inductive(ii) => {
3428                let (ty, ctors) = self.inductive(ii);
3429                let possible = ctors.iter().any(|(c, ks)| c == name && ks.is_some());
3430                (self.ctor_tag(&ty, name).is_none() || !possible).then_some(false)
3431            }
3432            Kind::Struct(_) | Kind::StructSynth(_) => {
3433                Some(self.struct_name_of_kind(tk).as_deref() == Some(name))
3434            }
3435            Kind::Nothing => Some(name == "None"),
3436            Kind::Int | Kind::Bool | Kind::Float | Kind::Text | Kind::BigInt | Kind::Rational => {
3437                Some(false)
3438            }
3439            _ => None,
3440        }
3441    }
3442
3443    /// Whether a new sequence of kind `kind` in `dst` may empty the buffer the register last held alone, in place: a
3444    /// register the region tracks, every handle of which is a sequence of that one kind.
3445    fn reuses_list_buffer(&self, dst: u16, kind: Kind) -> bool {
3446        self.owned.contains(&dst) && self.handle_kinds.get(&dst) == Some(&kind)
3447    }
3448
3449    /// Keep the owned flags true after `op`: a register it wrote holds a collection it just made (flag 1), the
3450    /// handle its own lowering already accounted for (the target of an owned change), or a handle that may be
3451    /// held elsewhere (flag 0); a register it read and may have left its handle in — another register, a
3452    /// field, an element, an iterator, a closure, a message — is no longer the only holder (flag 0).
3453    fn write_owned_flags(&mut self, op: &Op, after: &[Kind], pc: usize) {
3454        if self.owned.is_empty()
3455            || matches!(
3456                op,
3457                Op::Return { .. } | Op::ReturnNothing | Op::Halt | Op::FailWith { .. }
3458            )
3459        {
3460            return;
3461        }
3462        // RevSwap's lowering exchanges the ownership bits together with the
3463        // handles. Reclassifying its destinations as ordinary writes would
3464        // erase that proof and force the next NewEmptyList to allocate.
3465        if matches!(op, Op::RevSwap { .. }) {
3466            return;
3467        }
3468        let transfer = self.consumed_borrow_transfer(op);
3469        let mut kept: Vec<u16> = match (mutation(op), op) {
3470            (Some(Mutation::Owned(target)), _) => vec![target],
3471            (_, Op::AddAssign { dst, .. }) if kind_at(after, *dst) == Kind::Text => vec![*dst],
3472            (_, Op::Call { dst, .. } | Op::CallValue { dst, .. })
3473                if crate::optimize::active_config().is_on(crate::optimization::Opt::Borrow) =>
3474            {
3475                vec![*dst]
3476            }
3477            _ => Vec::new(),
3478        };
3479        if let Some((dst, arg)) = transfer {
3480            kept.push(dst);
3481            kept.push(arg);
3482        }
3483        // A copy out of a register that is dead from here on moves the collection rather than sharing it: the
3484        // destination holds it alone exactly when the source did.
3485        if let Op::Move { dst, src } = op {
3486            if self.owned.contains(src)
3487                && !self.owned.contains(dst)
3488                && crate::optimize::active_config().is_on(crate::optimization::Opt::Borrow)
3489                && self.feeds_readonly_call_arg(pc, *dst)
3490            {
3491                // The argument window merely borrows the handle.  Keep the
3492                // source register as its sole owner so a loop-local NewList in
3493                // that register can clear and refill the allocation next time.
3494                self.line("/* retain ownership across read-only call argument */");
3495                crate::optimize::mark_fired(crate::optimization::Opt::Borrow);
3496                return;
3497            }
3498            if self.owned.contains(dst)
3499                && self.owned.contains(src)
3500                && dst != src
3501                && self.dead_after(pc, *src)
3502            {
3503                self.line(&format!("own[{dst}] = own[{src}]; own[{src}] = 0;"));
3504                return;
3505            }
3506        }
3507        let taken = match op {
3508            Op::GetField { dst, .. } => Some(*dst).filter(|_| self.takes_out_field(pc)),
3509            Op::GlobalGet { dst, .. } => Some(*dst).filter(|_| self.takes_out_global(pc)),
3510            _ => None,
3511        };
3512        let inspected = inspected_regs(op);
3513        let fresh = fresh_regs(op, after);
3514        let mut written = Vec::new();
3515        self.for_each_written(op, |w| written.push(w));
3516        let mut read = Vec::new();
3517        op.for_each_read_reg(
3518            |f| self.prog.functions[f as usize].captures.len(),
3519            |r| read.push(r),
3520        );
3521        let mut escaped: Vec<u16> = read
3522            .into_iter()
3523            .filter(|r| {
3524                self.owned.contains(r)
3525                    && !written.contains(r)
3526                    && !inspected.contains(r)
3527                    && !kept.contains(r)
3528            })
3529            .collect();
3530        escaped.sort_unstable();
3531        escaped.dedup();
3532        for r in escaped {
3533            self.line(&format!("own[{r}] = 0;"));
3534        }
3535        written.sort_unstable();
3536        written.dedup();
3537        for w in written {
3538            if !self.owned.contains(&w) || kept.contains(&w) || taken == Some(w) {
3539                continue;
3540            }
3541            self.line(&format!(
3542                "own[{w}] = {};",
3543                if fresh.contains(&w) { 1 } else { 0 }
3544            ));
3545        }
3546        if let Some((dst, arg)) = transfer {
3547            if self.owned.contains(&dst) && self.owned.contains(&arg) && dst != arg {
3548                self.line("/* preserve sole ownership through consumed borrow */");
3549                self.line(&format!("own[{dst}] = own[{arg}]; own[{arg}] = 0;"));
3550                crate::optimize::mark_fired(crate::optimization::Opt::Borrow);
3551            }
3552        }
3553    }
3554
3555    /// Emit the VM's refusal of builtin `id` for arguments of kinds `ks`, taken from the builtin itself.
3556    fn builtin_refusal(
3557        &mut self,
3558        id: crate::semantics::builtins::BuiltinId,
3559        ks: &[Kind],
3560    ) -> Result<(), String> {
3561        let samples: Option<Vec<RuntimeValue>> = ks.iter().map(|k| sample_value(*k)).collect();
3562        let samples = samples.ok_or_else(|| format!("{id:?} over {ks:?}"))?;
3563        self.line(&format!(
3564            "logos_fatalf({});",
3565            words::builtin_refused(id, samples)
3566        ));
3567        Ok(())
3568    }
3569
3570    /// The C expression of register `reg`'s owned flag: `own[reg]` for a register the region tracks, `0` for one it
3571    /// does not (whose value may be held anywhere).
3572    fn owned_flag(&self, reg: u16) -> String {
3573        if self.owned.contains(&reg) {
3574            format!("own[{reg}]")
3575        } else {
3576            "0".to_string()
3577        }
3578    }
3579
3580    /// Release scalar-sequence boxes this function still owns when it returns, except for the
3581    /// register transferred to its caller. Rust drops the equivalent unique `Vec`s automatically;
3582    /// bytecode C used to retain every recursive temporary until process exit. `own[r]` is already
3583    /// the emitter's copy-on-write proof that no other live handle aliases this allocation, and
3584    /// `handle_kinds` limits this first reclamation step to registers whose every value has the
3585    /// same flat scalar-sequence representation (no nested element ownership to walk).
3586    fn release_owned_scalar_sequences(&mut self, except: Option<u16>) {
3587        if !crate::optimize::active_config().is_on(crate::optimization::Opt::Unbox) {
3588            return;
3589        }
3590        // `store_inout_params` has just handed these handles back through the
3591        // caller's pointer slots. They escape even though this frame's own[]
3592        // proof still says the callee was their sole owner.
3593        let inout = self
3594            .current_fn
3595            .map(|fi| self.inout_regs(fi))
3596            .unwrap_or_default();
3597        let drops: Vec<(u16, Kind)> = self
3598            .owned
3599            .iter()
3600            .copied()
3601            .filter(|reg| Some(*reg) != except)
3602            .filter(|reg| !inout.contains(reg))
3603            .filter_map(|reg| self.handle_kinds.get(&reg).copied().map(|kind| (reg, kind)))
3604            .filter(|(_, kind)| matches!(kind, Kind::SeqInt | Kind::SeqFloat | Kind::SeqBool))
3605            .collect();
3606        for (reg, kind) in drops {
3607            let ty = kind
3608                .seq_c_type()
3609                .expect("a scalar sequence has a C representation");
3610            let n = self.fresh();
3611            self.line(&format!(
3612                "if (own[{reg}] && r[{reg}]) {{ {ty} *_drop{n} = ({ty}*)(intptr_t)r[{reg}]; lc_gc_free(_drop{n}->data); lc_gc_free(_drop{n}); own[{reg}] = 0; }} /* unique local drop */"
3613            ));
3614            crate::optimize::mark_fired(crate::optimization::Opt::Unbox);
3615        }
3616    }
3617
3618    /// Before an owned change to register `target` (of kind `k`): unless the register is the only holder of its
3619    /// collection, give it a copy of its own — the VM's copy-on-write. A `mutable` parameter arrives its caller's
3620    /// own (the call site isolates it) and changes in place until another holder shares it; its final value goes
3621    /// back to the caller whichever it is. A CRDT changes in place, every holder sharing it by design.
3622    fn own_target(&mut self, target: u16, k: Kind) -> Result<(), String> {
3623        if matches!(k, Kind::CrdtCounter) {
3624            return Ok(());
3625        }
3626        let mark = self.out.len();
3627        self.line(&format!("if (!own[{target}]) {{"));
3628        match self.clone_collection_expr(target, k)? {
3629            Some(copy) => {
3630                self.line(&format!("r[{target}] = {copy};"));
3631                self.line(&format!("own[{target}] = 1;"));
3632                self.line("}");
3633            }
3634            None => self.out.truncate(mark),
3635        }
3636        Ok(())
3637    }
3638
3639    /// The registers live after each op of region `[start, end)`: a backward fixpoint over its control flow (a
3640    /// fall-through, a jump, a branch's two ways, a loop's exit), skipping the embedded closure bodies.
3641    fn liveness(
3642        &self,
3643        start: usize,
3644        end: usize,
3645        holes: &[(usize, usize)],
3646    ) -> Vec<std::collections::BTreeSet<u16>> {
3647        use std::collections::BTreeSet;
3648        let n = end - start;
3649        let succs = |pc: usize| self.successors(pc, start, end, holes);
3650        let mut live_in: Vec<BTreeSet<u16>> = vec![BTreeSet::new(); n];
3651        let mut live_out: Vec<BTreeSet<u16>> = vec![BTreeSet::new(); n];
3652        loop {
3653            let mut changed = false;
3654            for pc in (start..end).rev() {
3655                if in_hole(pc, holes) {
3656                    continue;
3657                }
3658                let mut out = BTreeSet::new();
3659                for s in succs(pc) {
3660                    out.extend(live_in[s - start].iter().copied());
3661                }
3662                let op = &self.prog.code[pc];
3663                let mut inn = out.clone();
3664                self.for_each_written(op, |w| {
3665                    inn.remove(&w);
3666                });
3667                op.for_each_read_reg(
3668                    |f| self.prog.functions[f as usize].captures.len(),
3669                    |r| {
3670                        inn.insert(r);
3671                    },
3672                );
3673                if out != live_out[pc - start] || inn != live_in[pc - start] {
3674                    live_out[pc - start] = out;
3675                    live_in[pc - start] = inn;
3676                    changed = true;
3677                }
3678            }
3679            if !changed {
3680                return live_out;
3681            }
3682        }
3683    }
3684
3685    /// The pcs control can pass to from the op at `pc`, within region `[start, end)` and around its holes.
3686    fn successors(
3687        &self,
3688        pc: usize,
3689        start: usize,
3690        end: usize,
3691        holes: &[(usize, usize)],
3692    ) -> Vec<usize> {
3693        let op = &self.prog.code[pc];
3694        let mut out = Vec::new();
3695        match op {
3696            Op::Return { .. } | Op::ReturnNothing | Op::Halt | Op::FailWith { .. } => {}
3697            Op::Jump { target } => out.push(*target),
3698            _ => {
3699                if let Some(t) = jump_target(op) {
3700                    out.push(t);
3701                }
3702                let mut next = pc + 1;
3703                while next < end && in_hole(next, holes) {
3704                    next += 1;
3705                }
3706                out.push(next);
3707            }
3708        }
3709        out.retain(|p| *p >= start && *p < end);
3710        out
3711    }
3712
3713    /// Whether register `reg` is dead after the op at `pc`: no path from there reads it before writing it.
3714    fn dead_after(&self, pc: usize, reg: u16) -> bool {
3715        let (start, _) = self.region_span;
3716        self.live_out
3717            .get(pc - start)
3718            .is_some_and(|live| !live.contains(&reg))
3719    }
3720
3721    /// Whether `reg` is dead on entry to `pc`. This differs from asking about
3722    /// the previous instruction in a loop: that instruction's live-out set
3723    /// follows its back edge, while a synthesized fast path can jump directly
3724    /// to the loop exit.
3725    fn dead_before(&self, pc: usize, reg: u16) -> bool {
3726        let (start, _) = self.region_span;
3727        let Some(live_out) = self.live_out.get(pc - start) else {
3728            return false;
3729        };
3730        let mut live = live_out.clone();
3731        self.for_each_written(&self.prog.code[pc], |written| {
3732            live.remove(&written);
3733        });
3734        self.prog.code[pc].for_each_read_reg(
3735            |function| self.prog.functions[function as usize].captures.len(),
3736            |read| {
3737                live.insert(read);
3738            },
3739        );
3740        !live.contains(&reg)
3741    }
3742
3743    /// Inject the bytecode-C counterpart of the Rust/direct-C fixed-window
3744    /// text comparison.  The original scalar bytecode remains immediately
3745    /// below this guard as the exact fallback; the fast path is taken only for
3746    /// an owned ASCII text builder, an ASCII needle window, and in-bounds word
3747    /// indices.  This keeps Unicode, bounds failures, and exact-integer edge
3748    /// cases on the ordinary lowering while removing per-character Text
3749    /// allocation from the benchmark's hot path.
3750    fn try_emit_byte_compare_window(&mut self, pc: usize, end: usize, before: &[Kind]) -> bool {
3751        if self.label != 'L'
3752            || !crate::optimize::active_config().is_on(crate::optimization::Opt::Simd)
3753            || pc + 17 > end
3754        {
3755            return false;
3756        }
3757
3758        let op = |offset: usize| &self.prog.code[pc + offset];
3759        let int_const = |idx: u32| match self.prog.constants.get(idx as usize) {
3760            Some(Constant::Int(value)) => Some(*value),
3761            _ => None,
3762        };
3763        let indexed = |op: &Op| match op {
3764            Op::Index {
3765                dst,
3766                collection,
3767                index,
3768            }
3769            | Op::IndexUnchecked {
3770                dst,
3771                collection,
3772                index,
3773            } => Some((*dst, *collection, *index)),
3774            _ => None,
3775        };
3776
3777        let (len_reg, len) = match op(0) {
3778            Op::LoadConst { dst, idx } => (*dst, int_const(*idx)),
3779            _ => return false,
3780        };
3781        let Some(len) = len.filter(|len| (1..=16).contains(len)) else {
3782            return false;
3783        };
3784        let (cond_reg, j_reg) = match op(1) {
3785            Op::Lt { dst, lhs, rhs } if *rhs == len_reg => (*dst, *lhs),
3786            _ => return false,
3787        };
3788        let exit = match op(2) {
3789            Op::JumpIfFalse { cond, target } if *cond == cond_reg && *target == pc + 17 => *target,
3790            _ => return false,
3791        };
3792        let (text_index, text_base) = match op(3) {
3793            Op::Add { dst, lhs, rhs } if *lhs == j_reg => (*dst, *rhs),
3794            Op::Add { dst, lhs, rhs } if *rhs == j_reg => (*dst, *lhs),
3795            _ => return false,
3796        };
3797        let Some((text_char, text, index)) = indexed(op(4)) else {
3798            return false;
3799        };
3800        if index != text_index {
3801            return false;
3802        }
3803        let one_reg = match op(5) {
3804            Op::LoadConst { dst, idx } if int_const(*idx) == Some(1) => *dst,
3805            _ => return false,
3806        };
3807        let needle_index = match op(6) {
3808            Op::Add { dst, lhs, rhs }
3809                if (*lhs == j_reg && *rhs == one_reg) || (*rhs == j_reg && *lhs == one_reg) =>
3810            {
3811                *dst
3812            }
3813            _ => return false,
3814        };
3815        let Some((needle_char, needle, index)) = indexed(op(7)) else {
3816            return false;
3817        };
3818        if index != needle_index {
3819            return false;
3820        }
3821        let mismatch = match op(8) {
3822            Op::NotEq { dst, lhs, rhs }
3823                if (*lhs == text_char && *rhs == needle_char)
3824                    || (*rhs == text_char && *lhs == needle_char) =>
3825            {
3826                *dst
3827            }
3828            _ => return false,
3829        };
3830        if !matches!(op(9), Op::JumpIfFalse { cond, target } if *cond == mismatch && *target == pc + 14)
3831        {
3832            return false;
3833        }
3834        let (flag_value_reg, flag_value) = match op(10) {
3835            Op::LoadConst { dst, idx } => (*dst, int_const(*idx)),
3836            _ => return false,
3837        };
3838        let Some(flag_value) = flag_value else {
3839            return false;
3840        };
3841        let flag = match op(11) {
3842            Op::Move { dst, src } if *src == flag_value_reg => *dst,
3843            _ => return false,
3844        };
3845        let break_reg = match op(12) {
3846            Op::LoadConst { dst, idx } if int_const(*idx) == Some(len) => *dst,
3847            _ => return false,
3848        };
3849        if !matches!(op(13), Op::Move { dst, src } if *dst == j_reg && *src == break_reg) {
3850            return false;
3851        }
3852        let inc_reg = match op(14) {
3853            Op::LoadConst { dst, idx } if int_const(*idx) == Some(1) => *dst,
3854            _ => return false,
3855        };
3856        if !matches!(op(15), Op::AddAssign { dst, src } if *dst == j_reg && *src == inc_reg)
3857            || !matches!(op(16), Op::Jump { target } if *target == pc)
3858        {
3859            return false;
3860        }
3861
3862        let (region_start, region_end) = self.region_span;
3863        if (region_start..region_end).any(|source| {
3864            !(pc..exit).contains(&source)
3865                && jump_target(&self.prog.code[source])
3866                    .is_some_and(|target| (pc + 1..exit).contains(&target))
3867        }) {
3868            return false;
3869        }
3870        let mut written = std::collections::BTreeSet::new();
3871        let mut flag_writes = 0;
3872        let mut index_writes = 0;
3873        for inner in pc..exit {
3874            self.for_each_written(&self.prog.code[inner], |reg| {
3875                written.insert(reg);
3876                flag_writes += usize::from(reg == flag);
3877                index_writes += usize::from(reg == j_reg);
3878            });
3879        }
3880        if [text, text_base, needle]
3881            .iter()
3882            .any(|reg| written.contains(reg))
3883            || flag_writes != 1
3884            || index_writes != 2
3885            || written
3886                .iter()
3887                .any(|reg| *reg != flag && !self.dead_before(exit, *reg))
3888        {
3889            return false;
3890        }
3891
3892        if kind_at(before, text) != Kind::Text
3893            || kind_at(before, needle) != Kind::Text
3894            || kind_at(before, text_base) != Kind::Int
3895            || !self.owned.contains(&text)
3896        {
3897            return false;
3898        }
3899
3900        let text_ptr = format!("((const unsigned char *)(intptr_t)r[{text}])");
3901        let needle_ptr = format!("((const unsigned char *)(intptr_t)r[{needle}])");
3902        let ascii_needle = (0..len)
3903            .map(|offset| format!("{needle_ptr}[{offset}] < 0x80"))
3904            .collect::<Vec<_>>()
3905            .join(" && ");
3906        let equal = (0..len)
3907            .map(|offset| {
3908                format!(
3909                    "{text_ptr}[(size_t)(r[{text_base}] - 1LL) + {offset}] == {needle_ptr}[{offset}]"
3910                )
3911            })
3912            .collect::<Vec<_>>()
3913            .join(" && ");
3914        self.line("/* shared fixed-window byte compare: guarded bytecode-C fast path */");
3915        self.line(&format!(
3916            "if (own[{text}] && lc_head(r[{text}])->ascii && lc_head(r[{text}])->len >= {len}u && r[{text_base}] >= 1LL && (uint64_t)(r[{text_base}] - 1LL) <= (uint64_t)(lc_head(r[{text}])->len - {len}u) && strlen((const char *)(intptr_t)r[{needle}]) >= {len}u && {ascii_needle}) {{"
3917        ));
3918        self.line(&format!("if (!({equal})) r[{flag}] = {flag_value}LL;"));
3919        self.line(&format!("goto L{exit};"));
3920        self.line("}");
3921        crate::optimize::mark_fired(crate::optimization::Opt::Simd);
3922        true
3923    }
3924
3925    /// `obj`'s field cell `cell` := `value`, under value semantics: the record becomes the register's own first,
3926    /// and the field-ownership bit records whether the record is now its field's only holder — true when
3927    /// `value` held the collection alone and is dead from here on.
3928    fn store_field(
3929        &mut self,
3930        obj: u16,
3931        ok: Kind,
3932        cell: usize,
3933        value: u16,
3934        value_kind: Kind,
3935        pc: usize,
3936    ) -> Result<(), String> {
3937        self.own_target(obj, ok)?;
3938        let stored = self.field_store_value(ok, cell, value, value_kind)?;
3939        self.line(&format!(
3940            "((int64_t*)(intptr_t)r[{obj}])[{cell}] = {stored};"
3941        ));
3942        if cell < 64 {
3943            let word = self
3944                .field_owner_word(obj, ok)
3945                .ok_or("a field store on a record of unknown layout")?;
3946            let sole = if self.owned.contains(&value) && self.dead_after(pc, value) {
3947                format!("((uint64_t)(own[{value}] != 0) << {cell})")
3948            } else {
3949                "0".to_string()
3950            };
3951            self.line(&format!("{word} = ({word} & ~(1ULL << {cell})) | {sole};"));
3952        }
3953        Ok(())
3954    }
3955
3956    /// After `dst` := the collection in field `cell` of the record in `obj`. When the read takes the collection
3957    /// out of the record, `dst` becomes its only holder exactly when the record was. Otherwise the record keeps
3958    /// holding it alone only if `dst` is never more than inspected and is dead wherever the field could change
3959    /// in place (a push into the field, a call the record reaches).
3960    fn read_field_collection(&mut self, dst: u16, obj: u16, ok: Kind, cell: usize, pc: usize) {
3961        let Some(word) = self.field_owner_word(obj, ok) else {
3962            return;
3963        };
3964        if cell >= 64 {
3965            return;
3966        }
3967        if self.takes_out_field(pc) {
3968            if self.owned.contains(&dst) {
3969                let record = if self.owned.contains(&obj) {
3970                    format!("own[{obj}]")
3971                } else {
3972                    "0".to_string()
3973                };
3974                self.line(&format!(
3975                    "own[{dst}] = {record} && (({word} >> {cell}) & 1);"
3976                ));
3977            }
3978        } else if !self.stays_an_inspection(dst) {
3979            self.line(&format!("{word} &= ~(1ULL << {cell});"));
3980        }
3981    }
3982
3983    /// Whether register `reg`'s handle, once read out of a record, can never be seen changed by a later change
3984    /// to that record's field: every read of it in the region is an inspection, and it is dead at every op that
3985    /// could change a field's collection in place.
3986    fn stays_an_inspection(&self, reg: u16) -> bool {
3987        let (start, end) = self.region_span;
3988        (start..end).all(|pc| {
3989            let op = &self.prog.code[pc];
3990            let mut read = false;
3991            op.for_each_read_reg(
3992                |f| self.prog.functions[f as usize].captures.len(),
3993                |r| read |= r == reg,
3994            );
3995            let inspected = !read || inspected_regs(op).contains(&reg);
3996            let changes_fields = matches!(
3997                op,
3998                Op::ListPushField { .. } | Op::Call { .. } | Op::CallValue { .. }
3999            );
4000            inspected && (!changes_fields || self.dead_after(pc, reg))
4001        })
4002    }
4003
4004    /// Whether the `GlobalGet` at `pc` takes its global's collection out: the ops that follow it (after constant
4005    /// loads) empty that global with `nothing` before anything else happens.
4006    fn takes_out_global(&self, pc: usize) -> bool {
4007        let Op::GlobalGet { idx, .. } = &self.prog.code[pc] else {
4008            return false;
4009        };
4010        let (_, end) = self.region_span;
4011        let mut loaded: std::collections::HashMap<u16, &Constant> =
4012            std::collections::HashMap::new();
4013        for at in pc + 1..end {
4014            if self.region_targets.contains(&at) {
4015                return false;
4016            }
4017            match &self.prog.code[at] {
4018                Op::LoadConst { dst, idx: c } => {
4019                    loaded.insert(*dst, &self.prog.constants[*c as usize]);
4020                }
4021                Op::GlobalSet { idx: g, src } => {
4022                    return g == idx && matches!(loaded.get(src), Some(Constant::Nothing))
4023                }
4024                _ => return false,
4025            }
4026        }
4027        false
4028    }
4029
4030    /// Whether the `GetField` at `pc` takes its field's collection out of the record: the ops that follow it (after
4031    /// constant loads) overwrite that field with `nothing` before anything else happens — the bytecode's
4032    /// take-out/put-back idiom for changing a collection that lives in a field, which leaves the register the
4033    /// field's only holder whenever the record was.
4034    fn takes_out_field(&self, pc: usize) -> bool {
4035        let Op::GetField { obj, field, .. } = &self.prog.code[pc] else {
4036            return false;
4037        };
4038        let Some(field_name) = self.const_text(*field) else {
4039            return false;
4040        };
4041        let (_, end) = self.region_span;
4042        let mut loaded: std::collections::HashMap<u16, &Constant> =
4043            std::collections::HashMap::new();
4044        for at in pc + 1..end {
4045            if self.region_targets.contains(&at) {
4046                return false;
4047            }
4048            match &self.prog.code[at] {
4049                Op::LoadConst { dst, idx } => {
4050                    loaded.insert(*dst, &self.prog.constants[*idx as usize]);
4051                }
4052                Op::SetIndex {
4053                    collection,
4054                    index,
4055                    value,
4056                }
4057                | Op::SetIndexUnchecked {
4058                    collection,
4059                    index,
4060                    value,
4061                } => {
4062                    let names_field =
4063                        matches!(loaded.get(index), Some(Constant::Text(t)) if t == field_name);
4064                    let clears = matches!(loaded.get(value), Some(Constant::Nothing));
4065                    return *collection == *obj && names_field && clears;
4066                }
4067                _ => return false,
4068            }
4069        }
4070        false
4071    }
4072    /// Select TextBytes calls whose result flows only through register moves
4073    /// into one for-each iterator. Those calls can keep the source text's
4074    /// byte pointer instead of allocating and expanding a Seq_i64.
4075    fn text_byte_iteration_plan(
4076        &self,
4077        start: usize,
4078        end: usize,
4079        holes: &[(usize, usize)],
4080        reachable: &std::collections::HashSet<usize>,
4081    ) -> (
4082        std::collections::HashSet<usize>,
4083        std::collections::HashSet<usize>,
4084    ) {
4085        use crate::semantics::builtins::BuiltinId;
4086
4087        if !crate::optimize::active_config().is_on(crate::optimization::Opt::TextBytesView) {
4088            return Default::default();
4089        }
4090
4091        let move_root = |pc: usize, mut reg: u16| {
4092            let mut before = pc;
4093            loop {
4094                let Some(def) = (start..before).rev().find(|at| {
4095                    let mut writes = false;
4096                    self.prog.code[*at].for_each_written_reg(|w| writes |= w == reg);
4097                    writes
4098                }) else {
4099                    return reg;
4100                };
4101                if (def + 1..=before).any(|at| self.region_targets.contains(&at)) {
4102                    return reg;
4103                }
4104                match self.prog.code[def] {
4105                    Op::Move { dst, src } if dst == reg => {
4106                        reg = src;
4107                        before = def;
4108                    }
4109                    _ => return reg,
4110                }
4111            }
4112        };
4113
4114        let mut calls = std::collections::HashSet::new();
4115        let mut iters = std::collections::HashSet::new();
4116        for pc in start..end {
4117            let Op::CallBuiltin {
4118                dst,
4119                builtin: BuiltinId::TextBytes,
4120                args_start,
4121                ..
4122            } = self.prog.code[pc]
4123            else {
4124                continue;
4125            };
4126            if !reachable.contains(&pc) || in_hole(pc, holes) {
4127                continue;
4128            }
4129
4130            let mut aliases = std::collections::BTreeSet::from([dst]);
4131            let source_root = move_root(pc, args_start);
4132            let sources = std::collections::BTreeSet::from([args_start, source_root]);
4133            let mut selected_iter = None;
4134            let mut valid = true;
4135
4136            for at in pc + 1..end {
4137                if in_hole(at, holes) || !reachable.contains(&at) {
4138                    continue;
4139                }
4140                let op = &self.prog.code[at];
4141                if selected_iter.is_none()
4142                    && (self.region_targets.contains(&at) || jump_target(op).is_some())
4143                {
4144                    valid = false;
4145                    break;
4146                }
4147
4148                let mut reads = std::collections::BTreeSet::new();
4149                op.for_each_read_reg(
4150                    |f| self.prog.functions[f as usize].captures.len(),
4151                    |r| {
4152                        reads.insert(r);
4153                    },
4154                );
4155                if reads.iter().any(|r| sources.contains(r)) {
4156                    valid = false;
4157                    break;
4158                }
4159                let alias_reads: Vec<u16> = reads
4160                    .iter()
4161                    .copied()
4162                    .filter(|r| aliases.contains(r))
4163                    .collect();
4164
4165                match op {
4166                    Op::Move { dst, src }
4167                        if aliases.contains(src) && alias_reads.as_slice() == [*src] =>
4168                    {
4169                        aliases.insert(*dst);
4170                    }
4171                    Op::IterPrepare { iterable }
4172                        if aliases.contains(iterable) && selected_iter.is_none() =>
4173                    {
4174                        selected_iter = Some(at);
4175                    }
4176                    _ if !alias_reads.is_empty() => {
4177                        valid = false;
4178                        break;
4179                    }
4180                    _ => {
4181                        op.for_each_written_reg(|w| {
4182                            aliases.remove(&w);
4183                        });
4184                    }
4185                }
4186            }
4187
4188            if valid {
4189                if let Some(iter_pc) = selected_iter {
4190                    calls.insert(pc);
4191                    iters.insert(iter_pc);
4192                }
4193            }
4194        }
4195        (calls, iters)
4196    }
4197
4198    /// A forward kind pass over region `[start, end)` (mirrors `codegen_riscv::infer_kinds`, scalar+seq subset).
4199    /// Returns `end - start + 1` register-kind vectors: entry `i` is the state BEFORE op `start+i`, and the
4200    /// last entry is the state AFTER the final op (so a creation site can read its post-write kind). A bounded
4201    /// forward FIXPOINT refines a `SeqUnknown` created empty to the concrete element kind a later `ListPush`
4202    /// establishes — seeding each pass's creation with the kind that slot reached in the previous pass.
4203    fn infer_kinds(
4204        &self,
4205        start: usize,
4206        end: usize,
4207        seed: Vec<Kind>,
4208        holes: &[(usize, usize)],
4209    ) -> Vec<Vec<Kind>> {
4210        // The region's exact webs grow with what holds a cell, and its number variables widen with the forms their
4211        // definitions make, until both hold still: each pass's kinds decide the webs and the joins, every definition of
4212        // an exact web makes a cell in the next, and every definition a join lands lands as its variable's kind.
4213        let mut forced = crate::vm::tower::Webs::default();
4214        let mut landings = numbers::Landings::default();
4215        for round in 0.. {
4216            let mut entry = seed.clone();
4217            for (r, k) in &landings.entries {
4218                if let Some(slot) = entry.get_mut(*r as usize) {
4219                    *slot = *k;
4220                }
4221            }
4222            for r in &forced.entries {
4223                if let Some(k) = entry.get_mut(*r as usize) {
4224                    *k = Kind::Tower;
4225                }
4226            }
4227            let kinds = self.infer_kinds_forced(start, end, entry.clone(), holes, &forced, &landings.defs);
4228            let next = if self.tower {
4229                self.tower_webs(start, end, holes, &kinds, &entry)
4230            } else {
4231                crate::vm::tower::Webs::default()
4232            };
4233            let mut next_landings = self.number_landings(start, end, holes, &kinds, &seed);
4234            if round == SETTLE_ROUNDS {
4235                next_landings = landings.clone();
4236                next_landings.refused.push(crate::vm::variables::Site::Op(start));
4237            }
4238            if next == forced && next_landings == landings {
4239                let converted = forced
4240                    .entries
4241                    .iter()
4242                    .filter_map(|r| {
4243                        let k = seed.get(*r as usize).copied().unwrap_or(Kind::Unknown);
4244                        (k != Kind::Tower).then_some((*r, k))
4245                    })
4246                    .collect();
4247                self.tower_entries.borrow_mut().insert(start, converted);
4248                let widened = landings
4249                    .entries
4250                    .iter()
4251                    .filter(|(r, _)| !forced.entries.contains(r))
4252                    .map(|(r, k)| (*r, seed.get(*r as usize).copied().unwrap_or(Kind::Unknown), *k))
4253                    .collect();
4254                self.number_entries.borrow_mut().insert(start, (widened, landings.refused));
4255                return kinds;
4256            }
4257            forced = next;
4258            landings = next_landings;
4259        }
4260        unreachable!("the rounds end when the webs and joins hold still")
4261    }
4262
4263    /// [`Emitter::infer_kinds`] with the definitions of `forced`'s exact webs making cells, and each definition of `lands`
4264    /// landing as its kind there.
4265    fn infer_kinds_forced(
4266        &self,
4267        start: usize,
4268        end: usize,
4269        seed: Vec<Kind>,
4270        holes: &[(usize, usize)],
4271        forced: &crate::vm::tower::Webs,
4272        lands: &std::collections::BTreeMap<usize, Kind>,
4273    ) -> Vec<Vec<Kind>> {
4274        let n = seed.len();
4275        let mut overrides = vec![Kind::Unknown; n];
4276        let mut per_pc: Vec<Vec<Kind>> = Vec::new();
4277        let set = |cur: &mut Vec<Kind>, reg: u16, k: Kind| {
4278            let i = reg as usize;
4279            if i < cur.len() {
4280                cur[i] = k;
4281            }
4282        };
4283        let kind_of =
4284            |cur: &[Kind], reg: u16| -> Kind { *cur.get(reg as usize).unwrap_or(&Kind::Unknown) };
4285        for _pass in 0..8 {
4286            let mut cur = seed.clone();
4287            per_pc = Vec::with_capacity(end - start + 1);
4288            let mut iter_elems: Vec<Kind> = Vec::new();
4289            // The most recent `TestArm` (target reg, enum index, variant index) — the arm a following `BindArm`
4290            // extracts a payload field from, so that field gets the variant's declared kind.
4291            let mut last_test: Option<(u16, u16, usize)> = None;
4292            // The most recent `TestArm` on an undeclared enum (target reg, constructor name) — the constructor a
4293            // following `BindArm` on it extracts a payload of.
4294            let mut last_test_ctor: Option<(u16, String)> = None;
4295            for pc in start..end {
4296                per_pc.push(cur.clone());
4297                // An embedded closure body's ops belong to a separate region — control jumps over them here, so
4298                // they contribute NO kind transfer to this region's register state (passthrough the hole).
4299                if in_hole(pc, holes) {
4300                    continue;
4301                }
4302                // An op that does not read exact integers as they are sees the machine integers they hold, as its
4303                // lowering does ([`Emitter::lower_over_exact`]): what it builds from them holds machine integers.
4304                // A popped value a numeric operator reads is the number it holds there ([`Emitter::optional_operands`]).
4305                let presented: Vec<(u16, Kind)> = self
4306                    .optional_operands(&self.prog.code[pc], &cur)
4307                    .into_iter()
4308                    .map(|(r, inner, _)| {
4309                        let held = kind_of(&cur, r);
4310                        set(&mut cur, r, inner);
4311                        (r, held)
4312                    })
4313                    .collect();
4314                let mut narrowed: Vec<u16> = Vec::new();
4315                self.prog.code[pc].for_each_read_reg(
4316                    |f| self.prog.functions[f as usize].captures.len(),
4317                    |r| {
4318                        if kind_of(&cur, r) == Kind::BigInt
4319                            && !reads_exact_reg(&self.prog.code[pc], &cur, r)
4320                            && !narrowed.contains(&r)
4321                        {
4322                            narrowed.push(r);
4323                        }
4324                    },
4325                );
4326                for r in &narrowed {
4327                    set(&mut cur, *r, Kind::Int);
4328                }
4329                // The (dst, kind) this op writes — or `None` for control flow / iterator bookkeeping.
4330                let write: Option<(u16, Kind)> = match &self.prog.code[pc] {
4331                    Op::LoadConst { dst, idx } => {
4332                        let k = match &self.prog.constants[*idx as usize] {
4333                            Constant::Int(_) | Constant::Duration(_) => Kind::Int,
4334                            Constant::Bool(_) => Kind::Bool,
4335                            Constant::Float(_) => Kind::Float,
4336                            Constant::Text(_) => Kind::Text,
4337                            // `nothing` — the unit value, riding the slot as `0`; a field seeded with it takes
4338                            // its real kind from its later `StructInsert` / `CrdtBump` / `CrdtResolve`.
4339                            Constant::Nothing => Kind::Nothing,
4340                            Constant::Date(_) => Kind::Date,
4341                            Constant::Moment(_) => Kind::Moment,
4342                            Constant::Time(_) => Kind::Time,
4343                            Constant::Span { .. } => Kind::Span,
4344                            _ => Kind::Unknown,
4345                        };
4346                        Some((*dst, k))
4347                    }
4348                    Op::Move { dst, src } => Some((*dst, kind_of(&cur, *src))),
4349                    // Add/Sub/Mul/Pow: the numeric join of the operands (see `arith_result_kind`).
4350                    Op::Add { dst, lhs, rhs } => {
4351                        let (lk, rk) = (kind_of(&cur, *lhs), kind_of(&cur, *rhs));
4352                        // A Text operand makes `+` concatenation (see `arith`).
4353                        let k = if lk == Kind::Text || rk == Kind::Text {
4354                            Kind::Text
4355                        } else {
4356                            self.arith_result_kind(lk, rk)
4357                        };
4358                        Some((*dst, k))
4359                    }
4360                    Op::Sub { dst, lhs, rhs }
4361                    | Op::Mul { dst, lhs, rhs }
4362                    | Op::Pow { dst, lhs, rhs } => Some((
4363                        *dst,
4364                        self.arith_result_kind(kind_of(&cur, *lhs), kind_of(&cur, *rhs)),
4365                    )),
4366                    // A quotient is no larger than its dividend, a remainder smaller than its modulus: each is exact
4367                    // only when that operand is.
4368                    Op::Div { dst, lhs, rhs } | Op::FloorDiv { dst, lhs, rhs } => Some((
4369                        *dst,
4370                        bounded_by(
4371                            join(kind_of(&cur, *lhs), kind_of(&cur, *rhs)),
4372                            kind_of(&cur, *lhs),
4373                        ),
4374                    )),
4375                    Op::Mod { dst, lhs, rhs } => Some((
4376                        *dst,
4377                        bounded_by(
4378                            join(kind_of(&cur, *lhs), kind_of(&cur, *rhs)),
4379                            kind_of(&cur, *rhs),
4380                        ),
4381                    )),
4382                    // A bitwise result is a machine integer, whatever its operands (`lc_xbitand` and kin).
4383                    Op::BitXor { dst, lhs, rhs }
4384                    | Op::BitAnd { dst, lhs, rhs }
4385                    | Op::BitOr { dst, lhs, rhs } => Some((
4386                        *dst,
4387                        match join(kind_of(&cur, *lhs), kind_of(&cur, *rhs)) {
4388                            Kind::Rational => Kind::Int,
4389                            joined => bounded_by(joined, Kind::Int),
4390                        },
4391                    )),
4392                    // A word shifts by an integer count and stays a word.
4393                    Op::Shl { dst, lhs, rhs } | Op::Shr { dst, lhs, rhs } => {
4394                        let lk = kind_of(&cur, *lhs);
4395                        Some((
4396                            *dst,
4397                            match (lk, join(lk, kind_of(&cur, *rhs))) {
4398                                (Kind::Word(_), _) => lk,
4399                                (_, Kind::Rational) => Kind::Int,
4400                                (_, joined) => bounded_by(joined, Kind::Int),
4401                            },
4402                        ))
4403                    }
4404                    Op::AddAssign { dst, src } => {
4405                        let (dk, sk) = (kind_of(&cur, *dst), kind_of(&cur, *src));
4406                        Some((
4407                            *dst,
4408                            if dk == Kind::Text || sk == Kind::Text {
4409                                Kind::Text
4410                            } else {
4411                                join(dk, sk)
4412                            },
4413                        ))
4414                    }
4415                    Op::Concat { dst, .. } | Op::FormatValue { dst, .. } => {
4416                        Some((*dst, Kind::Text))
4417                    }
4418                    Op::Lt { dst, .. }
4419                    | Op::Gt { dst, .. }
4420                    | Op::LtEq { dst, .. }
4421                    | Op::GtEq { dst, .. }
4422                    | Op::Eq { dst, .. }
4423                    | Op::NotEq { dst, .. }
4424                    | Op::ApproxEq { dst, .. }
4425                    | Op::Not { dst, .. }
4426                    | Op::Contains { dst, .. } => Some((*dst, Kind::Bool)),
4427                    Op::Length { dst, .. } => Some((*dst, Kind::Int)),
4428                    // Strength-reduced division by a constant: exact when its dividend is; a remainder by a
4429                    // constant is a machine integer, or the exact number a rational dividend makes.
4430                    Op::DivPow2 { dst, lhs, .. }
4431                    | Op::MagicDivU {
4432                        dst,
4433                        lhs,
4434                        mul_back: 0,
4435                        ..
4436                    } => Some((*dst, bounded_by(kind_of(&cur, *lhs), kind_of(&cur, *lhs)))),
4437                    Op::MagicDivU { dst, lhs, .. } if kind_of(&cur, *lhs) == Kind::Rational => Some((*dst, Kind::Rational)),
4438                    Op::MagicDivU { dst, .. } => Some((*dst, Kind::Int)),
4439                    // Exact division yields an exact rational (`7 / 2` → `7/2`), regardless of the Int operands.
4440                    Op::ExactDiv { dst, .. } => Some((*dst, Kind::Rational)),
4441                    Op::NewRange { dst, .. } => Some((*dst, Kind::SeqInt)),
4442                    Op::NewEmptyListI32 { dst } => Some((*dst, Kind::SeqInt)),
4443                    Op::NewEmptyList { dst }
4444                    | Op::NewEmptyListWithCapacity { dst, .. }
4445                    | Op::NewEmptyListFixed { dst, .. } => Some((*dst, Kind::SeqUnknown)),
4446                    Op::NewList { dst, start, count } => {
4447                        let k = if *count == 0 {
4448                            Kind::SeqUnknown
4449                        } else {
4450                            let items: Vec<Kind> = (0..*count).map(|i| kind_of(&cur, *start + i)).collect();
4451                            self.list_kind(&items).unwrap_or_else(|_| self.seq_kind_for(items[0]))
4452                        };
4453                        Some((*dst, k))
4454                    }
4455                    Op::ListPush { list, value } => {
4456                        Some((*list, self.seq_kind_for(kind_of(&cur, *value))))
4457                    }
4458                    // `Pop` yields an OPTIONAL (`Some(x)` if the seq was non-empty, else `nothing`), carrying the
4459                    // element kind so `Show` renders a present payload correctly.
4460                    Op::ListPop { list, dst } => Some((
4461                        *dst,
4462                        Kind::Optional(self.intern_opt(self.elem_kind(kind_of(&cur, *list)))),
4463                    )),
4464                    Op::NewEmptySet { dst } => Some((*dst, Kind::SetUnknown)),
4465                    Op::SetAdd { set, value } => Some((*set, kind_of(&cur, *value).set_of())),
4466                    Op::NewEmptyMap { dst } | Op::NewEmptyMapWithCapacity { dst, .. } => {
4467                        Some((*dst, Kind::MapUnknown))
4468                    }
4469                    // `SetIndex` on a MAP (`item k of m to v`) refines the map's (key,value) kinds in place; on
4470                    // a sequence it is an in-place element write that leaves the kind unchanged, save a rational
4471                    // stored among integers, which makes the list one of rationals (its variable lands as one).
4472                    Op::SetIndex {
4473                        collection,
4474                        index,
4475                        value,
4476                    }
4477                    | Op::SetIndexUnchecked {
4478                        collection,
4479                        index,
4480                        value,
4481                    } => {
4482                        let ck = kind_of(&cur, *collection);
4483                        if matches!(ck, Kind::Map(_) | Kind::MapUnknown) {
4484                            // A map that holds a value of the exact extension holds every value as one.
4485                            let held = match ck {
4486                                Kind::Map(mi) if self.map_pair(mi).1 == Kind::Tower => Kind::Tower,
4487                                _ => kind_of(&cur, *value),
4488                            };
4489                            Some((*collection, self.map_of(kind_of(&cur, *index), held)))
4490                        } else if kind_of(&cur, *value) == Kind::Rational
4491                            && matches!(self.elem_kind(ck), Kind::Int | Kind::BigInt)
4492                        {
4493                            Some((*collection, self.seq_kind_for(Kind::Rational)))
4494                        } else {
4495                            None
4496                        }
4497                    }
4498                    // A slice preserves the sequence kind; a seq-concat yields the (shared) element seq kind;
4499                    // set union/intersection yields the left set's kind.
4500                    Op::SliceOp {
4501                        dst, collection, ..
4502                    } => Some((*dst, kind_of(&cur, *collection))),
4503                    Op::SeqConcat { dst, lhs, rhs } => {
4504                        let lk = kind_of(&cur, *lhs);
4505                        Some((
4506                            *dst,
4507                            if lk == Kind::SeqUnknown {
4508                                kind_of(&cur, *rhs)
4509                            } else {
4510                                lk
4511                            },
4512                        ))
4513                    }
4514                    Op::UnionOp { dst, lhs, .. } | Op::IntersectOp { dst, lhs, .. } => {
4515                        Some((*dst, kind_of(&cur, *lhs)))
4516                    }
4517                    Op::Index {
4518                        dst,
4519                        collection,
4520                        index,
4521                    }
4522                    | Op::IndexUnchecked {
4523                        dst,
4524                        collection,
4525                        index,
4526                    } => {
4527                        let ck = kind_of(&cur, *collection);
4528                        // Positional tuple access: the element kind is the interned position at the constant
4529                        // index. A sequence yields its homogeneous element kind as before.
4530                        let k = if let Kind::Tuple(ti) = ck {
4531                            // A constant LOGOS index (1-based, or negative from the end) names one position of
4532                            // the interned layout; any index into a tuple whose positions agree names that kind.
4533                            let poss = self.tuple_positions(ti);
4534                            let at = self.const_int_at(start, pc, *index).and_then(|i| match i {
4535                                i if i >= 1 => Some((i - 1) as usize),
4536                                i if i <= -1 => poss.len().checked_sub(i.unsigned_abs() as usize),
4537                                _ => None,
4538                            });
4539                            match at.and_then(|p| poss.get(p).copied()) {
4540                                Some(k) => k,
4541                                None if !poss.is_empty() && poss.iter().all(|k| *k == poss[0]) => {
4542                                    poss[0]
4543                                }
4544                                None => Kind::Unknown,
4545                            }
4546                        } else if ck == Kind::Text {
4547                            // `item i of text` is the i-th character, itself a Text.
4548                            Kind::Text
4549                        } else if let Some((_, v)) = self.resolve_map_kv(ck) {
4550                            // `item k of m` yields the map's value kind (any kind — it rode the i64/str cell).
4551                            v
4552                        } else {
4553                            self.elem_kind(ck)
4554                        };
4555                        Some((*dst, k))
4556                    }
4557                    Op::Call {
4558                        dst,
4559                        func,
4560                        args_start,
4561                        ..
4562                    } => {
4563                        // A `mutable` parameter is the caller's own collection, passed by reference: after the
4564                        // call the argument register holds what the callee made of it — its declared kind.
4565                        if let Some(f) = self.prog.functions.get(*func as usize) {
4566                            for &p in &f.mutable_param_regs {
4567                                let pk = self
4568                                    .param_kinds
4569                                    .get(*func as usize)
4570                                    .and_then(|ks| ks.get(p as usize))
4571                                    .copied();
4572                                if let Some(pk) = pk.filter(|k| *k != Kind::Unknown) {
4573                                    set(&mut cur, *args_start + p, pk);
4574                                }
4575                            }
4576                        }
4577                        Some((
4578                            *dst,
4579                            self.fn_ret
4580                                .get(*func as usize)
4581                                .copied()
4582                                .unwrap_or(Kind::Unknown),
4583                        ))
4584                    }
4585                    // A closure value is an opaque heap pointer riding the i64 slot (like a struct/text pointer).
4586                    Op::MakeClosure { dst, .. } => Some((*dst, Kind::Int)),
4587                    // Calling a closure yields the join of every closure body's return kind (the dispatch targets).
4588                    Op::CallValue { dst, .. } => {
4589                        let k = self
4590                            .closure_bodies
4591                            .iter()
4592                            .map(|&b| self.fn_ret.get(b).copied().unwrap_or(Kind::Unknown))
4593                            .reduce(join)
4594                            .unwrap_or(Kind::Unknown);
4595                        Some((*dst, k))
4596                    }
4597                    // Scalar math/text builtins: Sqrt/ParseFloat→Float, Floor/Ceil/Round/ParseInt→Int (they
4598                    // land an i64), Abs→its argument's kind, Min/Max→the numeric join of the two args, Chr/Format→Text.
4599                    // Any other builtin stays Unknown here and is refused in `lower_op`.
4600                    Op::CallBuiltin {
4601                        dst,
4602                        builtin,
4603                        args_start,
4604                        ..
4605                    } => {
4606                        use crate::semantics::builtins::BuiltinId;
4607                        let a = kind_of(&cur, *args_start);
4608                        let k = match builtin {
4609                            BuiltinId::Sqrt | BuiltinId::ParseFloat => Kind::Float,
4610                            BuiltinId::Floor
4611                            | BuiltinId::Ceil
4612                            | BuiltinId::Round
4613                            | BuiltinId::ParseInt => Kind::Int,
4614                            BuiltinId::Abs => a,
4615                            BuiltinId::Min | BuiltinId::Max => {
4616                                join(a, kind_of(&cur, *args_start + 1))
4617                            }
4618                            BuiltinId::Chr | BuiltinId::Format | BuiltinId::TextFromBytes => {
4619                                Kind::Text
4620                            }
4621                            BuiltinId::TextBytes => Kind::SeqInt,
4622                            BuiltinId::ReadReals => Kind::SeqFloat,
4623                            BuiltinId::Contains => Kind::Bool,
4624                            BuiltinId::CountOnes
4625                            | BuiltinId::LeadingZeros
4626                            | BuiltinId::TrailingZeros => Kind::Int,
4627                            BuiltinId::Word8Make => Kind::Word(8),
4628                            BuiltinId::Word16Make => Kind::Word(16),
4629                            BuiltinId::Word32 | BuiltinId::Word32Shr => Kind::Word(32),
4630                            BuiltinId::Word64
4631                            | BuiltinId::Word64Shl
4632                            | BuiltinId::Word64Shr
4633                            | BuiltinId::Word64And => Kind::Word(64),
4634                            BuiltinId::IntOfWord8
4635                            | BuiltinId::IntOfWord16
4636                            | BuiltinId::IntOfWord32
4637                            | BuiltinId::IntOfWord64 => Kind::Int,
4638                            BuiltinId::Rotl | BuiltinId::Rotr | BuiltinId::Wnot => a,
4639                            BuiltinId::Wand | BuiltinId::Wor => a,
4640                            BuiltinId::TryReadBytes => self.inductive_kind(
4641                                "Result",
4642                                &[("Ok", vec![Kind::SeqInt]), ("Err", vec![Kind::Text])],
4643                            ),
4644                            BuiltinId::VfsReadFile | BuiltinId::VfsMount => Kind::Text,
4645                            BuiltinId::VfsWriteFile => Kind::Nothing,
4646                            BuiltinId::RandomInt => Kind::Int,
4647                            BuiltinId::RandomFloat => Kind::Float,
4648                            BuiltinId::EnvHas => Kind::Bool,
4649                            BuiltinId::EnvValue => Kind::Text,
4650                            BuiltinId::TimeNow => Kind::Int,
4651                            _ => Kind::Unknown,
4652                        };
4653                        Some((*dst, k))
4654                    }
4655                    // A tuple's layout is the kinds of its source registers at this pc; intern them so the
4656                    // `Kind::Tuple(idx)` stays `Copy` while the per-position kinds remain reachable.
4657                    Op::NewTuple { dst, start, count } => {
4658                        let ks: Vec<Kind> =
4659                            (0..*count).map(|i| kind_of(&cur, *start + i)).collect();
4660                        Some((*dst, Kind::Tuple(self.intern_tuple(ks))))
4661                    }
4662                    // Destructure writes MULTIPLE registers (one per position) — set each from the tuple's
4663                    // interned layout directly (like `RevSwap`), returning `None` for the single-write path.
4664                    Op::DestructureTuple { src, start, count } => {
4665                        if let Kind::Tuple(ti) = kind_of(&cur, *src) {
4666                            let poss = self.tuple_positions(ti);
4667                            for i in 0..*count {
4668                                let k = poss.get(i as usize).copied().unwrap_or(Kind::Unknown);
4669                                let idx = (*start + i) as usize;
4670                                if idx < cur.len() {
4671                                    cur[idx] = k;
4672                                }
4673                            }
4674                        }
4675                        None
4676                    }
4677                    Op::NewStruct { dst, type_name } => {
4678                        Some((*dst, self.struct_kind_of_type_name(*type_name)))
4679                    }
4680                    // A counter CRDT (`ConvergentCount`/`Tally`, kind 4/5) is a `logos_crdt*` handle that a
4681                    // holding field SUMS — its value kind is `CrdtCounter` (which, flowing into a `StructInsert`,
4682                    // marks that field a counter field). A sequence CRDT (`SharedSequence`/RGA, kind 1) IS a plain
4683                    // seq handle on the single replica — an empty `SeqUnknown` refined by a later `CrdtAppend`
4684                    // element. Other kinds are refused at lowering. Mirrors `codegen_riscv`'s `NewCrdt` kind.
4685                    Op::NewCrdt { dst, kind } => Some((
4686                        *dst,
4687                        match kind {
4688                            4 | 5 => Kind::CrdtCounter,
4689                            1 => Kind::SeqUnknown,
4690                            _ => Kind::Unknown,
4691                        },
4692                    )),
4693                    // A field write on a SYNTHESIZED struct (a `Tally` whose declared type didn't resolve) has no
4694                    // declared field type, so refine the field's kind from the value flowed into its cell — a
4695                    // later `GetField`/`Show` then renders it. (A resolved `Struct` has its declared field kinds,
4696                    // so this is a no-op there.) The write itself is a heap-cell store — no register write here.
4697                    Op::StructInsert { obj, field, value } => {
4698                        if let Kind::StructSynth(idx) = kind_of(&cur, *obj) {
4699                            self.synth_refine_field_kind(idx, *field, kind_of(&cur, *value));
4700                        }
4701                        None
4702                    }
4703                    // An append to a shared sequence gives it the appended element's kind.
4704                    Op::CrdtAppend { seq, value } => {
4705                        Some((*seq, self.seq_kind_for(kind_of(&cur, *value))))
4706                    }
4707                    // `CrdtBump` on a synth field's `Nothing`/`Int` cell keeps it an `Int` (the VM's
4708                    // `crdt_counter_bump(Nothing|Int, n) = Int(..)`), so refine the field kind to `Int`.
4709                    Op::CrdtBump { obj, field, .. } => {
4710                        if let Kind::StructSynth(idx) = kind_of(&cur, *obj) {
4711                            self.synth_refine_field_kind(idx, *field, Kind::Int);
4712                        }
4713                        None
4714                    }
4715                    Op::GetField { dst, obj, field } => {
4716                        let ok = kind_of(&cur, *obj);
4717                        // A counter field's declared cell type is `Int`; reading it SUMS the per-replica map to an
4718                        // `Int` (matching the VM's `Show c's points`), so its register kind is `Int`. A plain (or
4719                        // synthesized) field takes its resolved declaration kind.
4720                        let is_counter = self
4721                            .struct_name_of_kind(ok)
4722                            .zip(self.resolve_field_any(ok, *field))
4723                            .map(|(n, (cell, _))| self.counter_fields.contains(&(n, cell)))
4724                            .unwrap_or(false);
4725                        let k = if is_counter {
4726                            Kind::Int
4727                        } else {
4728                            self.resolve_field_any(ok, *field)
4729                                .map(|(_, k)| k)
4730                                .unwrap_or(Kind::Unknown)
4731                        };
4732                        Some((*dst, k))
4733                    }
4734                    Op::NewInductive {
4735                        dst,
4736                        type_name,
4737                        ctor,
4738                        args_start,
4739                        count,
4740                    } => {
4741                        let k = match self.enum_index_of_type_name(*type_name) {
4742                            Some(ei) => Kind::Enum(ei),
4743                            None => match (self.const_text(*type_name), self.const_text(*ctor)) {
4744                                (Some(ty), Some(c)) => {
4745                                    let payload: Vec<Kind> = (0..*count)
4746                                        .map(|i| kind_of(&cur, *args_start + i))
4747                                        .collect();
4748                                    self.inductive_kind(ty, &[(c, payload)])
4749                                }
4750                                _ => Kind::Unknown,
4751                            },
4752                        };
4753                        Some((*dst, k))
4754                    }
4755                    Op::TestArm {
4756                        dst,
4757                        target,
4758                        variant,
4759                    } => {
4760                        match kind_of(&cur, *target) {
4761                            Kind::Enum(ei) => {
4762                                if let Some(vi) = self.variant_index(ei, *variant) {
4763                                    last_test = Some((*target, ei, vi));
4764                                }
4765                            }
4766                            Kind::Inductive(_) => {
4767                                if let Some(c) = self.const_text(*variant) {
4768                                    last_test_ctor = Some((*target, c.to_string()));
4769                                }
4770                            }
4771                            _ => {}
4772                        }
4773                        Some((*dst, Kind::Bool))
4774                    }
4775                    Op::BindArm {
4776                        dst, target, index, ..
4777                    } => {
4778                        let k = match (kind_of(&cur, *target), &last_test, &last_test_ctor) {
4779                            (Kind::Enum(_), Some((t, ei, vi)), _) if t == target => {
4780                                self.variant_field_kind(*ei, *vi, *index as usize)
4781                            }
4782                            (Kind::Inductive(ii), _, Some((t, c))) if t == target => {
4783                                self.inductive_payload(ii, c, *index as usize)
4784                            }
4785                            // A `Some` arm over a value that was popped (present or `nothing`) binds the value.
4786                            (Kind::Optional(oi), _, _) => self.opt_inner(oi),
4787                            _ => Kind::Unknown,
4788                        };
4789                        Some((*dst, k))
4790                    }
4791                    // Reversible-arithmetic places are Int; a reversible swap exchanges two slots' kinds.
4792                    Op::RevXor { place, .. }
4793                    | Op::RevNeg { place }
4794                    | Op::RevAdd { place, .. }
4795                    | Op::RevSub { place, .. } => Some((*place, Kind::Int)),
4796                    Op::RevSwap { a, b } => {
4797                        let (ai, bi) = (*a as usize, *b as usize);
4798                        if ai < cur.len() && bi < cur.len() {
4799                            cur.swap(ai, bi);
4800                            // Held exact, a side the program can make outgrow i64 holds the word it receives tagged.
4801                            if !self.on_words.get() {
4802                                for r in [*a, *b] {
4803                                    if self.exact_outs.contains(&(pc, r))
4804                                        && matches!(cur[r as usize], Kind::Int | Kind::Bool)
4805                                    {
4806                                        cur[r as usize] = Kind::BigInt;
4807                                    }
4808                                }
4809                            }
4810                        }
4811                        None
4812                    }
4813                    Op::DeepClone { dst, src } => Some((*dst, kind_of(&cur, *src))),
4814                    Op::GlobalGet { dst, idx } => Some((
4815                        *dst,
4816                        self.global_kinds
4817                            .get(*idx as usize)
4818                            .copied()
4819                            .unwrap_or(Kind::Unknown),
4820                    )),
4821                    Op::IterPrepare { iterable } => {
4822                        let ik = kind_of(&cur, *iterable);
4823                        // A Text iterates its characters, each itself a Text; a map its entries, each a
4824                        // `(key, value)` tuple; a set and a sequence their elements.
4825                        iter_elems.push(match ik {
4826                            Kind::Text => Kind::Text,
4827                            Kind::Map(_) | Kind::MapUnknown => {
4828                                let (kk, vk) = self.resolve_map_kv(ik).unwrap();
4829                                Kind::Tuple(self.intern_tuple(vec![kk, vk]))
4830                            }
4831                            other => self.elem_kind(other),
4832                        });
4833                        None
4834                    }
4835                    Op::Args { dst } => Some((*dst, Kind::SeqText)),
4836                    Op::LoadToday { dst } => Some((*dst, Kind::Date)),
4837                    Op::LoadNow { dst } => Some((*dst, Kind::Moment)),
4838                    Op::IterNext { dst, .. } => {
4839                        Some((*dst, iter_elems.last().copied().unwrap_or(Kind::Unknown)))
4840                    }
4841                    Op::IterPop => {
4842                        iter_elems.pop();
4843                        None
4844                    }
4845                    // A peer handle IS its address string; `Sync` keeps `dst`'s current value (offline identity —
4846                    // NO write, so the prior kind stands); `Await` yields the received 64-bit message (an Int).
4847                    // Other CRDT/net ops mutate a struct cell / topic FIFO, not a register — no write here.
4848                    Op::NetMakePeer { dst, .. } => Some((*dst, Kind::Peer)),
4849                    Op::NetAwait { dst, stream, .. } => Some((
4850                        *dst,
4851                        if *stream {
4852                            self.net_stream.get()
4853                        } else {
4854                            self.net_message.get()
4855                        },
4856                    )),
4857                    // A `Tcp` op yields a LOGOS `Result` ([`Emitter::tcp_result_kind`]).
4858                    Op::Tcp { dst, kind, .. } => Some((*dst, self.tcp_result_kind(*kind))),
4859                    _ => None,
4860                };
4861                // An operation the VM refuses for its operands' kinds — words of two widths, a word beside another
4862                // number, the remainder of a Real, an approximate comparison of anything but numbers — has no
4863                // result: its register holds a value that cannot exist.
4864                let write = match (&self.prog.code[pc], write) {
4865                    (op, Some((dst, _))) if refused_for_kinds(op, &cur) => Some((dst, Kind::Never)),
4866                    (_, w) => w,
4867                };
4868                // On machine words an exact value arrives as the machine word it is checked to fit.
4869                let write = match write {
4870                    Some((dst, Kind::BigInt))
4871                        if self.on_words.get() && self.arrives_exact(&self.prog.code[pc], &cur) =>
4872                    {
4873                        Some((dst, Kind::Int))
4874                    }
4875                    w => w,
4876                };
4877                // An integer the program can make outgrow i64 is held exact ([`Emitter::exactness`]) — on machine
4878                // words, only one the exact tower computes; `promoted` records, as of this inference, whether the op
4879                // itself makes a machine integer.
4880                let write = match write {
4881                    Some((dst, k @ (Kind::Int | Kind::BigInt))) if self.held_exact(pc) => {
4882                        let version = self.on_words.get() as usize;
4883                        if k == Kind::Int {
4884                            self.promoted.borrow_mut()[version].insert(pc);
4885                        } else {
4886                            self.promoted.borrow_mut()[version].remove(&pc);
4887                        }
4888                        Some((dst, Kind::BigInt))
4889                    }
4890                    w => w,
4891                };
4892                // A value of the extension computed from the kinds read here, and a definition of an exact web,
4893                // which makes a cell whatever it computes (`tower_from` keeps what it does compute).
4894                let write = match self.tower.then(|| self.tower_write(&self.prog.code[pc], &cur)).flatten() {
4895                    Some(w) => Some(w),
4896                    None => write,
4897                };
4898                let write = match write {
4899                    Some((dst, k)) if forced.defs.contains(&pc) => {
4900                        let version = self.on_words.get() as usize;
4901                        if k == Kind::Tower {
4902                            self.tower_from.borrow_mut()[version].remove(&pc);
4903                        } else {
4904                            self.tower_from.borrow_mut()[version].insert(pc, k);
4905                        }
4906                        Some((dst, Kind::Tower))
4907                    }
4908                    w => w,
4909                };
4910                // A definition a number join lands computes its own kind and lands as its variable's.
4911                let write = {
4912                    let version = self.on_words.get() as usize;
4913                    match (write, lands.get(&pc)) {
4914                        (Some((dst, k)), Some(&to)) if k != Kind::Tower && k != to => {
4915                            self.number_from.borrow_mut()[version].insert(pc, (k, to));
4916                            Some((dst, to))
4917                        }
4918                        (w, _) => {
4919                            self.number_from.borrow_mut()[version].remove(&pc);
4920                            w
4921                        }
4922                    }
4923                };
4924                for r in &narrowed {
4925                    set(&mut cur, *r, Kind::BigInt);
4926                }
4927                for (r, held) in &presented {
4928                    set(&mut cur, *r, *held);
4929                }
4930                if let Some((dst, k)) = write {
4931                    // What the program declares the value to be (`a new Seq of Text`, `Let x: Seq of Int be …`)
4932                    // types it wherever nothing it was built from does.
4933                    let k = match k {
4934                        Kind::Unknown | Kind::SeqUnknown | Kind::SetUnknown | Kind::MapUnknown => {
4935                            match self.declared_kind_at(pc) {
4936                                Some(d) if d != Kind::Unknown => d,
4937                                _ => k,
4938                            }
4939                        }
4940                        other => other,
4941                    };
4942                    // A variable holding an undeclared enum keeps what is known of its payloads across its
4943                    // assignments (`none` then `some x`), as long as the two agree.
4944                    let k = match (kind_of(&cur, dst), k) {
4945                        (Kind::Inductive(a), Kind::Inductive(b)) => {
4946                            self.unify_inductive(a, b).unwrap_or(k)
4947                        }
4948                        _ => k,
4949                    };
4950                    // Upgrade a freshly-created empty sequence to the concrete kind it reached last pass
4951                    // (monotone — a concrete kind is never downgraded).
4952                    let ov = kind_of(&overrides, dst);
4953                    let k = match k {
4954                        Kind::SeqUnknown if is_concrete_seq(ov) => ov,
4955                        Kind::SetUnknown if is_concrete_set(ov) => ov,
4956                        Kind::MapUnknown if is_concrete_map(ov) => ov,
4957                        other => other,
4958                    };
4959                    set(&mut cur, dst, k);
4960                }
4961            }
4962            per_pc.push(cur.clone()); // state AFTER the final op
4963            if cur == overrides {
4964                break;
4965            }
4966            overrides = cur;
4967        }
4968        per_pc
4969    }
4970
4971    fn fresh(&mut self) -> usize {
4972        let n = self.tmp;
4973        self.tmp += 1;
4974        n
4975    }
4976
4977    /// Resolve a declared boundary type to a register kind (a `Struct` by name → `Kind::Struct(idx)`; scalars
4978    /// and scalar-element sequences fall through to the free mapping).
4979    fn boundary_kind(&self, ty: &Option<BoundaryType>) -> Kind {
4980        match ty {
4981            Some(BoundaryType::Struct(name)) => self
4982                .prog
4983                .struct_types
4984                .iter()
4985                .position(|s| &s.name == name)
4986                .map(|i| Kind::Struct(i as u16))
4987                .unwrap_or(Kind::Unknown),
4988            Some(BoundaryType::Enum(name)) => self
4989                .prog
4990                .enum_types
4991                .iter()
4992                .position(|e| &e.name == name)
4993                .map(|i| Kind::Enum(i as u16))
4994                .unwrap_or(Kind::Unknown),
4995            // A declared tuple return/param type `(Int, Text)` → an interned `Kind::Tuple`, so a function that
4996            // returns a tuple gives its `Call` dst (and a following `DestructureTuple`) the right layout.
4997            Some(BoundaryType::Tuple(elems)) => {
4998                let ks: Vec<Kind> = elems
4999                    .iter()
5000                    .map(|e| self.boundary_kind(&Some(e.clone())))
5001                    .collect();
5002                Kind::Tuple(self.intern_tuple(ks))
5003            }
5004            // `Seq of T` — a sequence of scalars, or of any other declared kind as a `SeqOf`; a list of exact values
5005            // is one exact value, the runtime's list.
5006            Some(BoundaryType::Seq(elem)) => match self.boundary_kind(&Some((**elem).clone())) {
5007                Kind::Tower => Kind::Tower,
5008                k => self.seq_kind_for(k),
5009            },
5010            // `Option of T` / `Result of T and E` — the undeclared enums, with their payloads' declared kinds.
5011            Some(BoundaryType::Option(inner)) => {
5012                let inner = self.boundary_kind(&Some((**inner).clone()));
5013                self.inductive_kind("Option", &[("Some", vec![inner]), ("None", Vec::new())])
5014            }
5015            Some(BoundaryType::Result(ok, err)) => {
5016                let (ok, err) = (
5017                    self.boundary_kind(&Some((**ok).clone())),
5018                    self.boundary_kind(&Some((**err).clone())),
5019                );
5020                self.inductive_kind("Result", &[("Ok", vec![ok]), ("Err", vec![err])])
5021            }
5022            // A declared `Map from K to V` → the interned map kind (or a refusal-forcing `Unknown` for an
5023            // unhashable key). The value may be any declared kind.
5024            Some(BoundaryType::Map(k, v)) => self.map_of(
5025                self.boundary_kind(&Some((**k).clone())),
5026                self.boundary_kind(&Some((**v).clone())),
5027            ),
5028            // A declared type of the extension (`Hyperreal`, `Algebraic`, …) holds the runtime's value.
5029            Some(BoundaryType::Builtin(name)) if super::tower::TYPE_NAMES.contains(&name.as_str()) => Kind::Tower,
5030            other => boundary_to_kind_c(other),
5031        }
5032    }
5033
5034    /// The kind the program declares for the value the op at `pc` defines (`declared_types`, sorted by pc).
5035    fn declared_kind_at(&self, pc: usize) -> Option<Kind> {
5036        let at = self
5037            .prog
5038            .declared_types
5039            .binary_search_by_key(&(pc as u32), |(p, _)| *p)
5040            .ok()?;
5041        Some(self.boundary_kind(&Some(self.prog.declared_types[at].1.clone())))
5042    }
5043
5044    /// The `Result` a native `Tcp` op answers with (`TcpOp::code()`): `Ok` carries the handle or byte count
5045    /// (Int), the received bytes (Seq of Int) or `nothing`; `Err` carries the failure's Text.
5046    fn tcp_result_kind(&self, code: u8) -> Kind {
5047        let ok = match code {
5048            0 | 1 | 6 | 7 | 8 => Kind::Int,
5049            2 | 3 => Kind::SeqInt,
5050            4 | 5 | 9 => Kind::Nothing,
5051            _ => Kind::Unknown,
5052        };
5053        self.inductive_kind("Result", &[("Ok", vec![ok]), ("Err", vec![Kind::Text])])
5054    }
5055
5056    /// The `prog.enum_types` index a `NewInductive` type-name constant (a `Text`) refers to.
5057    fn enum_index_of_type_name(&self, type_name: u32) -> Option<u16> {
5058        let ename = match self.prog.constants.get(type_name as usize) {
5059            Some(Constant::Text(s)) => s,
5060            _ => return None,
5061        };
5062        self.prog
5063            .enum_types
5064            .iter()
5065            .position(|e| &e.name == ename)
5066            .map(|i| i as u16)
5067    }
5068
5069    /// The declaration-order index of a variant (a `Text` constant) within an enum type.
5070    fn variant_index(&self, enum_idx: u16, variant: u32) -> Option<usize> {
5071        let ed = self.prog.enum_types.get(enum_idx as usize)?;
5072        let vname = match self.prog.constants.get(variant as usize) {
5073            Some(Constant::Text(s)) => s.as_str(),
5074            _ => return None,
5075        };
5076        ed.variants.iter().position(|v| v.name == vname)
5077    }
5078
5079    /// The `Kind` of payload field `index` of a specific enum variant.
5080    fn variant_field_kind(&self, enum_idx: u16, variant_idx: usize, index: usize) -> Kind {
5081        self.prog
5082            .enum_types
5083            .get(enum_idx as usize)
5084            .and_then(|e| e.variants.get(variant_idx))
5085            .and_then(|v| v.field_types.get(index))
5086            .map(|t| self.boundary_kind(&Some(t.clone())))
5087            .unwrap_or(Kind::Unknown)
5088    }
5089
5090    /// The `prog.struct_types` index a `NewStruct` type-name constant (a `Text`) refers to.
5091    fn struct_index_of_type_name(&self, type_name: u32) -> Option<u16> {
5092        let sname = match self.prog.constants.get(type_name as usize) {
5093            Some(Constant::Text(s)) => s,
5094            _ => return None,
5095        };
5096        self.prog
5097            .struct_types
5098            .iter()
5099            .position(|s| &s.name == sname)
5100            .map(|i| i as u16)
5101    }
5102
5103    /// The Text value of a constant slot (`None` for a non-Text constant).
5104    fn const_text(&self, idx: u32) -> Option<&str> {
5105        match self.prog.constants.get(idx as usize) {
5106            Some(Constant::Text(s)) => Some(s.as_str()),
5107            _ => None,
5108        }
5109    }
5110
5111    /// Intern a synthesized struct layout by NAME (dedup), returning the `u16` a `Kind::StructSynth` carries.
5112    fn synth_index_of_name(&self, name: &str) -> u16 {
5113        let mut t = self.synth_structs.borrow_mut();
5114        if let Some(i) = t.iter().position(|(n, _)| n == name) {
5115            return i as u16;
5116        }
5117        t.push((name.to_string(), Vec::new()));
5118        (t.len() - 1) as u16
5119    }
5120
5121    /// The struct kind a `NewStruct` type-name constant produces: a `Kind::Struct` when the type resolved into
5122    /// `prog.struct_types`, else a `Kind::StructSynth` over a synthesized layout (interned by name). `Unknown`
5123    /// only for a non-Text type-name constant.
5124    fn struct_kind_of_type_name(&self, type_name: u32) -> Kind {
5125        if let Some(si) = self.struct_index_of_type_name(type_name) {
5126            return Kind::Struct(si);
5127        }
5128        match self.const_text(type_name) {
5129            Some(name) => Kind::StructSynth(self.synth_index_of_name(name)),
5130            None => Kind::Unknown,
5131        }
5132    }
5133
5134    /// The struct NAME behind a `Struct`/`StructSynth` kind (used to key `counter_fields`).
5135    fn struct_name_of_kind(&self, k: Kind) -> Option<String> {
5136        match k {
5137            Kind::Struct(si) => self
5138                .prog
5139                .struct_types
5140                .get(si as usize)
5141                .map(|s| s.name.clone()),
5142            Kind::StructSynth(idx) => self
5143                .synth_structs
5144                .borrow()
5145                .get(idx as usize)
5146                .map(|(n, _)| n.clone()),
5147            _ => None,
5148        }
5149    }
5150
5151    /// The cell index of a field-name constant within a SYNTHESIZED layout, appending it (growing the layout in
5152    /// first-seen order) when not yet present. This is how an unresolved struct's field→cell map is built from
5153    /// the field ops encountered on it.
5154    fn synth_field_cell(&self, idx: u16, field: u32) -> Option<usize> {
5155        let fname = self.const_text(field)?.to_string();
5156        let mut t = self.synth_structs.borrow_mut();
5157        let (_, fields) = t.get_mut(idx as usize)?;
5158        Some(match fields.iter().position(|(f, _)| f == &fname) {
5159            Some(cell) => cell,
5160            None => {
5161                fields.push((fname, Kind::Unknown));
5162                fields.len() - 1
5163            }
5164        })
5165    }
5166
5167    /// Monotonically refine the KIND of a synthesized struct's field (the value flowed into its cell): a concrete
5168    /// kind is never downgraded to `Unknown`, and a later concrete kind overrides an earlier one (the last write
5169    /// establishes the readable value — the LWW cell).
5170    fn synth_refine_field_kind(&self, idx: u16, field: u32, k: Kind) {
5171        if k == Kind::Unknown {
5172            return;
5173        }
5174        let cell = match self.synth_field_cell(idx, field) {
5175            Some(c) => c,
5176            None => return,
5177        };
5178        let mut t = self.synth_structs.borrow_mut();
5179        if let Some((_, fields)) = t.get_mut(idx as usize) {
5180            if let Some((_, fk)) = fields.get_mut(cell) {
5181                *fk = k;
5182            }
5183        }
5184    }
5185
5186    /// The number of cells a `Struct`/`StructSynth` lays out (its field count).
5187    fn struct_cell_count(&self, k: Kind) -> Option<usize> {
5188        match k {
5189            Kind::Struct(si) => self
5190                .prog
5191                .struct_types
5192                .get(si as usize)
5193                .map(|s| s.fields.len()),
5194            Kind::StructSynth(idx) => self
5195                .synth_structs
5196                .borrow()
5197                .get(idx as usize)
5198                .map(|(_, f)| f.len()),
5199            _ => None,
5200        }
5201    }
5202
5203    /// Heap fields have a stable representation across every store and read.
5204    /// Integer cells use exact tagged words; counter cells retain their CRDT handles.
5205    fn field_storage_kind(&self, obj: Kind, cell: usize) -> Option<Kind> {
5206        if self
5207            .struct_name_of_kind(obj)
5208            .is_some_and(|name| self.counter_fields.contains(&(name, cell)))
5209        {
5210            return Some(Kind::CrdtCounter);
5211        }
5212        let kind = match obj {
5213            Kind::Struct(si) => {
5214                let (_, ty) = self.prog.struct_types.get(si as usize)?.fields.get(cell)?;
5215                self.boundary_kind(&Some(ty.clone()))
5216            }
5217            Kind::StructSynth(si) => self.synth_structs.borrow().get(si as usize)?.1.get(cell)?.1,
5218            _ => return None,
5219        };
5220        Some(if kind == Kind::Int {
5221            Kind::BigInt
5222        } else {
5223            kind
5224        })
5225    }
5226
5227    fn field_store_value(
5228        &mut self,
5229        obj: Kind,
5230        cell: usize,
5231        value: u16,
5232        kind: Kind,
5233    ) -> Result<String, String> {
5234        if self.field_storage_kind(obj, cell) == Some(Kind::Tower) && kind != Kind::Tower {
5235            let converted = self.tower_of(&format!("r[{value}]"), kind)?;
5236            return Ok(format!("(int64_t)(intptr_t)({converted})"));
5237        }
5238        // A rational field holds an integer as the rational it equals. The language holds nothing in one a record is
5239        // built without, which a compiled rational cannot hold.
5240        if self.field_storage_kind(obj, cell) == Some(Kind::Rational) && kind != Kind::Rational {
5241            if kind == Kind::Nothing {
5242                return Err(
5243                    "a record's rational field holding nothing: a compiled rational holds no nothing".to_string(),
5244                );
5245            }
5246            return self.convert_number(&format!("r[{value}]"), kind, Kind::Rational);
5247        }
5248        if self.field_storage_kind(obj, cell) == Some(Kind::BigInt) {
5249            // Move-out clears and uninitialized CRDT fields carry the empty
5250            // cell, which a later store or bump replaces.
5251            if kind == Kind::Nothing {
5252                return Ok("0".to_string());
5253            }
5254            as_exact(value, kind)
5255        } else {
5256            Ok(format!("r[{value}]"))
5257        }
5258    }
5259
5260    /// Resolve a field on ANY struct kind (`Struct` or synthesized `StructSynth`) to its cell index and `Kind`.
5261    /// A `Struct`'s field kind is its declared type; a synthesized field's kind is the (refined) kind of the
5262    /// value flowed into its cell (`Unknown` until a `StructInsert`/`CrdtBump` establishes it). The counter
5263    /// side-set drives the counter-vs-plain decision separately.
5264    fn resolve_field_any(&self, obj_kind: Kind, field: u32) -> Option<(usize, Kind)> {
5265        match obj_kind {
5266            Kind::Struct(si) => self.resolve_field(si, field),
5267            Kind::StructSynth(idx) => {
5268                let cell = self.synth_field_cell(idx, field)?;
5269                let fk = self
5270                    .synth_structs
5271                    .borrow()
5272                    .get(idx as usize)
5273                    .and_then(|(_, f)| f.get(cell))
5274                    .map(|(_, k)| *k)
5275                    .unwrap_or(Kind::Unknown);
5276                Some((cell, self.field_storage_kind(obj_kind, cell).unwrap_or(fk)))
5277            }
5278            _ => None,
5279        }
5280    }
5281
5282    /// Resolve a field (by the object's `Struct` kind + a field-name constant) to its declaration-order cell
5283    /// index and its `Kind`.
5284    fn resolve_field(&self, struct_idx: u16, field: u32) -> Option<(usize, Kind)> {
5285        let sd = self.prog.struct_types.get(struct_idx as usize)?;
5286        let fname = match self.prog.constants.get(field as usize) {
5287            Some(Constant::Text(s)) => s.clone(),
5288            _ => return None,
5289        };
5290        let idx = sd.fields.iter().position(|(n, _)| *n == fname)?;
5291        Some((idx, self.field_storage_kind(Kind::Struct(struct_idx), idx)?))
5292    }
5293
5294    /// Resolve a field by struct type NAME + field NAME (policy conditions carry names, not `ConstIdx`) to its
5295    /// declaration-order cell index and declared `Kind`. Only real `prog.struct_types` are consulted — a policy
5296    /// always names a declared type.
5297    fn field_cell_by_name(&self, type_name: &str, field_name: &str) -> Option<(usize, Kind)> {
5298        let sd = self
5299            .prog
5300            .struct_types
5301            .iter()
5302            .position(|s| s.name == type_name)?;
5303        let struct_idx = sd as u16;
5304        let sd = &self.prog.struct_types[sd];
5305        let idx = sd.fields.iter().position(|(n, _)| n == field_name)?;
5306        Some((idx, self.field_storage_kind(Kind::Struct(struct_idx), idx)?))
5307    }
5308
5309    /// A C boolean EXPRESSION (0/1) for the policy `cond` evaluated against the `subject` struct's field cells
5310    /// (and the `object` struct for capability conditions). Mirrors `semantics::policy::evaluate_policy_condition`
5311    /// and `codegen_riscv::emit_policy_condition` exactly, but as a composable C expression instead of asm.
5312    fn policy_condition_expr(
5313        &self,
5314        cond: &PolicyCondition,
5315        subject: u16,
5316        subject_type: &str,
5317        object: u16,
5318        object_type: Option<&str>,
5319        interner: &Interner,
5320    ) -> Result<String, String> {
5321        let cell_c = |reg: u16, cell: usize| format!("((int64_t*)(intptr_t)r[{reg}])[{cell}]");
5322        match cond {
5323            PolicyCondition::FieldEquals {
5324                field,
5325                value,
5326                is_string_literal,
5327            } => {
5328                let fname = interner.resolve(*field);
5329                let vstr = interner.resolve(*value);
5330                let (cell, fk) = self
5331                    .field_cell_by_name(subject_type, fname)
5332                    .ok_or("policy references an unknown field")?;
5333                let lv = cell_c(subject, cell);
5334                Ok(match fk {
5335                    Kind::Text => format!(
5336                        "(strcmp((const char*)(intptr_t){lv}, \"{}\")==0)",
5337                        c_escape(vstr)
5338                    ),
5339                    Kind::BigInt if !*is_string_literal => match vstr.parse::<i64>() {
5340                        Ok(n) => format!("(lc_xcmp({lv}, lc_xof({n}LL)) == 0)"),
5341                        Err(_) => "0".to_string(),
5342                    },
5343                    Kind::Bool if !*is_string_literal => match vstr.parse::<bool>() {
5344                        Ok(b) => format!("({lv} == {})", b as i64),
5345                        Err(_) => "0".to_string(),
5346                    },
5347                    _ => "0".to_string(), // string-literal vs Int/Bool field, or an unmodeled kind
5348                })
5349            }
5350            PolicyCondition::FieldBool { field, value } => {
5351                let fname = interner.resolve(*field);
5352                let (cell, _) = self
5353                    .field_cell_by_name(subject_type, fname)
5354                    .ok_or("policy references an unknown field")?;
5355                Ok(format!("({} == {})", cell_c(subject, cell), *value as i64))
5356            }
5357            PolicyCondition::Predicate { predicate, .. } => {
5358                let sub = self.policy_ctx.and_then(|(_, policies)| {
5359                    interner.lookup(subject_type).and_then(|ts| {
5360                        policies.get_predicates(ts).and_then(|ps| {
5361                            ps.iter()
5362                                .find(|p| p.predicate_name == *predicate)
5363                                .map(|p| p.condition.clone())
5364                        })
5365                    })
5366                });
5367                match sub {
5368                    Some(c) => self.policy_condition_expr(
5369                        &c,
5370                        subject,
5371                        subject_type,
5372                        object,
5373                        object_type,
5374                        interner,
5375                    ),
5376                    None => Ok("0".to_string()),
5377                }
5378            }
5379            PolicyCondition::ObjectFieldEquals {
5380                subject: subj_field,
5381                field,
5382                ..
5383            } => self.field_equality_expr(
5384                subject,
5385                subject_type,
5386                *subj_field,
5387                object,
5388                object_type,
5389                *field,
5390                interner,
5391            ),
5392            PolicyCondition::SubjectFieldEqualsObjectField {
5393                subject_field,
5394                object_field,
5395                ..
5396            } => self.field_equality_expr(
5397                subject,
5398                subject_type,
5399                *subject_field,
5400                object,
5401                object_type,
5402                *object_field,
5403                interner,
5404            ),
5405            PolicyCondition::Or(l, r) => {
5406                let le = self.policy_condition_expr(
5407                    l,
5408                    subject,
5409                    subject_type,
5410                    object,
5411                    object_type,
5412                    interner,
5413                )?;
5414                let re = self.policy_condition_expr(
5415                    r,
5416                    subject,
5417                    subject_type,
5418                    object,
5419                    object_type,
5420                    interner,
5421                )?;
5422                Ok(format!("(({le}) || ({re}))"))
5423            }
5424            PolicyCondition::And(l, r) => {
5425                let le = self.policy_condition_expr(
5426                    l,
5427                    subject,
5428                    subject_type,
5429                    object,
5430                    object_type,
5431                    interner,
5432                )?;
5433                let re = self.policy_condition_expr(
5434                    r,
5435                    subject,
5436                    subject_type,
5437                    object,
5438                    object_type,
5439                    interner,
5440                )?;
5441                Ok(format!("(({le}) && ({re}))"))
5442            }
5443        }
5444    }
5445
5446    /// `subject.<subj_field> == object.<obj_field>` as a C boolean expression (the VM's `values_equal`); a
5447    /// missing object (a predicate context, not a capability) is `0`. Text compares with `strcmp`, else `==`.
5448    fn field_equality_expr(
5449        &self,
5450        subject: u16,
5451        subject_type: &str,
5452        subj_field: crate::intern::Symbol,
5453        object: u16,
5454        object_type: Option<&str>,
5455        obj_field: crate::intern::Symbol,
5456        interner: &Interner,
5457    ) -> Result<String, String> {
5458        let obj_type = match (object_type, object != u16::MAX) {
5459            (Some(t), true) => t,
5460            _ => return Ok("0".to_string()),
5461        };
5462        let sf = interner.resolve(subj_field);
5463        let of = interner.resolve(obj_field);
5464        let (sc, sk) = self
5465            .field_cell_by_name(subject_type, sf)
5466            .ok_or("policy references an unknown subject field")?;
5467        let (oc, _) = self
5468            .field_cell_by_name(obj_type, of)
5469            .ok_or("policy references an unknown object field")?;
5470        let sl = format!("((int64_t*)(intptr_t)r[{subject}])[{sc}]");
5471        let ol = format!("((int64_t*)(intptr_t)r[{object}])[{oc}]");
5472        Ok(match sk {
5473            Kind::Text => {
5474                format!("(strcmp((const char*)(intptr_t){sl}, (const char*)(intptr_t){ol})==0)")
5475            }
5476            _ => format!("({sl} == {ol})"),
5477        })
5478    }
5479
5480    /// The boxed sequence in register `reg`, cast to its typed `Seq_*` pointer.
5481    fn seq_ptr(reg: u16, seq_kind: Kind) -> Result<String, String> {
5482        let ty = seq_kind
5483            .seq_c_type()
5484            .ok_or_else(|| format!("sequence op on a non-sequence kind {seq_kind:?}"))?;
5485        Ok(format!("(({ty}*)(intptr_t)r[{reg}])"))
5486    }
5487
5488    /// The boxed set in register `reg`, cast to its typed `Set_*` pointer.
5489    fn set_ptr(reg: u16, set_kind: Kind) -> Result<String, String> {
5490        let ty = set_kind
5491            .set_c_type()
5492            .ok_or_else(|| format!("set op on a non-set kind {set_kind:?}"))?;
5493        Ok(format!("(({ty}*)(intptr_t)r[{reg}])"))
5494    }
5495
5496    /// Emit a deep copy of the collection in register `reg` into a fresh box, returning the `int64_t`-cast
5497    /// handle expression; `None` for an immutable scalar/Text (the caller uses `r[reg]` directly). The single
5498    /// choke point for value-semantics cloning (DeepClone, EnsureOwned COW, by-value call arguments).
5499    fn clone_collection_expr(&mut self, reg: u16, k: Kind) -> Result<Option<String>, String> {
5500        if k == Kind::Tower {
5501            return Ok(Some(format!("(int64_t)(intptr_t)ltower_own((LTower)(intptr_t)r[{reg}])")));
5502        }
5503        let k = match k {
5504            Kind::SeqUnknown => Kind::SeqInt,
5505            Kind::SetUnknown => Kind::SetInt,
5506            other => other,
5507        };
5508        if let (Some(ty), Some(infix)) = (k.seq_c_type(), k.seq_infix()) {
5509            let ptr = Self::seq_ptr(reg, k)?;
5510            let t = self.fresh();
5511            self.line(&format!(
5512                "{ty} *_cl{t} = ({ty}*)lc_gc_alloc(sizeof({ty})); *_cl{t} = seq_{infix}_copy({ptr});"
5513            ));
5514            Ok(Some(format!("(int64_t)(intptr_t)_cl{t}")))
5515        } else if is_concrete_set(k) {
5516            let (ty, infix) = (k.set_c_type().unwrap(), k.seq_infix().unwrap());
5517            let ptr = Self::set_ptr(reg, k)?;
5518            let t = self.fresh();
5519            self.line(&format!(
5520                "{ty} *_cl{t} = ({ty}*)lc_gc_alloc(sizeof({ty})); *_cl{t} = lc_set_{infix}_copy({ptr});"
5521            ));
5522            Ok(Some(format!("(int64_t)(intptr_t)_cl{t}")))
5523        } else if let Some((key, val)) = self.resolve_map_kv(k) {
5524            let (ty, pre) = (map_type_name(key, val).unwrap(), map_fn(key, val).unwrap());
5525            let t = self.fresh();
5526            self.line(&format!("{ty} *_cl{t} = ({ty}*)lc_gc_alloc(sizeof({ty})); *_cl{t} = lc_{pre}_copy(({ty}*)(intptr_t)r[{reg}]);"));
5527            Ok(Some(format!("(int64_t)(intptr_t)_cl{t}")))
5528        } else if let Some(n) = self.struct_cell_count(k) {
5529            // A record's copy shares its fields' collections until one of them changes: its field-ownership
5530            // word (the cell after the fields) starts clear, so the first change through the copy copies that
5531            // field's collection — the VM's deep copy, made lazily.
5532            let t = self.fresh();
5533            self.line(&format!(
5534                "int64_t *_cl{t} = (int64_t*)lc_gc_alloc({} * sizeof(int64_t)); memcpy(_cl{t}, (void*)(intptr_t)r[{reg}], {n} * sizeof(int64_t)); _cl{t}[{n}] = 0;",
5535                n + 1
5536            ));
5537            Ok(Some(format!("(int64_t)(intptr_t)_cl{t}")))
5538        } else {
5539            Ok(None)
5540        }
5541    }
5542
5543    /// The C lvalue of the field-ownership word of the record in register `obj` (of struct kind `k`): bit `c` is
5544    /// set while the record is the only holder of the collection in field cell `c`.
5545    fn field_owner_word(&self, obj: u16, k: Kind) -> Option<String> {
5546        let n = self.struct_cell_count(k)?;
5547        Some(format!("((uint64_t*)(intptr_t)r[{obj}])[{n}]"))
5548    }
5549
5550    /// A C expression yielding the display text (a `char*`) of the value `v` — an `int64_t` C expression — of
5551    /// kind `k`: the VM's `to_display_string`, recursively. A list shows its elements, a tuple its positions, a
5552    /// record its fields in name order, an enum its constructor and payloads, an `Option` its payload or
5553    /// `nothing` — each part shown by its own kind.
5554    fn render(&mut self, v: &str, k: Kind) -> Result<String, String> {
5555        let n = self.fresh();
5556        Ok(match k {
5557            Kind::Int => format!("logos_dyn_sprintf(\"%\" PRId64, (int64_t)({v}))"),
5558            Kind::Bool => format!("(({v}) ? \"true\" : \"false\")"),
5559            Kind::Float => format!("logos_fmt_f64(as_f64({v}))"),
5560            Kind::Text => format!("((const char*)(intptr_t)({v}))"),
5561            Kind::Rational => format!("ldec_to_string((LDec*)(intptr_t)({v}))"),
5562            Kind::BigInt => format!("lint_to_string(lc_xget({v}))"),
5563            Kind::Tower => format!("ltower_to_string((LTower)(intptr_t)({v}))"),
5564            Kind::Nothing => "\"nothing\"".to_string(),
5565            Kind::Peer => format!("logos_dyn_sprintf(\"<peer %s>\", (const char*)(intptr_t)({v}))"),
5566            Kind::Date => format!("logos_date_to_string((int64_t)({v}))"),
5567            Kind::Word(_) => format!("logos_dyn_sprintf(\"%\" PRIu64, (uint64_t)({v}))"),
5568            Kind::Moment => format!("logos_moment_to_string((int64_t)({v}))"),
5569            Kind::Time => format!("logos_format_time_of_day((int64_t)({v}))"),
5570            Kind::Span => format!(
5571                "logos_span_to_string((LogosSpan){{(int32_t)((int64_t)({v}) >> 32), (int32_t)(int64_t)({v})}})"
5572            ),
5573            Kind::CrdtCounter => {
5574                format!("logos_dyn_sprintf(\"%lld\", logos_crdt_sum((logos_crdt*)(intptr_t)({v})))")
5575            }
5576            Kind::SeqInt | Kind::SeqFloat | Kind::SeqText | Kind::SeqBool | Kind::SeqUnknown => {
5577                let sk = seq_kind_of(k);
5578                format!(
5579                    "seq_{}_to_string(({}*)(intptr_t)({v}))",
5580                    sk.seq_infix().unwrap(),
5581                    sk.seq_c_type().unwrap()
5582                )
5583            }
5584            Kind::SetWord(_) => format!("set_i64_to_string_as((Set_i64*)(intptr_t)({v}), 1)"),
5585            Kind::SetInt | Kind::SetText | Kind::SetUnknown => {
5586                let sk = resolve_set(k).unwrap();
5587                format!(
5588                    "set_{}_to_string(({}*)(intptr_t)({v}))",
5589                    sk.seq_infix().unwrap(),
5590                    sk.set_c_type().unwrap()
5591                )
5592            }
5593            Kind::Map(_) | Kind::MapUnknown => {
5594                let (kk, vk) = self.resolve_map_kv(k).unwrap();
5595                let ty = map_type_name(kk, vk).unwrap();
5596                let key = if kk == Kind::Text {
5597                    format!("_m{n}->keys[_si{n}]")
5598                } else {
5599                    format!("_m{n}->keys[_si{n}]")
5600                };
5601                let key_text = self.render(&format!("(int64_t)(intptr_t){key}"), kk)?;
5602                let val = map_val_load(&format!("_m{n}->vals[_si{n}]"), vk);
5603                let val_text = self.render(&val, vk)?;
5604                format!(
5605                    "({{ {ty} *_m{n} = ({ty}*)(intptr_t)({v}); lc_sb _b{n} = {{0}}; lc_sb_add(&_b{n}, \"{{\"); int _f{n} = 1; \
5606                     for (size_t _oi{n} = 0; _oi{n} < _m{n}->ord_len; _oi{n}++) {{ size_t _si{n} = _m{n}->order[_oi{n}]; if (!_m{n}->state[_si{n}]) continue; \
5607                     if (!_f{n}) lc_sb_add(&_b{n}, \", \"); _f{n} = 0; lc_sb_add(&_b{n}, {key_text}); lc_sb_add(&_b{n}, \": \"); lc_sb_add(&_b{n}, {val_text}); }} \
5608                     lc_sb_add(&_b{n}, \"}}\"); _b{n}.s; }})"
5609                )
5610            }
5611            Kind::Tuple(ti) => {
5612                let poss = self.tuple_positions(ti);
5613                let mut parts = String::new();
5614                for (i, pk) in poss.iter().enumerate() {
5615                    if i > 0 {
5616                        parts.push_str(&format!("lc_sb_add(&_b{n}, \", \"); "));
5617                    }
5618                    let cell = self.render(&format!("_t{n}[{i}]"), *pk)?;
5619                    parts.push_str(&format!("lc_sb_add(&_b{n}, {cell}); "));
5620                }
5621                format!("({{ int64_t *_t{n} = (int64_t*)(intptr_t)({v}); lc_sb _b{n} = {{0}}; lc_sb_add(&_b{n}, \"(\"); {parts}lc_sb_add(&_b{n}, \")\"); _b{n}.s; }})")
5622            }
5623            Kind::SeqOf(ei) => {
5624                let item = self.render(
5625                    &format!("_s{n}->data[_i{n}]"),
5626                    self.elem_kind(Kind::SeqOf(ei)),
5627                )?;
5628                format!(
5629                    "({{ Seq_i64 *_s{n} = (Seq_i64*)(intptr_t)({v}); lc_sb _b{n} = {{0}}; lc_sb_add(&_b{n}, \"[\"); \
5630                     for (size_t _i{n} = 0; _i{n} < _s{n}->len; _i{n}++) {{ if (_i{n}) lc_sb_add(&_b{n}, \", \"); lc_sb_add(&_b{n}, {item}); }} \
5631                     lc_sb_add(&_b{n}, \"]\"); _b{n}.s; }})"
5632                )
5633            }
5634            Kind::Struct(_) | Kind::StructSynth(_) => {
5635                let name = self.struct_name_of_kind(k).ok_or("a record with no name")?;
5636                let mut fields: Vec<(String, usize, Kind)> = match k {
5637                    Kind::Struct(si) => {
5638                        let sd = self.prog.struct_types[si as usize].clone();
5639                        sd.fields
5640                            .iter()
5641                            .enumerate()
5642                            .map(|(c, (f, _))| {
5643                                (f.clone(), c, self.field_storage_kind(k, c).unwrap())
5644                            })
5645                            .collect()
5646                    }
5647                    Kind::StructSynth(idx) => {
5648                        let fs = self.synth_structs.borrow()[idx as usize].1.clone();
5649                        fs.into_iter()
5650                            .enumerate()
5651                            .map(|(c, (f, _))| (f, c, self.field_storage_kind(k, c).unwrap()))
5652                            .collect()
5653                    }
5654                    _ => unreachable!(),
5655                };
5656                if fields.is_empty() {
5657                    return Ok(format!("\"{}\"", c_escape(&name)));
5658                }
5659                fields.sort_by(|a, b| a.0.cmp(&b.0));
5660                let mut parts = String::new();
5661                for (i, (f, c, fk)) in fields.iter().enumerate() {
5662                    let is_counter = self.counter_fields.contains(&(name.clone(), *c));
5663                    let fk = if is_counter { Kind::CrdtCounter } else { *fk };
5664                    let cell = self.render(&format!("_r{n}[{c}]"), fk)?;
5665                    let label = format!("{}{}: ", if i > 0 { ", " } else { "" }, f);
5666                    parts.push_str(&format!(
5667                        "lc_sb_add(&_b{n}, \"{}\"); lc_sb_add(&_b{n}, {cell}); ",
5668                        c_escape(&label)
5669                    ));
5670                }
5671                format!(
5672                    "({{ int64_t *_r{n} = (int64_t*)(intptr_t)({v}); lc_sb _b{n} = {{0}}; lc_sb_add(&_b{n}, \"{} {{ \"); {parts}lc_sb_add(&_b{n}, \" }}\"); _b{n}.s; }})",
5673                    c_escape(&name)
5674                )
5675            }
5676            Kind::Enum(ei) => {
5677                let ed = self.prog.enum_types[ei as usize].clone();
5678                let arms: Vec<(String, Option<Vec<Kind>>)> = ed
5679                    .variants
5680                    .iter()
5681                    .map(|var| {
5682                        (
5683                            var.name.clone(),
5684                            Some(
5685                                var.field_types
5686                                    .iter()
5687                                    .map(|t| self.boundary_kind(&Some(t.clone())))
5688                                    .collect(),
5689                            ),
5690                        )
5691                    })
5692                    .collect();
5693                self.render_constructors(v, &arms, n)?
5694            }
5695            Kind::Inductive(ii) => {
5696                let (ty, ctors) = self.inductive(ii);
5697                if ty == "Option" {
5698                    // An `Option` shows as its payload, or `nothing` — never as its wrapper.
5699                    let can_be_some = ctors.iter().any(|(c, ks)| c == "Some" && ks.is_some());
5700                    if !can_be_some {
5701                        "\"nothing\"".to_string()
5702                    } else {
5703                        let some = self.ctor_tag("Option", "Some").unwrap();
5704                        let payload = self.inductive_payload(ii, "Some", 0);
5705                        let inner = self.render(&format!("_o{n}[1]"), payload)?;
5706                        format!("({{ int64_t *_o{n} = (int64_t*)(intptr_t)({v}); (_o{n}[0] == {some}) ? {inner} : \"nothing\"; }})")
5707                    }
5708                } else {
5709                    self.render_constructors(v, &ctors, n)?
5710                }
5711            }
5712            // A popped value of a sequence that never holds one is always absent.
5713            Kind::Optional(oi) if self.opt_inner(oi) == Kind::Never => "\"nothing\"".to_string(),
5714            Kind::Optional(oi) => {
5715                let inner = self.render(&format!("_o{n}[1]"), self.opt_inner(oi))?;
5716                format!("({{ int64_t *_o{n} = (int64_t*)(intptr_t)({v}); _o{n}[0] ? {inner} : \"nothing\"; }})")
5717            }
5718            other => return Err(format!("the display of a {other:?} value")),
5719        })
5720    }
5721
5722    /// The display of an enum value `v` whose constructors, in tag order, carry the payload kinds `arms`: the
5723    /// constructor's name, then its payloads in parentheses when it has any.
5724    fn render_constructors(
5725        &mut self,
5726        v: &str,
5727        arms: &[(String, Option<Vec<Kind>>)],
5728        n: usize,
5729    ) -> Result<String, String> {
5730        let mut cases = String::new();
5731        for (tag, (name, payloads)) in arms.iter().enumerate() {
5732            // A constructor the value can never be needs no case.
5733            let Some(payloads) = payloads else { continue };
5734            let mut body = format!("lc_sb_add(&_b{n}, \"{}\"); ", c_escape(name));
5735            if !payloads.is_empty() {
5736                body.push_str(&format!("lc_sb_add(&_b{n}, \"(\"); "));
5737                for (i, pk) in payloads.iter().enumerate() {
5738                    if i > 0 {
5739                        body.push_str(&format!("lc_sb_add(&_b{n}, \", \"); "));
5740                    }
5741                    let part = self.render(&format!("_e{n}[{}]", i + 1), *pk)?;
5742                    body.push_str(&format!("lc_sb_add(&_b{n}, {part}); "));
5743                }
5744                body.push_str(&format!("lc_sb_add(&_b{n}, \")\"); "));
5745            }
5746            cases.push_str(&format!("case {tag}: {body}break; "));
5747        }
5748        Ok(format!(
5749            "({{ int64_t *_e{n} = (int64_t*)(intptr_t)({v}); lc_sb _b{n} = {{0}}; lc_sb_add(&_b{n}, \"\"); switch (_e{n}[0]) {{ {cases}}} _b{n}.s; }})"
5750        ))
5751    }
5752
5753    /// The C address a `Send`/`Stream`/`Await` operand names — a peer's topic or an address string — or, for any
5754    /// other kind, the VM's refusal emitted in its place (`None`).
5755    fn peer_address(&mut self, reg: u16, kinds: &[Kind]) -> Result<Option<String>, String> {
5756        match kind_at(kinds, reg) {
5757            Kind::Peer | Kind::Text => Ok(Some(as_cstr(reg))),
5758            k => {
5759                let sample =
5760                    sample_value(k).ok_or_else(|| format!("a peer address of kind {k:?}"))?;
5761                self.line(&format!("logos_fatalf({});", words::not_a_peer(sample)));
5762                Ok(None)
5763            }
5764        }
5765    }
5766
5767    /// A C expression (0 or 1) that the values `a` (of kind `ka`) and `b` (of kind `kb`) — `int64_t` C expressions —
5768    /// are equal, as the VM's `values_equal` decides it: numbers by value across their kinds, text by its bytes,
5769    /// a sequence or tuple element by element, a record field by field, an enum by constructor and payloads, a set
5770    /// or map by content in any order, absence as one thing however spelled; values of unrelated kinds never.
5771    fn eq_expr(&mut self, a: &str, ka: Kind, b: &str, kb: Kind) -> Result<String, String> {
5772        let numeric =
5773            |k: Kind| matches!(k, Kind::Int | Kind::Float | Kind::BigInt | Kind::Rational);
5774        let n = self.fresh();
5775        Ok(match (ka, kb) {
5776            (x, y) if numeric(x) && numeric(y) => {
5777                let (la, lb) = (self.scalar_c(a, x), self.scalar_c(b, y));
5778                match (x, y) {
5779                    (Kind::Int, Kind::Int) => format!("({a} == {b})"),
5780                    (Kind::BigInt | Kind::Int, Kind::BigInt | Kind::Int) => {
5781                        let tagged = |v: &str, k: Kind| {
5782                            if k == Kind::BigInt {
5783                                v.to_string()
5784                            } else {
5785                                format!("lc_xof({v})")
5786                            }
5787                        };
5788                        format!("(lc_xcmp({}, {}) == 0)", tagged(a, x), tagged(b, y))
5789                    }
5790                    (Kind::Rational, Kind::Rational) => format!("(ldec_cmp({la}, {lb}) == 0)"),
5791                    (Kind::Rational, Kind::Int) => format!("(ldec_cmp({la}, ldec_from_i64({b})) == 0)"),
5792                    (Kind::Int, Kind::Rational) => format!("(ldec_cmp(ldec_from_i64({a}), {lb}) == 0)"),
5793                    (Kind::Rational, Kind::BigInt) => format!("(ldec_cmp({la}, ldec_from_lint({lb})) == 0)"),
5794                    (Kind::BigInt, Kind::Rational) => format!("(ldec_cmp(ldec_from_lint({la}), {lb}) == 0)"),
5795                    (Kind::Rational, Kind::Float) => format!("(ldec_cmp_f64({la}, {lb}) == 0)"),
5796                    (Kind::Float, Kind::Rational) => format!("(ldec_cmp_f64({lb}, {la}) == 0)"),
5797                    (Kind::BigInt, Kind::Float) => format!("(lint_to_f64({la}) == {lb})"),
5798                    (Kind::Float, Kind::BigInt) => format!("({la} == lint_to_f64({lb}))"),
5799                    _ => format!("({la} == {lb})"),
5800                }
5801            }
5802            (Kind::Bool, Kind::Bool) | (Kind::Date, Kind::Date) | (Kind::Moment, Kind::Moment) => {
5803                format!("({a} == {b})")
5804            }
5805            (Kind::Word(x), Kind::Word(y)) => {
5806                if x == y {
5807                    format!("({a} == {b})")
5808                } else {
5809                    "0".to_string()
5810                }
5811            }
5812            (Kind::Text, Kind::Text) | (Kind::Peer, Kind::Peer) => {
5813                format!("str_equals((const char*)(intptr_t)({a}), (const char*)(intptr_t)({b}))")
5814            }
5815            (Kind::Nothing, Kind::Nothing) => "1".to_string(),
5816            (Kind::Nothing, Kind::Inductive(ii)) | (Kind::Inductive(ii), Kind::Nothing) => {
5817                let v = if ka == Kind::Nothing { b } else { a };
5818                match (
5819                    self.inductive(ii).0.as_str(),
5820                    self.ctor_tag("Option", "None"),
5821                ) {
5822                    ("Option", Some(none)) => format!("(((int64_t*)(intptr_t)({v}))[0] == {none})"),
5823                    _ => "0".to_string(),
5824                }
5825            }
5826            (Kind::Optional(oi), other) | (other, Kind::Optional(oi))
5827                if !matches!(other, Kind::Optional(_)) =>
5828            {
5829                let (o, v) = if matches!(ka, Kind::Optional(_)) {
5830                    (a, b)
5831                } else {
5832                    (b, a)
5833                };
5834                let inner = self.opt_inner(oi);
5835                if other == Kind::Nothing {
5836                    format!("(!((int64_t*)(intptr_t)({o}))[0])")
5837                } else {
5838                    let same = self.eq_expr(&format!("_o{n}[1]"), inner, v, other)?;
5839                    format!(
5840                        "({{ int64_t *_o{n} = (int64_t*)(intptr_t)({o}); _o{n}[0] && {same}; }})"
5841                    )
5842                }
5843            }
5844            (Kind::Optional(x), Kind::Optional(y)) => {
5845                let same = self.eq_expr(
5846                    &format!("_p{n}[1]"),
5847                    self.opt_inner(x),
5848                    &format!("_q{n}[1]"),
5849                    self.opt_inner(y),
5850                )?;
5851                format!("({{ int64_t *_p{n} = (int64_t*)(intptr_t)({a}), *_q{n} = (int64_t*)(intptr_t)({b}); (_p{n}[0] == _q{n}[0]) && (!_p{n}[0] || {same}); }})")
5852            }
5853            (x, y) if is_sequence(x) && is_sequence(y) => {
5854                let (sx, sy) = (seq_kind_of(x), seq_kind_of(y));
5855                let (tx, ty) = (sx.seq_c_type().unwrap(), sy.seq_c_type().unwrap());
5856                let (ex, ey) = (self.elem_kind(x), self.elem_kind(y));
5857                let same = if ex == Kind::Never || ey == Kind::Never {
5858                    "1".to_string()
5859                } else {
5860                    let (ca, cb) = (
5861                        elem_to_reg(&format!("_s{n}->data[_i{n}]"), ex),
5862                        elem_to_reg(&format!("_t{n}->data[_i{n}]"), ey),
5863                    );
5864                    self.eq_expr(&ca, ex, &cb, ey)?
5865                };
5866                format!(
5867                    "({{ {tx} *_s{n} = ({tx}*)(intptr_t)({a}); {ty} *_t{n} = ({ty}*)(intptr_t)({b}); int _e{n} = _s{n}->len == _t{n}->len; \
5868                     for (size_t _i{n} = 0; _e{n} && _i{n} < _s{n}->len; _i{n}++) _e{n} = {same}; _e{n}; }})"
5869                )
5870            }
5871            (Kind::Tuple(x), Kind::Tuple(y)) => {
5872                let (px, py) = (self.tuple_positions(x), self.tuple_positions(y));
5873                if px.len() != py.len() {
5874                    return Ok("0".to_string());
5875                }
5876                let mut parts = Vec::new();
5877                for (i, (kx, ky)) in px.iter().zip(py.iter()).enumerate() {
5878                    parts.push(self.eq_expr(
5879                        &format!("_s{n}[{i}]"),
5880                        *kx,
5881                        &format!("_t{n}[{i}]"),
5882                        *ky,
5883                    )?);
5884                }
5885                let all = if parts.is_empty() {
5886                    "1".to_string()
5887                } else {
5888                    parts.join(" && ")
5889                };
5890                format!("({{ int64_t *_s{n} = (int64_t*)(intptr_t)({a}), *_t{n} = (int64_t*)(intptr_t)({b}); {all}; }})")
5891            }
5892            (
5893                x @ (Kind::Struct(_) | Kind::StructSynth(_)),
5894                y @ (Kind::Struct(_) | Kind::StructSynth(_)),
5895            ) => {
5896                if self.struct_name_of_kind(x) != self.struct_name_of_kind(y) || x != y {
5897                    return Ok("0".to_string());
5898                }
5899                let name = self.struct_name_of_kind(x).unwrap_or_default();
5900                let fields: Vec<(usize, Kind)> = match x {
5901                    Kind::Struct(si) => {
5902                        let sd = self.prog.struct_types[si as usize].clone();
5903                        sd.fields
5904                            .iter()
5905                            .enumerate()
5906                            .map(|(c, (_, _))| (c, self.field_storage_kind(x, c).unwrap()))
5907                            .collect()
5908                    }
5909                    Kind::StructSynth(idx) => self.synth_structs.borrow()[idx as usize]
5910                        .1
5911                        .iter()
5912                        .enumerate()
5913                        .map(|(c, (_, _))| (c, self.field_storage_kind(x, c).unwrap()))
5914                        .collect(),
5915                    _ => unreachable!(),
5916                };
5917                let mut parts = Vec::new();
5918                for (c, fk) in fields {
5919                    let fk = if self.counter_fields.contains(&(name.clone(), c)) {
5920                        Kind::CrdtCounter
5921                    } else {
5922                        fk
5923                    };
5924                    let (ca, cb) = (format!("_s{n}[{c}]"), format!("_t{n}[{c}]"));
5925                    parts.push(if fk == Kind::CrdtCounter {
5926                        format!("(logos_crdt_sum((logos_crdt*)(intptr_t){ca}) == logos_crdt_sum((logos_crdt*)(intptr_t){cb}))")
5927                    } else {
5928                        self.eq_expr(&ca, fk, &cb, fk)?
5929                    });
5930                }
5931                let all = if parts.is_empty() {
5932                    "1".to_string()
5933                } else {
5934                    parts.join(" && ")
5935                };
5936                format!("({{ int64_t *_s{n} = (int64_t*)(intptr_t)({a}), *_t{n} = (int64_t*)(intptr_t)({b}); {all}; }})")
5937            }
5938            (Kind::Enum(x), Kind::Enum(y)) if x == y => {
5939                let ed = self.prog.enum_types[x as usize].clone();
5940                let arms: Vec<(String, Option<Vec<Kind>>)> = ed
5941                    .variants
5942                    .iter()
5943                    .map(|var| {
5944                        (
5945                            var.name.clone(),
5946                            Some(
5947                                var.field_types
5948                                    .iter()
5949                                    .map(|t| self.boundary_kind(&Some(t.clone())))
5950                                    .collect(),
5951                            ),
5952                        )
5953                    })
5954                    .collect();
5955                self.eq_constructors(a, b, &arms, n)?
5956            }
5957            (Kind::Inductive(x), Kind::Inductive(y)) => {
5958                let (tx, _) = self.inductive(x);
5959                let (ty, _) = self.inductive(y);
5960                if tx != ty {
5961                    return Ok("0".to_string());
5962                }
5963                let merged = self
5964                    .unify_inductive(x, y)
5965                    .ok_or("an equality of two differently-shaped enums")?;
5966                let Kind::Inductive(m) = merged else {
5967                    unreachable!()
5968                };
5969                let arms = self.inductive(m).1;
5970                self.eq_constructors(a, b, &arms, n)?
5971            }
5972            (
5973                Kind::SetInt | Kind::SetWord(_) | Kind::SetText | Kind::SetUnknown,
5974                Kind::SetInt | Kind::SetWord(_) | Kind::SetText | Kind::SetUnknown,
5975            ) => {
5976                let (sx, sy) = (resolve_set(ka).unwrap(), resolve_set(kb).unwrap());
5977                if sx != sy && ka != Kind::SetUnknown && kb != Kind::SetUnknown {
5978                    return Ok("0".to_string());
5979                }
5980                let (ty, infix) = (sx.set_c_type().unwrap(), sx.seq_infix().unwrap());
5981                format!(
5982                    "({{ {ty} *_s{n} = ({ty}*)(intptr_t)({a}), *_t{n} = ({ty}*)(intptr_t)({b}); int _e{n} = _s{n}->len == _t{n}->len; \
5983                     for (size_t _o{n} = 0; _e{n} && _o{n} < _s{n}->ord_len; _o{n}++) {{ size_t _k{n} = _s{n}->order[_o{n}]; if (_s{n}->state[_k{n}]) _e{n} = set_{infix}_contains(_t{n}, _s{n}->keys[_k{n}]); }} _e{n}; }})"
5984                )
5985            }
5986            (Kind::Map(_) | Kind::MapUnknown, Kind::Map(_) | Kind::MapUnknown) => {
5987                let ((kx, vx), (ky, vy)) = (
5988                    self.resolve_map_kv(ka).unwrap(),
5989                    self.resolve_map_kv(kb).unwrap(),
5990                );
5991                if kx != ky {
5992                    return Ok("0".to_string());
5993                }
5994                let (tx, ty) = (
5995                    map_type_name(kx, vx).unwrap(),
5996                    map_type_name(ky, vy).unwrap(),
5997                );
5998                let (fy, key) = (map_fn(ky, vy).unwrap(), format!("_s{n}->keys[_k{n}]"));
5999                let va = map_val_load(&format!("_s{n}->vals[_k{n}]"), vx);
6000                let vb = map_val_load(&format!("{fy}_get(_t{n}, {key})"), vy);
6001                let same = self.eq_expr(&va, vx, &vb, vy)?;
6002                format!(
6003                    "({{ {tx} *_s{n} = ({tx}*)(intptr_t)({a}); {ty} *_t{n} = ({ty}*)(intptr_t)({b}); int _e{n} = _s{n}->len == _t{n}->len; \
6004                     for (size_t _o{n} = 0; _e{n} && _o{n} < _s{n}->ord_len; _o{n}++) {{ size_t _k{n} = _s{n}->order[_o{n}]; if (_s{n}->state[_k{n}]) _e{n} = {fy}_contains(_t{n}, {key}) && {same}; }} _e{n}; }})"
6005                )
6006            }
6007            (Kind::Unknown, _) | (_, Kind::Unknown) => {
6008                return Err(format!("an equality over {ka:?} and {kb:?}"))
6009            }
6010            _ => "0".to_string(),
6011        })
6012    }
6013
6014    /// Equality of two values of one enum whose constructors, in tag order, carry the payload kinds `arms`:
6015    /// the same constructor with equal payloads.
6016    fn eq_constructors(
6017        &mut self,
6018        a: &str,
6019        b: &str,
6020        arms: &[(String, Option<Vec<Kind>>)],
6021        n: usize,
6022    ) -> Result<String, String> {
6023        let mut cases = String::new();
6024        for (tag, (_, payloads)) in arms.iter().enumerate() {
6025            let Some(payloads) = payloads else { continue };
6026            let mut parts = Vec::new();
6027            for (i, pk) in payloads.iter().enumerate() {
6028                parts.push(self.eq_expr(
6029                    &format!("_s{n}[{}]", i + 1),
6030                    *pk,
6031                    &format!("_t{n}[{}]", i + 1),
6032                    *pk,
6033                )?);
6034            }
6035            let all = if parts.is_empty() {
6036                "1".to_string()
6037            } else {
6038                parts.join(" && ")
6039            };
6040            cases.push_str(&format!("case {tag}: _e{n} = {all}; break; "));
6041        }
6042        Ok(format!(
6043            "({{ int64_t *_s{n} = (int64_t*)(intptr_t)({a}), *_t{n} = (int64_t*)(intptr_t)({b}); int _e{n} = _s{n}[0] == _t{n}[0]; if (_e{n}) switch (_s{n}[0]) {{ {cases}default: break; }} _e{n}; }})"
6044        ))
6045    }
6046
6047    /// A scalar value `v` (an `int64_t` C expression) of numeric kind `k` in the form the numeric runtime takes
6048    /// it: a `double` for a Real, an `LInt` for an exact integer, an `LDec` for a rational, the integer itself.
6049    /// Register `reg` (kind `k`) as the double the VM computes with beside a real: a rational's numerator over its
6050    /// denominator, any other number as [`as_double`] reads it.
6051    fn real_of(&self, reg: u16, k: Kind) -> String {
6052        if k == Kind::Rational {
6053            format!("ldec_to_f64(*(LDec*)(intptr_t)r[{reg}])")
6054        } else {
6055            as_double(reg, k)
6056        }
6057    }
6058
6059    /// The C expression of the machine integer an op reads from `reg` at `place`: the register; the `Int` a cell of the
6060    /// extension or a whole rational is, a fraction or one past i64 refused in the VM's words for the place (any
6061    /// other value of the extension in the interpreter's words for it).
6062    fn int_operand(&self, kinds: &[Kind], reg: u16, place: words::IntegerPlace) -> String {
6063        match kind_at(kinds, reg) {
6064            Kind::Tower => format!(
6065                "ltower_int_at((LTower)(intptr_t)r[{reg}], {}, {})",
6066                words::integer_past_a_word(place),
6067                words::fraction_for_an_integer(place)
6068            ),
6069            Kind::Rational => format!(
6070                "ldec_as_i64(*(LDec*)(intptr_t)r[{reg}], {}, {})",
6071                words::integer_past_a_word(place),
6072                words::fraction_for_an_integer(place)
6073            ),
6074            _ => format!("r[{reg}]"),
6075        }
6076    }
6077
6078    /// `r[dst] = lhs % rhs` with a rational operand: two exact numbers as `ldec_remainder` takes them; beside a real,
6079    /// the VM's refusal for the kinds the two values are, a whole rational being the integer it equals.
6080    fn rational_remainder(&mut self, dst: u16, lhs: u16, rhs: u16, lk: Kind, rk: Kind) -> Result<(), String> {
6081        if lk != Kind::Float && rk != Kind::Float {
6082            let (l, r) = (as_ldec(lhs, lk)?, as_ldec(rhs, rk)?);
6083            self.box_ldec(dst, &format!("ldec_remainder({l}, {r})"));
6084            return Ok(());
6085        }
6086        let fraction = RuntimeValue::from_rational(
6087            logicaffeine_base::Rational::from_ratio_i64(1, 2).expect("a nonzero denominator"),
6088        );
6089        let side = |k: Kind, whole: bool| {
6090            if k == Kind::Rational && !whole {
6091                Some(fraction.clone())
6092            } else {
6093                sample_value(if k == Kind::Rational { Kind::Int } else { k })
6094            }
6095        };
6096        let refused = |whole: bool| -> Result<String, String> {
6097            let (Some(l), Some(r)) = (side(lk, whole), side(rk, whole)) else {
6098                return Err(format!("a remainder of {lk:?} and {rk:?}"));
6099            };
6100            Ok(words::modulo_refused(l, r))
6101        };
6102        let (whole, fractional) = (refused(true)?, refused(false)?);
6103        let rational = if lk == Kind::Rational { lhs } else { rhs };
6104        self.line(&format!(
6105            "logos_fatalf(ldec_whole((LDec*)(intptr_t)r[{rational}]) ? {whole} : {fractional});"
6106        ));
6107        Ok(())
6108    }
6109
6110    /// The sequence kind of a list literal whose items are of kinds `items`: one kind (the forms of one integer aside),
6111    /// or integers beside rationals, held as rationals; the VM keeps each item the value it is, so items of other
6112    /// different kinds are refused by name.
6113    fn list_kind(&self, items: &[Kind]) -> Result<Kind, String> {
6114        let integer = |k: Kind| matches!(k, Kind::Int | Kind::BigInt);
6115        let exact = |k: Kind| integer(k) || k == Kind::Rational;
6116        let known: Vec<Kind> = items.iter().copied().filter(|k| *k != Kind::Never).collect();
6117        let Some(&first) = known.first() else {
6118            return Ok(self.seq_kind_for(items.first().copied().unwrap_or(Kind::Unknown)));
6119        };
6120        if known.iter().all(|k| *k == first || (integer(*k) && integer(first))) {
6121            return Ok(self.seq_kind_for(first));
6122        }
6123        if known.iter().all(|k| exact(*k)) {
6124            return Ok(self.seq_kind_for(Kind::Rational));
6125        }
6126        Err("a list whose items differ in kind is not yet lowered".to_string())
6127    }
6128
6129    /// Whether a value of kind `from` is handed to a place declared `to` as the exact number it equals: an integer
6130    /// where a rational is declared, a list of integers where a list of rationals is.
6131    fn widens_to(&self, from: Kind, to: Kind) -> bool {
6132        match (from, to) {
6133            (Kind::Int | Kind::BigInt, Kind::Rational) => true,
6134            (Kind::SeqInt, Kind::SeqOf(_)) => self.elem_kind(to) == Kind::Rational,
6135            _ => false,
6136        }
6137    }
6138
6139    /// Register `reg` (kind `k`) as an item of a sequence whose items are of kind `elem`: an integer stored among
6140    /// rationals is the rational it equals.
6141    fn elem_value(&mut self, reg: u16, k: Kind, elem: Kind) -> Result<String, String> {
6142        if elem == Kind::Rational && matches!(k, Kind::Int | Kind::BigInt) {
6143            return self.convert_number(&format!("r[{reg}]"), k, Kind::Rational);
6144        }
6145        Ok(as_elem_push(reg, elem))
6146    }
6147
6148    /// `r[dst]` set to a fresh box holding the `LDec` expression `value`.
6149    fn box_ldec(&mut self, dst: u16, value: &str) {
6150        let n = self.fresh();
6151        self.line(&format!(
6152            "LDec *_rat{n} = (LDec*)lc_gc_alloc(sizeof(LDec)); *_rat{n} = {value}; r[{dst}] = (int64_t)(intptr_t)_rat{n};"
6153        ));
6154    }
6155
6156    fn scalar_c(&self, v: &str, k: Kind) -> String {
6157        match k {
6158            Kind::Float => format!("as_f64({v})"),
6159            Kind::BigInt => format!("lc_xget({v})"),
6160            Kind::Rational => format!("(*(LDec*)(intptr_t)({v}))"),
6161            _ => format!("({v})"),
6162        }
6163    }
6164
6165    /// Register `reg` (of kind `k`) as text, the way `combined with`, interpolation and `format` read it: a
6166    /// scalar as `Show` renders it, any other value as its display.
6167    fn text_of(&mut self, reg: u16, k: Kind) -> Result<String, String> {
6168        match k {
6169            Kind::Text | Kind::Bool | Kind::Float | Kind::Rational | Kind::BigInt | Kind::Int => {
6170                Ok(coerce_cstr(reg, k))
6171            }
6172            Kind::Word(_) => Ok(format!(
6173                "logos_dyn_sprintf(\"%\" PRIu64, (uint64_t)r[{reg}])"
6174            )),
6175            other => self.render(&format!("r[{reg}]"), other),
6176        }
6177    }
6178
6179    fn lower_op(
6180        &mut self,
6181        op: &Op,
6182        kinds: &[Kind],
6183        after: &[Kind],
6184        in_function: bool,
6185        pc: usize,
6186    ) -> Result<(), String> {
6187        match op {
6188            Op::LoadConst { dst, idx } => match &self.prog.constants[*idx as usize] {
6189                Constant::Int(n) => self.line(&format!("r[{dst}] = {n}LL;")),
6190                Constant::Duration(n) => self.line(&format!("r[{dst}] = {n}LL;")),
6191                Constant::Bool(b) => self.line(&format!("r[{dst}] = {};", if *b { 1 } else { 0 })),
6192                Constant::Float(f) => self.line(&format!(
6193                    "r[{dst}] = (int64_t)0x{:016X}ULL; /* {} */",
6194                    f.to_bits(),
6195                    f
6196                )),
6197                Constant::Text(s) => self.line(&format!(
6198                    "r[{dst}] = (int64_t)(intptr_t)\"{}\";",
6199                    c_escape(s)
6200                )),
6201                // `Nothing` — the seed value of a CRDT register/counter field (a `Divergent T` / counter before
6202                // its first write) and the empty-`Optional` sentinel. It rides the slot as `0` (an empty
6203                // pointer / zero int); a following `StructInsert` / `CrdtBump` / `CrdtResolve` establishes the
6204                // real cell value, matching the VM's `RuntimeValue::Nothing` initial field state.
6205                Constant::Nothing => self.line(&format!("r[{dst}] = 0;")),
6206                Constant::Date(days) => self.line(&format!("r[{dst}] = {days}LL;")),
6207                Constant::Moment(nanos) | Constant::Time(nanos) => self.line(&format!("r[{dst}] = {nanos}LL;")),
6208                Constant::Span { months, days } => self.line(&format!(
6209                    "r[{dst}] = (int64_t)(((uint64_t)(uint32_t){months} << 32) | (uint32_t){days});"
6210                )),
6211                c => return Err(format!("LoadConst {c:?} not yet lowered")),
6212            },
6213            Op::Move { dst, src } => self.line(&format!("r[{dst}] = r[{src}];")),
6214            // An exact result (or operand) goes through the tagged `lc_x*` operations; anything else takes the
6215            // machine path.
6216            Op::Add { dst, lhs, rhs }
6217                if self.exact_integers(&[*lhs, *rhs], kinds, kind_at(after, *dst)) =>
6218            {
6219                self.emit_exact_bin(
6220                    *dst,
6221                    *lhs,
6222                    *rhs,
6223                    kind_at(kinds, *lhs),
6224                    kind_at(kinds, *rhs),
6225                    "lc_xadd",
6226                )?;
6227            }
6228            Op::Add { dst, lhs, rhs } => self.arith(*dst, *lhs, *rhs, "+", kinds)?,
6229            Op::Sub { dst, lhs, rhs }
6230                if self.exact_integers(&[*lhs, *rhs], kinds, kind_at(after, *dst)) =>
6231            {
6232                self.emit_exact_bin(
6233                    *dst,
6234                    *lhs,
6235                    *rhs,
6236                    kind_at(kinds, *lhs),
6237                    kind_at(kinds, *rhs),
6238                    "lc_xsub",
6239                )?;
6240            }
6241            Op::Sub { dst, lhs, rhs } => self.arith(*dst, *lhs, *rhs, "-", kinds)?,
6242            Op::Mul { dst, lhs, rhs }
6243                if self.exact_integers(&[*lhs, *rhs], kinds, kind_at(after, *dst)) =>
6244            {
6245                self.emit_exact_bin(
6246                    *dst,
6247                    *lhs,
6248                    *rhs,
6249                    kind_at(kinds, *lhs),
6250                    kind_at(kinds, *rhs),
6251                    "lc_xmul",
6252                )?;
6253            }
6254            Op::Mul { dst, lhs, rhs } => self.arith(*dst, *lhs, *rhs, "*", kinds)?,
6255            // A quotient or remainder of exact integers: exact when its result is, else the machine integer it
6256            // provably fits.
6257            Op::Div { dst, lhs, rhs }
6258                if self.exact_integers(&[*lhs, *rhs], kinds, kind_at(after, *dst)) =>
6259            {
6260                self.emit_exact_bounded(*dst, *lhs, *rhs, kinds, kind_at(after, *dst), "lc_xdiv")?;
6261            }
6262            Op::Div { dst, lhs, rhs } => self.arith(*dst, *lhs, *rhs, "/", kinds)?,
6263            // Exact rational division (`Let x: Rational be 7 / 2` → `7/2`, never floored). Operands are Int or
6264            // Rational; box the reduced `LDec` result and store its pointer in the register. Reuses the runtime's
6265            // exact `ldec_div` (num/den over `LBig`) verbatim — the same helper the AST C backend calls.
6266            Op::ExactDiv { dst, lhs, rhs } => {
6267                let (lk, rk) = (kind_at(kinds, *lhs), kind_at(kinds, *rhs));
6268                let (l, r) = (as_ldec(*lhs, lk)?, as_ldec(*rhs, rk)?);
6269                self.line(&format!("lc_exact_divisor({});", exact_zero_test(*rhs, rk)));
6270                let n = self.fresh();
6271                self.line(&format!(
6272                    "LDec *_rat{n} = (LDec*)lc_gc_alloc(sizeof(LDec)); *_rat{n} = ldec_div({l}, {r}); r[{dst}] = (int64_t)(intptr_t)_rat{n};"
6273                ));
6274            }
6275            // An integer remainder by zero fails as the VM's does; the VM computes no remainder of a Real, so
6276            // neither does the walker — it fails with the VM's refusal for those two operands.
6277            Op::Mod { dst, lhs, rhs }
6278                if self.exact_integers(&[*lhs, *rhs], kinds, kind_at(after, *dst)) =>
6279            {
6280                self.emit_exact_bounded(*dst, *lhs, *rhs, kinds, kind_at(after, *dst), "lc_xmod")?;
6281            }
6282            Op::Mod { dst, lhs, rhs } => {
6283                let (lk, rk) = (kind_at(kinds, *lhs), kind_at(kinds, *rhs));
6284                if lk == Kind::Rational || rk == Kind::Rational {
6285                    self.rational_remainder(*dst, *lhs, *rhs, lk, rk)?;
6286                } else if matches!(lk, Kind::Word(_)) || matches!(rk, Kind::Word(_)) {
6287                    // A word's remainder is unsigned, in its ring, as `/` is.
6288                    self.word_arith(*dst, *lhs, *rhs, "%", lk, rk)?;
6289                } else if lk == Kind::Float || rk == Kind::Float {
6290                    let (lv, rv) = (sample_value(lk), sample_value(rk));
6291                    let (Some(lv), Some(rv)) = (lv, rv) else {
6292                        return Err(format!("a remainder of {lk:?} and {rk:?}"));
6293                    };
6294                    self.line(&format!("logos_fatalf({});", words::modulo_refused(lv, rv)));
6295                } else {
6296                    self.line(&format!("r[{dst}] = logos_imod(r[{lhs}], r[{rhs}]);"));
6297                }
6298            }
6299            Op::AddAssign { dst, src }
6300                if self.exact_integers(&[*dst, *src], kinds, kind_at(after, *dst)) =>
6301            {
6302                self.emit_exact_bin(
6303                    *dst,
6304                    *dst,
6305                    *src,
6306                    kind_at(kinds, *dst),
6307                    kind_at(kinds, *src),
6308                    "lc_xadd",
6309                )?;
6310            }
6311            Op::AddAssign { dst, src } => {
6312                let (dk, sk) = (kind_at(kinds, *dst), kind_at(kinds, *src));
6313                if dk == Kind::Text {
6314                    // `Set s to s + …` on Text appends to the text the register owns, in place. A number's
6315                    // rendering is a fresh temporary nothing else holds, released once appended.
6316                    if sk == Kind::Int
6317                        && crate::optimize::active_config()
6318                            .is_on(crate::optimization::Opt::StringAccumReuse)
6319                    {
6320                        crate::optimize::mark_fired(crate::optimization::Opt::StringAccumReuse);
6321                        self.line(&format!("r[{dst}] = lc_text_append_i64(r[{dst}], own[{dst}], r[{src}]); own[{dst}] = 1;"));
6322                        return Ok(());
6323                    }
6324                    let tail = self.text_of(*src, sk)?;
6325                    if !matches!(sk, Kind::Int | Kind::Float | Kind::Rational | Kind::BigInt) {
6326                        self.line(&format!("r[{dst}] = lc_text_append(r[{dst}], own[{dst}], {tail}); own[{dst}] = 1;"));
6327                    } else {
6328                        let n = self.fresh();
6329                        self.line(&format!(
6330                            "{{ char *_tail{n} = (char*)({tail}); r[{dst}] = lc_text_append(r[{dst}], own[{dst}], _tail{n}); lc_gc_free(_tail{n}); own[{dst}] = 1; }}"
6331                        ));
6332                    }
6333                } else if sk == Kind::Text {
6334                    let (l, r) = (self.text_of(*dst, dk)?, self.text_of(*src, sk)?);
6335                    self.line(&format!(
6336                        "r[{dst}] = (int64_t)(intptr_t)str_concat({l}, {r});"
6337                    ));
6338                } else if dk == Kind::Rational || sk == Kind::Rational {
6339                    self.arith(*dst, *dst, *src, "+", kinds)?;
6340                } else if matches!(dk, Kind::Word(_)) || matches!(sk, Kind::Word(_)) {
6341                    // A word adds in its ring, as `+` does.
6342                    self.word_arith(*dst, *dst, *src, "+", dk, sk)?;
6343                } else if dk == Kind::Float || sk == Kind::Float {
6344                    self.line(&format!(
6345                        "r[{dst}] = as_bits({} + {});",
6346                        as_double(*dst, dk),
6347                        as_double(*src, sk)
6348                    ));
6349                } else {
6350                    self.line(&format!("r[{dst}] = r[{dst}] + r[{src}];"));
6351                }
6352            }
6353            Op::Pow { dst, lhs, rhs } => {
6354                let (lk, rk) = (kind_at(kinds, *lhs), kind_at(kinds, *rhs));
6355                if (lk == Kind::Rational || rk == Kind::Rational) && lk != Kind::Float && rk != Kind::Float {
6356                    let (b, e) = (as_ldec(*lhs, lk)?, as_ldec(*rhs, rk)?);
6357                    self.box_ldec(*dst, &format!("ldec_power({b}, {e})"));
6358                } else if lk == Kind::Rational || rk == Kind::Rational {
6359                    self.line(&format!(
6360                        "r[{dst}] = as_bits(pow({}, {}));",
6361                        self.real_of(*lhs, lk),
6362                        self.real_of(*rhs, rk)
6363                    ));
6364                } else if kind_at(after, *dst) == Kind::BigInt {
6365                    let base = as_exact(*lhs, lk)?;
6366                    let exponent = if rk == Kind::BigInt {
6367                        format!("lc_xnarrow(r[{rhs}])")
6368                    } else {
6369                        format!("r[{rhs}]")
6370                    };
6371                    self.line(&format!("r[{dst}] = lc_xpow({base}, {exponent});"));
6372                } else if lk == Kind::Float || rk == Kind::Float {
6373                    self.line(&format!(
6374                        "r[{dst}] = as_bits(pow({}, {}));",
6375                        as_double(*lhs, lk),
6376                        as_double(*rhs, rk)
6377                    ));
6378                } else {
6379                    self.line(&format!("r[{dst}] = ipow64(r[{lhs}], r[{rhs}]);"));
6380                }
6381            }
6382            Op::FloorDiv { dst, lhs, rhs }
6383                if self.exact_integers(&[*lhs, *rhs], kinds, kind_at(after, *dst)) =>
6384            {
6385                self.emit_exact_bounded(
6386                    *dst,
6387                    *lhs,
6388                    *rhs,
6389                    kinds,
6390                    kind_at(after, *dst),
6391                    "lc_xfloordiv",
6392                )?;
6393            }
6394            Op::FloorDiv { dst, lhs, rhs } => {
6395                let (lk, rk) = (kind_at(kinds, *lhs), kind_at(kinds, *rhs));
6396                if lk == Kind::Float || rk == Kind::Float {
6397                    self.line(&format!(
6398                        "r[{dst}] = as_bits(lc_rfldiv({}, {}));",
6399                        self.real_of(*lhs, lk),
6400                        self.real_of(*rhs, rk)
6401                    ));
6402                } else if lk == Kind::Rational || rk == Kind::Rational {
6403                    let (l, r) = (as_ldec(*lhs, lk)?, as_ldec(*rhs, rk)?);
6404                    self.box_ldec(*dst, &format!("ldec_floor_quotient({l}, {r})"));
6405                } else {
6406                    self.line(&format!("r[{dst}] = fldiv64(r[{lhs}], r[{rhs}]);"));
6407                }
6408            }
6409            // Scalar math builtins called as functions (`sqrt(x)`, `abs(x)`, `floor(x)`, `min(a,b)`, …). Args are
6410            // already evaluated into consecutive registers at `args_start`. Mirrors `codegen_riscv::call_builtin`
6411            // exactly: Sqrt is Int/Float→Float; Abs is fabs (Float) / wrapping i64 (Int, i64::MIN is the bignum
6412            // gap); Floor/Ceil/Round land an i64 (Float via libm then truncate, Int passthrough); Min/Max promote
6413            // to Float if either arg is Float, else integer compare-select. Non-math builtins are refused.
6414            Op::CallBuiltin {
6415                dst,
6416                builtin,
6417                args_start,
6418                ..
6419            } => {
6420                use crate::semantics::builtins::BuiltinId;
6421                let a = *args_start;
6422                let ak = kind_at(kinds, a);
6423                match builtin {
6424                    // `Fail with <value>.` / a broken contract's reason: the reason on stderr, exit 1 — the
6425                    // report every tier gives. A contract reason that is not Text is refused in the other
6426                    // tiers' words; a kind this backend cannot render is refused at generation, never
6427                    // printed as a pointer.
6428                    BuiltinId::Fail | BuiltinId::FailReason => {
6429                        let text = match (builtin, ak) {
6430                            (_, Kind::Text) => as_cstr(a),
6431                            (BuiltinId::Fail, Kind::Int | Kind::Bool | Kind::Float | Kind::Rational | Kind::BigInt) => {
6432                                coerce_cstr(a, ak)
6433                            }
6434                            (BuiltinId::FailReason, Kind::Int | Kind::BigInt) => {
6435                                "\"The reason after `otherwise` must be Text, but it was Int.\"".to_string()
6436                            }
6437                            (BuiltinId::FailReason, Kind::Float) => {
6438                                "\"The reason after `otherwise` must be Text, but it was Real.\"".to_string()
6439                            }
6440                            (BuiltinId::FailReason, Kind::Bool) => {
6441                                "\"The reason after `otherwise` must be Text, but it was Bool.\"".to_string()
6442                            }
6443                            (_, k) => return Err(format!("a failure reason of kind {k:?} is not yet rendered by the C backend")),
6444                        };
6445                        self.line(&format!(
6446                            "fflush(stdout); fprintf(stderr, \"%s\\n\", {text}); exit(1);"
6447                        ));
6448                    }
6449                    // `format(x)` — the value's text exactly as `Show` renders it.
6450                    BuiltinId::Format => match ak {
6451                        Kind::Text => self.line(&format!("r[{dst}] = r[{a}];")),
6452                        k => {
6453                            let text = self.text_of(a, k)?;
6454                            self.line(&format!("r[{dst}] = (int64_t)(intptr_t)({text});"));
6455                        }
6456                    },
6457                    BuiltinId::Sqrt => {
6458                        self.line(&format!("r[{dst}] = as_bits(sqrt({}));", as_double(a, ak)));
6459                    }
6460                    BuiltinId::Abs => match ak {
6461                        Kind::Float => {
6462                            self.line(&format!("r[{dst}] = as_bits(fabs(as_f64(r[{a}])));"))
6463                        }
6464                        Kind::Int => {
6465                            self.line(&format!("r[{dst}] = (r[{a}] < 0) ? -r[{a}] : r[{a}];"))
6466                        }
6467                        k => return Err(format!("abs of a non-number kind {k:?}")),
6468                    },
6469                    BuiltinId::Floor | BuiltinId::Ceil | BuiltinId::Round => match ak {
6470                        Kind::Float => {
6471                            let f = match builtin {
6472                                BuiltinId::Floor => "floor",
6473                                BuiltinId::Ceil => "ceil",
6474                                _ => "round",
6475                            };
6476                            self.line(&format!("r[{dst}] = (int64_t){f}(as_f64(r[{a}]));"));
6477                        }
6478                        Kind::Int => self.line(&format!("r[{dst}] = r[{a}];")),
6479                        k => return Err(format!("floor/ceil/round of a non-number kind {k:?}")),
6480                    },
6481                    BuiltinId::Min | BuiltinId::Max => {
6482                        let b = *args_start + 1;
6483                        let bk = kind_at(kinds, b);
6484                        if ak == Kind::Float || bk == Kind::Float {
6485                            let f = if matches!(builtin, BuiltinId::Min) {
6486                                "logos_min_f64"
6487                            } else {
6488                                "logos_max_f64"
6489                            };
6490                            self.line(&format!(
6491                                "r[{dst}] = as_bits({f}({}, {}));",
6492                                as_double(a, ak),
6493                                as_double(b, bk)
6494                            ));
6495                        } else {
6496                            let op = if matches!(builtin, BuiltinId::Min) {
6497                                "<"
6498                            } else {
6499                                ">"
6500                            };
6501                            self.line(&format!(
6502                                "r[{dst}] = (r[{a}] {op} r[{b}]) ? r[{a}] : r[{b}];"
6503                            ));
6504                        }
6505                    }
6506                    // `text_bytes(t)` — the text's UTF-8 bytes; `text_from_bytes(bs)` — back, validated.
6507                    BuiltinId::TextBytes => {
6508                        if self.text_byte_calls.contains(&pc) {
6509                            self.line(&format!("r[{dst}] = r[{a}];"));
6510                        } else {
6511                            let n = self.fresh();
6512                            self.line(&format!(
6513                                "Seq_i64 *_tb{n} = (Seq_i64*)lc_gc_alloc(sizeof(Seq_i64)); *_tb{n} = logos_text_bytes({}); r[{dst}] = (int64_t)(intptr_t)_tb{n};",
6514                                as_cstr(a)
6515                            ));
6516                        }
6517                    }
6518                    BuiltinId::TextFromBytes => {
6519                        let ptr = Self::seq_ptr(a, seq_kind_of(ak))?;
6520                        self.line(&format!(
6521                            "r[{dst}] = (int64_t)(intptr_t)lc_text_from_bytes({ptr});"
6522                        ));
6523                    }
6524                    // `contains(haystack, needle)` — a substring test over two Texts, refused as the VM refuses
6525                    // any other pair.
6526                    BuiltinId::Contains => {
6527                        let b = a + 1;
6528                        let bk = kind_at(kinds, b);
6529                        if ak == Kind::Text && bk == Kind::Text {
6530                            self.line(&format!(
6531                                "r[{dst}] = (strstr({}, {}) != NULL);",
6532                                as_cstr(a),
6533                                as_cstr(b)
6534                            ));
6535                        } else {
6536                            let (Some(x), Some(y)) = (sample_value(ak), sample_value(bk)) else {
6537                                return Err(format!("contains over {ak:?} and {bk:?}"));
6538                            };
6539                            self.line(&format!(
6540                                "logos_fatalf({});",
6541                                words::builtin_refused(*builtin, vec![x, y])
6542                            ));
6543                        }
6544                    }
6545                    // The file statements, against the sandboxed file system (`c_vfs_runtime`): the content and the
6546                    // path are displayed, as the VM displays them.
6547                    BuiltinId::VfsWriteFile => {
6548                        let content = self.text_of(a, ak)?;
6549                        let path = self.text_of(a + 1, kind_at(kinds, a + 1))?;
6550                        self.line(&format!(
6551                            "logos_vfs_write({path}, {content}); r[{dst}] = 0;"
6552                        ));
6553                    }
6554                    BuiltinId::VfsReadFile | BuiltinId::VfsMount => {
6555                        let path = self.text_of(a, ak)?;
6556                        let f = if *builtin == BuiltinId::VfsReadFile {
6557                            "logos_vfs_read"
6558                        } else {
6559                            "logos_vfs_mount"
6560                        };
6561                        self.line(&format!("r[{dst}] = (int64_t)(intptr_t){f}({path});"));
6562                    }
6563                    // The bit counts of an Int's 64-bit two's-complement pattern (64 zeros for 0).
6564                    BuiltinId::CountOnes | BuiltinId::LeadingZeros | BuiltinId::TrailingZeros => {
6565                        match ak {
6566                            Kind::Int => {
6567                                let count = match builtin {
6568                                BuiltinId::CountOnes => format!("__builtin_popcountll((unsigned long long)r[{a}])"),
6569                                BuiltinId::LeadingZeros => format!("(r[{a}] ? __builtin_clzll((unsigned long long)r[{a}]) : 64)"),
6570                                _ => format!("(r[{a}] ? __builtin_ctzll((unsigned long long)r[{a}]) : 64)"),
6571                            };
6572                                self.line(&format!("r[{dst}] = (int64_t){count};"));
6573                            }
6574                            k => {
6575                                let sample = sample_value(k)
6576                                    .ok_or_else(|| format!("a bit count of a {k:?}"))?;
6577                                self.line(&format!(
6578                                    "logos_fatalf({});",
6579                                    words::builtin_refused(*builtin, vec![sample])
6580                                ));
6581                            }
6582                        }
6583                    }
6584                    // Machine words: a word from an Int or another word (truncated to the width), a word's value as
6585                    // an Int, rotations and bitwise words — each refused, for any other operand, in the VM's words.
6586                    BuiltinId::Word8Make
6587                    | BuiltinId::Word16Make
6588                    | BuiltinId::Word32
6589                    | BuiltinId::Word64 => match ak {
6590                        Kind::Int | Kind::Word(_) | Kind::BigInt => {
6591                            let bits = match builtin {
6592                                BuiltinId::Word8Make => 8,
6593                                BuiltinId::Word16Make => 16,
6594                                BuiltinId::Word32 => 32,
6595                                _ => 64,
6596                            };
6597                            let mask = if bits == 64 {
6598                                "UINT64_MAX".to_string()
6599                            } else {
6600                                format!("((1ULL << {bits}) - 1)")
6601                            };
6602                            let low = if ak == Kind::BigInt {
6603                                format!("lc_xword_bits(r[{a}])")
6604                            } else {
6605                                format!("(uint64_t)r[{a}]")
6606                            };
6607                            self.line(&format!("r[{dst}] = (int64_t)({low} & {mask});"));
6608                        }
6609                        k => self.builtin_refusal(*builtin, &[k])?,
6610                    },
6611                    BuiltinId::IntOfWord8
6612                    | BuiltinId::IntOfWord16
6613                    | BuiltinId::IntOfWord32
6614                    | BuiltinId::IntOfWord64 => match (builtin, ak) {
6615                        (BuiltinId::IntOfWord8, Kind::Int) => {
6616                            self.line(&format!("r[{dst}] = (int64_t)(uint8_t)r[{a}];"))
6617                        }
6618                        (BuiltinId::IntOfWord16, Kind::Int) => {
6619                            self.line(&format!("r[{dst}] = (int64_t)(uint16_t)r[{a}];"))
6620                        }
6621                        (_, Kind::Int | Kind::Word(_)) => self.line(&format!("r[{dst}] = r[{a}];")),
6622                        (_, k) => self.builtin_refusal(*builtin, &[k])?,
6623                    },
6624                    BuiltinId::Rotl | BuiltinId::Rotr => {
6625                        let nk = kind_at(kinds, a + 1);
6626                        match (ak, nk) {
6627                            (Kind::Word(bits), Kind::Int | Kind::Word(_)) => {
6628                                let mask = if bits == 64 {
6629                                    "UINT64_MAX".to_string()
6630                                } else {
6631                                    format!("((1ULL << {bits}) - 1)")
6632                                };
6633                                let (there, back) = if *builtin == BuiltinId::Rotl {
6634                                    ("<<", ">>")
6635                                } else {
6636                                    (">>", "<<")
6637                                };
6638                                let n = self.fresh();
6639                                self.line(&format!(
6640                                    "{{ uint64_t _w{n} = (uint64_t)r[{a}]; uint32_t _c{n} = (uint32_t)r[{}] % {bits}; r[{dst}] = (int64_t)(_c{n} ? (((_w{n} {there} _c{n}) | (_w{n} {back} ({bits} - _c{n}))) & {mask}) : _w{n}); }}",
6641                                    a + 1
6642                                ));
6643                            }
6644                            (x, y) => self.builtin_refusal(*builtin, &[x, y])?,
6645                        }
6646                    }
6647                    BuiltinId::Wand | BuiltinId::Wor => {
6648                        let bk = kind_at(kinds, a + 1);
6649                        match (ak, bk) {
6650                            (Kind::Word(x), Kind::Word(y)) if x == y => {
6651                                let op = if *builtin == BuiltinId::Wand {
6652                                    "&"
6653                                } else {
6654                                    "|"
6655                                };
6656                                self.line(&format!("r[{dst}] = r[{a}] {op} r[{}];", a + 1));
6657                            }
6658                            (x, y) => self.builtin_refusal(*builtin, &[x, y])?,
6659                        }
6660                    }
6661                    BuiltinId::Wnot => match ak {
6662                        Kind::Word(bits) => {
6663                            let mask = if bits == 64 {
6664                                "UINT64_MAX".to_string()
6665                            } else {
6666                                format!("((1ULL << {bits}) - 1)")
6667                            };
6668                            self.line(&format!(
6669                                "r[{dst}] = (int64_t)(~(uint64_t)r[{a}] & {mask});"
6670                            ));
6671                        }
6672                        k => self.builtin_refusal(*builtin, &[k])?,
6673                    },
6674                    BuiltinId::Word32Shr => {
6675                        let nk = kind_at(kinds, a + 1);
6676                        match (ak, nk) {
6677                            (Kind::Int | Kind::Word(_), Kind::Int) => {
6678                                self.line(&format!("r[{dst}] = (int64_t)(uint32_t)((uint32_t)r[{a}] >> ((uint32_t)r[{}] & 31));", a + 1));
6679                            }
6680                            (x, y) => self.builtin_refusal(*builtin, &[x, y])?,
6681                        }
6682                    }
6683                    BuiltinId::Word64Shl | BuiltinId::Word64Shr => {
6684                        let nk = kind_at(kinds, a + 1);
6685                        match (ak, nk) {
6686                            (Kind::Int | Kind::Word(_), Kind::Int) => {
6687                                let dir = if *builtin == BuiltinId::Word64Shl {
6688                                    "<<"
6689                                } else {
6690                                    ">>"
6691                                };
6692                                self.line(&format!("r[{dst}] = (int64_t)((uint64_t)r[{a}] {dir} ((uint32_t)r[{}] & 63));", a + 1));
6693                            }
6694                            (x, y) => self.builtin_refusal(*builtin, &[x, y])?,
6695                        }
6696                    }
6697                    BuiltinId::Word64And => {
6698                        let bk = kind_at(kinds, a + 1);
6699                        match (ak, bk) {
6700                            (Kind::Int | Kind::Word(_), Kind::Int | Kind::Word(_)) => {
6701                                self.line(&format!(
6702                                    "r[{dst}] = (int64_t)((uint64_t)r[{a}] & (uint64_t)r[{}]);",
6703                                    a + 1
6704                                ));
6705                            }
6706                            (x, y) => self.builtin_refusal(*builtin, &[x, y])?,
6707                        }
6708                    }
6709                    BuiltinId::ReadReals => {
6710                        let path = self.text_of(a, ak)?;
6711                        let n = self.fresh();
6712                        self.line(&format!(
6713                            "Seq_f64 *_rr{n} = (Seq_f64*)lc_gc_alloc(sizeof(Seq_f64)); *_rr{n} = logos_read_reals({path}); r[{dst}] = (int64_t)(intptr_t)_rr{n};"
6714                        ));
6715                    }
6716                    BuiltinId::TryReadBytes => {
6717                        let path = self.text_of(a, ak)?;
6718                        let (ok, err) = (
6719                            self.ctor_tag("Result", "Ok").unwrap(),
6720                            self.ctor_tag("Result", "Err").unwrap(),
6721                        );
6722                        self.line(&format!(
6723                            "r[{dst}] = lc_try_read_bytes({path}, {ok}, {err});"
6724                        ));
6725                    }
6726                    BuiltinId::Chr => match ak {
6727                        Kind::Int => {
6728                            self.line(&format!("r[{dst}] = (int64_t)(intptr_t)lc_chr(r[{a}]);"))
6729                        }
6730                        k => {
6731                            let sample =
6732                                sample_value(k).ok_or_else(|| format!("chr of a {k:?}"))?;
6733                            self.line(&format!(
6734                                "logos_fatalf({});",
6735                                words::builtin_refused(*builtin, vec![sample])
6736                            ));
6737                        }
6738                    },
6739                    // `parseInt(text)` — refused as the VM refuses anything but Text.
6740                    BuiltinId::ParseInt => match ak {
6741                        Kind::Text => {
6742                            self.line(&format!("r[{dst}] = lc_parse_int({});", as_cstr(a)))
6743                        }
6744                        k => {
6745                            let sample =
6746                                sample_value(k).ok_or_else(|| format!("parseInt of a {k:?}"))?;
6747                            self.line(&format!(
6748                                "logos_fatalf({});",
6749                                words::builtin_refused(*builtin, vec![sample])
6750                            ));
6751                        }
6752                    },
6753                    // `randomInt(lo, hi)`: a uniform draw from `lo..=hi`, refused in the VM's words when the bounds
6754                    // are crossed or are not integers.
6755                    BuiltinId::RandomInt => match (ak, kind_at(kinds, a + 1)) {
6756                        (Kind::Int, Kind::Int) => {
6757                            self.line(&format!("r[{dst}] = lc_random_int(r[{a}], r[{}]);", a + 1))
6758                        }
6759                        (x, y) => self.builtin_refusal(*builtin, &[x, y])?,
6760                    },
6761                    BuiltinId::RandomFloat => {
6762                        self.line(&format!("r[{dst}] = as_bits(lc_random_float());"))
6763                    }
6764                    // `get(name)`'s two halves, as the VM expands it: whether the process environment sets the
6765                    // variable, and its text (`""` when unset) — refused in the VM's words for a name that is
6766                    // not Text.
6767                    BuiltinId::EnvHas => match ak {
6768                        Kind::Text => {
6769                            self.line(&format!("r[{dst}] = (getenv({}) != NULL);", as_cstr(a)))
6770                        }
6771                        k => self.builtin_refusal(*builtin, &[k])?,
6772                    },
6773                    BuiltinId::EnvValue => match ak {
6774                        Kind::Text => self.line(&format!(
6775                            "r[{dst}] = (int64_t)(intptr_t)lc_env_value({});",
6776                            as_cstr(a)
6777                        )),
6778                        k => self.builtin_refusal(*builtin, &[k])?,
6779                    },
6780                    // `now()`: time.lg's Unix time in milliseconds.
6781                    BuiltinId::TimeNow => self.line(&format!("r[{dst}] = logos_time_now();")),
6782                    other => return Err(format!("CallBuiltin {other:?} not yet lowered")),
6783                }
6784            }
6785            // A tuple is a heap buffer of one i64 cell per position (the register value IS the cell — the
6786            // uniform i64 file means no per-cell conversion, exactly like a struct). Destructure reads them back.
6787            Op::NewTuple { dst, start, count } => {
6788                let t = self.fresh();
6789                let n = (*count).max(1); // lc_gc_calloc(0,…) is UB-adjacent; a 0-tuple still gets one cell (unused)
6790                self.line(&format!(
6791                    "int64_t *_tup{t} = (int64_t*)lc_gc_calloc({n}, sizeof(int64_t));"
6792                ));
6793                for i in 0..*count {
6794                    self.line(&format!("_tup{t}[{i}] = r[{}];", *start + i));
6795                }
6796                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_tup{t};"));
6797            }
6798            Op::DestructureTuple { src, start, count } => {
6799                for i in 0..*count {
6800                    self.line(&format!(
6801                        "r[{}] = ((int64_t*)(intptr_t)r[{src}])[{i}];",
6802                        *start + i
6803                    ));
6804                }
6805            }
6806            // Bitwise / shifts are integer-domain (Bool 0/1 rides the same lowering) — never float, so the
6807            // shared `arith` int path is exactly right.
6808            Op::BitXor { dst, lhs, rhs }
6809                if self.exact_integers(&[*lhs, *rhs], kinds, Kind::Int) =>
6810            {
6811                self.emit_exact_bounded(*dst, *lhs, *rhs, kinds, Kind::Int, "lc_xbitxor")?;
6812            }
6813            Op::BitAnd { dst, lhs, rhs }
6814                if self.exact_integers(&[*lhs, *rhs], kinds, Kind::Int) =>
6815            {
6816                self.emit_exact_bounded(*dst, *lhs, *rhs, kinds, Kind::Int, "lc_xbitand")?;
6817            }
6818            Op::BitOr { dst, lhs, rhs } if self.exact_integers(&[*lhs, *rhs], kinds, Kind::Int) => {
6819                self.emit_exact_bounded(*dst, *lhs, *rhs, kinds, Kind::Int, "lc_xbitor")?;
6820            }
6821            Op::BitXor { dst, lhs, rhs } => self.arith(*dst, *lhs, *rhs, "^", kinds)?,
6822            Op::BitAnd { dst, lhs, rhs } => self.arith(*dst, *lhs, *rhs, "&", kinds)?,
6823            Op::BitOr { dst, lhs, rhs } => self.arith(*dst, *lhs, *rhs, "|", kinds)?,
6824            // Shifts mask the count to 6 bits (LOGOS `WordVal` semantics); a raw C shift by ≥64 is UB.
6825            // A word shifts by its count modulo its width (Rust's `wrapping_shl`/`wrapping_shr`), logically.
6826            Op::Shl { dst, lhs, rhs } | Op::Shr { dst, lhs, rhs }
6827                if matches!(kind_at(kinds, *lhs), Kind::Word(_)) =>
6828            {
6829                let Kind::Word(bits) = kind_at(kinds, *lhs) else {
6830                    unreachable!()
6831                };
6832                let mask = if bits == 64 {
6833                    "UINT64_MAX".to_string()
6834                } else {
6835                    format!("((1ULL << {bits}) - 1)")
6836                };
6837                let dir = if matches!(op, Op::Shl { .. }) {
6838                    "<<"
6839                } else {
6840                    ">>"
6841                };
6842                self.line(&format!("r[{dst}] = (int64_t)(((uint64_t)r[{lhs}] {dir} ((uint32_t)r[{rhs}] & {})) & {mask});", bits - 1));
6843            }
6844            Op::Shl { dst, lhs, rhs } | Op::Shr { dst, lhs, rhs }
6845                if kind_at(kinds, *lhs) == Kind::Rational || kind_at(kinds, *rhs) == Kind::Rational =>
6846            {
6847                let (l, r) = (as_ldec(*lhs, kind_at(kinds, *lhs))?, as_ldec(*rhs, kind_at(kinds, *rhs))?);
6848                let dir = if matches!(op, Op::Shl { .. }) { '<' } else { '>' };
6849                self.line(&format!("r[{dst}] = ldec_bitwise({l}, {r}, '{dir}');"));
6850            }
6851            Op::Shl { dst, lhs, rhs } if self.exact_integers(&[*lhs, *rhs], kinds, Kind::Int) => {
6852                self.emit_exact_bounded(*dst, *lhs, *rhs, kinds, Kind::Int, "lc_xshl")?;
6853            }
6854            Op::Shr { dst, lhs, rhs } if self.exact_integers(&[*lhs, *rhs], kinds, Kind::Int) => {
6855                self.emit_exact_bounded(*dst, *lhs, *rhs, kinds, Kind::Int, "lc_xshr")?;
6856            }
6857            Op::Shl { dst, lhs, rhs } => self.line(&format!(
6858                "r[{dst}] = (int64_t)((uint64_t)r[{lhs}] << (r[{rhs}] & 63));"
6859            )),
6860            Op::Shr { dst, lhs, rhs } => {
6861                self.line(&format!("r[{dst}] = r[{lhs}] >> (r[{rhs}] & 63);"))
6862            }
6863            // `is approximately` compares numbers; any other operand fails the run with the VM's refusal.
6864            Op::ApproxEq { dst, lhs, rhs } => {
6865                let (lk, rk) = (kind_at(kinds, *lhs), kind_at(kinds, *rhs));
6866                let numeric = |k: Kind| matches!(k, Kind::Int | Kind::Float);
6867                if numeric(lk) && numeric(rk) {
6868                    self.line(&format!(
6869                        "r[{dst}] = lc_approx_eq({}, {});",
6870                        as_double(*lhs, lk),
6871                        as_double(*rhs, rk)
6872                    ));
6873                } else {
6874                    let (Some(lv), Some(rv)) = (sample_value(lk), sample_value(rk)) else {
6875                        return Err(format!("`is approximately` over {lk:?} and {rk:?}"));
6876                    };
6877                    self.line(&format!("logos_fatalf({});", words::approx_refused(lv, rv)));
6878                }
6879            }
6880            // Strength-reduced constant division/modulo: bit-identical to `Div`/`Mod` by the constant, and
6881            // gcc -O2 re-derives the same magic-reciprocal code from the constant divisor (same speed). An integer
6882            // a number join holds as a rational divides as the exact number it is.
6883            Op::DivPow2 { .. } | Op::MagicDivU { .. } if crate::vm::tower::by_constant(op).is_some_and(|(_, lhs, _, _)| kind_at(kinds, lhs) == Kind::Rational) => {
6884                let (dst, lhs, divisor, f) = crate::vm::tower::by_constant(op).expect("a division by a constant");
6885                let f = if f == "div" { "ldec_quotient" } else { "ldec_remainder" };
6886                let l = as_ldec(lhs, Kind::Rational)?;
6887                self.box_ldec(dst, &format!("{f}({l}, ldec_from_i64((int64_t){divisor}LL))"));
6888            }
6889            Op::DivPow2 { dst, lhs, k } if kind_at(kinds, *lhs) == Kind::BigInt => {
6890                let q = format!("lc_xdiv(r[{lhs}], lc_xof((int64_t)1 << {k}))");
6891                self.line(&format!(
6892                    "r[{dst}] = {};",
6893                    if kind_at(after, *dst) == Kind::BigInt {
6894                        q
6895                    } else {
6896                        format!("lc_xnarrow({q})")
6897                    }
6898                ));
6899            }
6900            Op::MagicDivU {
6901                dst,
6902                lhs,
6903                mul_back,
6904                divisor,
6905                ..
6906            } if kind_at(kinds, *lhs) == Kind::BigInt => {
6907                let f = if *mul_back == 0 { "lc_xdiv" } else { "lc_xmod" };
6908                let v = format!("{f}(r[{lhs}], lc_xof({divisor}LL))");
6909                self.line(&format!(
6910                    "r[{dst}] = {};",
6911                    if kind_at(after, *dst) == Kind::BigInt {
6912                        v
6913                    } else {
6914                        format!("lc_xnarrow({v})")
6915                    }
6916                ));
6917            }
6918            Op::DivPow2 { dst, lhs, k } => {
6919                self.line(&format!("r[{dst}] = r[{lhs}] / (1LL << {k});"))
6920            }
6921            Op::MagicDivU {
6922                dst,
6923                lhs,
6924                mul_back,
6925                divisor,
6926                wrap,
6927                ..
6928            } => {
6929                if *mul_back == 0 {
6930                    self.line(&format!("r[{dst}] = r[{lhs}] / {divisor}LL;"));
6931                } else if *wrap {
6932                    crate::optimize::mark_fired(crate::optimization::Opt::ModWrap);
6933                    self.line(&format!(
6934                        "r[{dst}] = r[{lhs}] >= {divisor}LL ? r[{lhs}] - {divisor}LL : r[{lhs}];"
6935                    ));
6936                } else {
6937                    self.line(&format!("r[{dst}] = r[{lhs}] % {divisor}LL;"));
6938                }
6939            }
6940            Op::Concat { dst, lhs, rhs } => {
6941                // `combined with` / interpolation coerces a non-text operand to its `Show` rendering first.
6942                let l = self.text_of(*lhs, kind_at(kinds, *lhs))?;
6943                let r = self.text_of(*rhs, kind_at(kinds, *rhs))?;
6944                self.line(&format!(
6945                    "r[{dst}] = (int64_t)(intptr_t)str_concat({l}, {r});"
6946                ));
6947            }
6948            // Interpolation `{x}` / `Show x` debug — render a value to text by its `Show` form. An explicit
6949            // format spec (width/precision) is refused for now; the bare + debug-prefix forms are the common ones.
6950            Op::FormatValue {
6951                dst,
6952                src,
6953                spec,
6954                debug_prefix,
6955            } => {
6956                let sk = kind_at(kinds, *src);
6957                // With an explicit spec, format via the runtime exactly as the AST backend's `c_format_spec_for_hole`
6958                // (width/precision/alignment/`$`money/`^`center); with none, coerce to text as the VM does.
6959                let body = if *spec != u32::MAX {
6960                    let spec_str = match &self.prog.constants[*spec as usize] {
6961                        Constant::Text(s) => s.clone(),
6962                        _ => return Err("FormatValue spec is not Text".into()),
6963                    };
6964                    self.format_spec_body(*src, sk, &spec_str)?
6965                } else {
6966                    self.text_of(*src, sk)?
6967                };
6968                if *debug_prefix != u32::MAX {
6969                    let prefix = match &self.prog.constants[*debug_prefix as usize] {
6970                        Constant::Text(p) => c_escape(p),
6971                        _ => return Err("FormatValue debug prefix is not Text".into()),
6972                    };
6973                    self.line(&format!(
6974                        "r[{dst}] = (int64_t)(intptr_t)str_concat(\"{prefix}=\", {body});"
6975                    ));
6976                } else {
6977                    self.line(&format!("r[{dst}] = (int64_t)(intptr_t)({body});"));
6978                }
6979            }
6980            Op::Lt { dst, lhs, rhs } => self.cmp(*dst, *lhs, *rhs, "<", kinds)?,
6981            Op::Gt { dst, lhs, rhs } => self.cmp(*dst, *lhs, *rhs, ">", kinds)?,
6982            Op::LtEq { dst, lhs, rhs } => self.cmp(*dst, *lhs, *rhs, "<=", kinds)?,
6983            Op::GtEq { dst, lhs, rhs } => self.cmp(*dst, *lhs, *rhs, ">=", kinds)?,
6984            // `is equal to` / `is not equal to` — the VM's structural equality ([`Emitter::eq_expr`]).
6985            Op::Eq { dst, lhs, rhs } | Op::NotEq { dst, lhs, rhs } => {
6986                let (lk, rk) = (kind_at(kinds, *lhs), kind_at(kinds, *rhs));
6987                let scalar = |k: Kind| {
6988                    matches!(
6989                        k,
6990                        Kind::Int | Kind::Bool | Kind::Float | Kind::BigInt | Kind::Rational
6991                    )
6992                };
6993                let not = if matches!(op, Op::NotEq { .. }) {
6994                    "!"
6995                } else {
6996                    ""
6997                };
6998                if scalar(lk) && scalar(rk) && !(lk == Kind::Bool) == !(rk == Kind::Bool) {
6999                    self.cmp(
7000                        *dst,
7001                        *lhs,
7002                        *rhs,
7003                        if not.is_empty() { "==" } else { "!=" },
7004                        kinds,
7005                    )?;
7006                } else {
7007                    let same = self.eq_expr(&format!("r[{lhs}]"), lk, &format!("r[{rhs}]"), rk)?;
7008                    self.line(&format!("r[{dst}] = {not}({same});"));
7009                }
7010            }
7011            // `not x`: on a Bool it's logical negation; on a Text it's the emptiness test (an empty string is
7012            // falsy in LOGOS, so `not ""` is true).
7013            Op::Not { dst, src } => {
7014                if kind_at(kinds, *src) == Kind::Text {
7015                    self.line(&format!("r[{dst}] = (str_len({}) == 0);", as_cstr(*src)));
7016                } else {
7017                    self.line(&format!("r[{dst}] = !r[{src}];"));
7018                }
7019            }
7020            Op::Jump { target } => self.line(&format!("goto {}{target};", self.label)),
7021            Op::JumpIfFalse { cond, target } => {
7022                self.line(&format!("if (!r[{cond}]) goto {}{target};", self.label))
7023            }
7024            Op::JumpIfTrue { cond, target } => {
7025                self.line(&format!("if (r[{cond}]) goto {}{target};", self.label))
7026            }
7027            // A collection argument passes as its handle: the callee's registers start with clear owned flags, so
7028            // its first change to a parameter copies it (value semantics), while a `mutable` parameter — the
7029            // caller's collection — changes in place. A `mutable` parameter passes its argument register itself,
7030            // which holds the parameter's final value when the call returns, as the VM leaves it.
7031            Op::Call {
7032                dst,
7033                func,
7034                args_start,
7035                arg_count,
7036            } => {
7037                let params = self
7038                    .param_kinds
7039                    .get(*func as usize)
7040                    .cloned()
7041                    .unwrap_or_default();
7042                let inout = self.inout_regs(*func as usize);
7043                // On machine words, a function with a word entry takes its exact parameters as the machine words
7044                // they hold here, and an exact result it escapes to resumes the region held exact at this call: an
7045                // exact result arrives on machine words as a word, so the call is one of the region's arrivals.
7046                let exact_result = self.fn_ret.get(*func as usize) == Some(&Kind::BigInt);
7047                let twin = self.on_words.get()
7048                    && self.word_twins.contains(&(*func as usize))
7049                    && (0..*arg_count).all(|i| {
7050                        params.get(i as usize) != Some(&Kind::BigInt)
7051                            || matches!(kind_at(kinds, args_start + i), Kind::Int | Kind::Bool)
7052                    });
7053                let mut args = Vec::with_capacity(*arg_count as usize);
7054                for i in 0..*arg_count {
7055                    let reg = args_start + i;
7056                    // An exact parameter handed a machine integer receives it as an exact integer.
7057                    let widen = !twin
7058                        && matches!(
7059                            (params.get(i as usize), kind_at(kinds, reg)),
7060                            (Some(Kind::BigInt), Kind::Int | Kind::Bool)
7061                        );
7062                    if inout.contains(&i) {
7063                        if widen {
7064                            self.line(&format!("r[{reg}] = lc_xof(r[{reg}]);"));
7065                        }
7066                        args.push(format!("&r[{reg}]"));
7067                    } else if widen {
7068                        args.push(format!("lc_xof(r[{reg}])"));
7069                    } else if params.get(i as usize) == Some(&Kind::Tower)
7070                        && kind_at(kinds, reg) != Kind::Tower
7071                    {
7072                        // An exact parameter takes the runtime's value of what the caller holds.
7073                        let cell = self.tower_of(&format!("r[{reg}]"), kind_at(kinds, reg))?;
7074                        args.push(format!("(int64_t)(intptr_t)({cell})"));
7075                    } else if let Some(&to) = params
7076                        .get(i as usize)
7077                        .filter(|to| self.widens_to(kind_at(kinds, reg), **to))
7078                    {
7079                        // A Rational parameter handed an integer takes the rational it equals.
7080                        args.push(self.convert_number(&format!("r[{reg}]"), kind_at(kinds, reg), to)?);
7081                    } else if params.get(i as usize) == Some(&Kind::Float)
7082                        && matches!(kind_at(kinds, reg), Kind::Int | Kind::BigInt)
7083                    {
7084                        // A Real parameter handed an integer takes it widened to the nearest double, as the VM widens it.
7085                        args.push(format!("as_bits({})", as_double(reg, kind_at(kinds, reg))));
7086                    } else {
7087                        args.push(format!("r[{reg}]"));
7088                    }
7089                }
7090                let ret_own = if self.owned.contains(dst)
7091                    && crate::optimize::active_config().is_on(crate::optimization::Opt::Borrow)
7092                {
7093                    crate::optimize::mark_fired(crate::optimization::Opt::Borrow);
7094                    format!("&own[{dst}]")
7095                } else {
7096                    "NULL".to_string()
7097                };
7098                args.push(ret_own);
7099                if twin && exact_result {
7100                    let n = self.fresh();
7101                    self.line(&format!(
7102                        "{{ lc_wret _w{n} = fn_{func}_w({}); r[{dst}] = _w{n}.v; if (_w{n}.escaped) goto A{pc}; }}",
7103                        args.join(", ")
7104                    ));
7105                    self.twin_arrival = true;
7106                } else if twin {
7107                    self.line(&format!("r[{dst}] = fn_{func}_w({}).v;", args.join(", ")));
7108                } else {
7109                    self.line(&format!("r[{dst}] = fn_{func}({});", args.join(", ")));
7110                }
7111            }
7112            // Build a closure object `[body_index, captured values…, present flags…]`: a local capture snapshots
7113            // the register at the build site, a global capture the module global (present=1, assumed defined).
7114            Op::MakeClosure {
7115                dst,
7116                func,
7117                locals_start,
7118            } => {
7119                let caps = self.prog.functions[*func as usize].captures.clone();
7120                let ncap = caps.len();
7121                let n = self.fresh();
7122                self.line(&format!(
7123                    "int64_t *_clo{n} = (int64_t*)lc_gc_calloc({}, sizeof(int64_t));",
7124                    1 + 2 * ncap
7125                ));
7126                self.line(&format!("_clo{n}[0] = {func};"));
7127                let mut local_k: u16 = 0;
7128                for (i, (_sym, gidx)) in caps.iter().enumerate() {
7129                    match gidx {
7130                        Some(g) => {
7131                            self.line(&format!("_clo{n}[{}] = g[{}];", 1 + i, g));
7132                            self.line(&format!("_clo{n}[{}] = 1;", 1 + ncap + i));
7133                        }
7134                        None => {
7135                            self.line(&format!(
7136                                "_clo{n}[{}] = r[{}];",
7137                                1 + i,
7138                                locals_start + local_k
7139                            ));
7140                            self.line(&format!("_clo{n}[{}] = 1;", 1 + ncap + i));
7141                            local_k += 1;
7142                        }
7143                    }
7144                }
7145                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_clo{n};"));
7146            }
7147            // Call a closure value: dispatch on its runtime body index, passing the args then the closure pointer
7148            // as the hidden trailing arg (the layout each closure-body `fn_*` expects).
7149            Op::CallValue {
7150                dst,
7151                callee,
7152                args_start,
7153                arg_count,
7154                ..
7155            } => {
7156                let bodies: Vec<usize> = self.closure_bodies.iter().copied().collect();
7157                if bodies.is_empty() {
7158                    return Err("CallValue but the program builds no closures".into());
7159                }
7160                let arglist: String = (0..*arg_count)
7161                    .map(|k| format!("r[{}], ", args_start + k))
7162                    .collect();
7163                let ret_own = if self.owned.contains(dst)
7164                    && crate::optimize::active_config().is_on(crate::optimization::Opt::Borrow)
7165                {
7166                    crate::optimize::mark_fired(crate::optimization::Opt::Borrow);
7167                    format!("&own[{dst}]")
7168                } else {
7169                    "NULL".to_string()
7170                };
7171                self.line(&format!(
7172                    "switch ((int)((int64_t*)(intptr_t)r[{callee}])[0]) {{"
7173                ));
7174                for b in bodies {
7175                    self.line(&format!(
7176                        "case {b}: r[{dst}] = fn_{b}({arglist}r[{callee}], {ret_own}); break;"
7177                    ));
7178                }
7179                self.line("default: fprintf(stderr, \"CallValue: unknown closure body\\n\"); fflush(stdout); exit(1);");
7180                self.line("}");
7181            }
7182            // ── Collections ──────────────────────────────────────────────────────────────────────────────
7183            Op::NewRange { dst, start, end } => {
7184                let t = self.fresh();
7185                self.line(&format!(
7186                    "Seq_i64 *_s{t} = (Seq_i64*)lc_gc_alloc(sizeof(Seq_i64)); *_s{t} = seq_i64_new();"
7187                ));
7188                let (from, to) = (
7189                    self.int_operand(kinds, *start, words::IntegerPlace::Range),
7190                    self.int_operand(kinds, *end, words::IntegerPlace::Range),
7191                );
7192                self.line(&format!("for (int64_t _i{t} = {from}, _e{t} = {to}; _i{t} <= _e{t}; _i{t}++) seq_i64_push(_s{t}, _i{t});"));
7193                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_s{t};"));
7194            }
7195            Op::NewList { dst, start, count } => {
7196                // An empty `[]` carries no element kind here — take the fixpoint-refined kind if a later push
7197                // established one, else box an empty `Seq_i64` (an empty list renders `[]` for any element type).
7198                let seq_kind = if *count == 0 {
7199                    let k = kind_at(after, *dst);
7200                    if is_concrete_seq(k) {
7201                        k
7202                    } else {
7203                        Kind::SeqInt
7204                    }
7205                } else {
7206                    self.list_kind(&(0..*count).map(|i| kind_at(kinds, start + i)).collect::<Vec<_>>())?
7207                };
7208                let ty = seq_kind
7209                    .seq_c_type()
7210                    .ok_or_else(|| format!("NewList of a non-scalar element kind {seq_kind:?}"))?;
7211                let infix = seq_kind.seq_infix().unwrap();
7212                let elem = self.elem_kind(seq_kind);
7213                let t = self.fresh();
7214                self.line(&format!(
7215                    "{ty} *_s{t} = ({ty}*)lc_gc_alloc(sizeof({ty})); *_s{t} = seq_{infix}_new();"
7216                ));
7217                for k in 0..*count {
7218                    let item = self.elem_value(start + k, kind_at(kinds, start + k), elem)?;
7219                    self.line(&format!("seq_{infix}_push(_s{t}, {item});"));
7220                }
7221                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_s{t};"));
7222            }
7223            // An empty sequence: allocate the fixpoint-refined typed `Seq_*`; a never-populated one that stays
7224            // `SeqUnknown` allocates a `Seq_i64` (an empty list renders `[]` regardless of element type).
7225            Op::NewEmptyListFixed { dst, capacity } => {
7226                let sk = match kind_at(after, *dst) {
7227                    k if is_concrete_seq(k) => k,
7228                    _ => Kind::SeqInt,
7229                };
7230                let ty = sk
7231                    .seq_c_type()
7232                    .ok_or_else(|| format!("fixed list of non-sequence kind {sk:?}"))?;
7233                let elem = match sk {
7234                    Kind::SeqFloat => "double",
7235                    Kind::SeqText => "char *",
7236                    Kind::SeqBool => "bool",
7237                    _ => "int64_t",
7238                };
7239                let t = self.fresh();
7240                self.line(&format!(
7241                    "{elem} _fxd{t}[{capacity}] = {{0}}; /* shared fixed Seq */"
7242                ));
7243                self.line(&format!("{ty} _fx{t} = ({ty}){{_fxd{t}, 0, {capacity}}};"));
7244                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)&_fx{t};"));
7245                if self.owned.contains(dst) {
7246                    self.line(&format!("own[{dst}] = 1;"));
7247                }
7248                crate::optimize::mark_fired(crate::optimization::Opt::Scalarize);
7249            }
7250            Op::NewEmptyListWithCapacity { dst, capacity } => {
7251                let sk = {
7252                    let k = kind_at(after, *dst);
7253                    if is_concrete_seq(k) {
7254                        k
7255                    } else {
7256                        Kind::SeqInt
7257                    }
7258                };
7259                let ty = sk.seq_c_type().unwrap();
7260                let infix = sk.seq_infix().unwrap();
7261                let t = self.fresh();
7262                let reuses = self.reuses_list_buffer(*dst, sk);
7263                if reuses {
7264                    self.line(&format!("if (own[{dst}] && r[{dst}] != 0) {{"));
7265                    self.line(&format!("{ty} *_s{t} = ({ty}*)(intptr_t)r[{dst}];"));
7266                    self.line(&format!("_s{t}->len = 0;"));
7267                    self.line(&format!(
7268                        "if (r[{capacity}] > 0 && (uint64_t)r[{capacity}] > _s{t}->cap) {{"
7269                    ));
7270                    self.line(&format!("_s{t}->data = lc_gc_realloc(_s{t}->data, (size_t)r[{capacity}] * sizeof(*_s{t}->data));"));
7271                    self.line(&format!("_s{t}->cap = (size_t)r[{capacity}];"));
7272                    self.line("}");
7273                    self.line("} else {");
7274                }
7275                self.line(&format!("{ty} *_s{t} = ({ty}*)lc_gc_alloc(sizeof({ty})); *_s{t} = seq_{infix}_with_capacity(r[{capacity}]);"));
7276                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_s{t};"));
7277                if reuses {
7278                    self.line("}");
7279                }
7280            }
7281            Op::NewEmptyList { dst } | Op::NewEmptyListI32 { dst } => {
7282                let sk = {
7283                    let k = kind_at(after, *dst);
7284                    if is_concrete_seq(k) {
7285                        k
7286                    } else {
7287                        Kind::SeqInt
7288                    }
7289                };
7290                let ty = sk.seq_c_type().unwrap();
7291                let infix = sk.seq_infix().unwrap();
7292                let t = self.fresh();
7293                let reuses = self.reuses_list_buffer(*dst, sk);
7294                if reuses {
7295                    self.line(&format!("if (own[{dst}] && r[{dst}] != 0) {{"));
7296                    self.line("/* reuse sole-owned list buffer */");
7297                    self.line(&format!("(({ty}*)(intptr_t)r[{dst}])->len = 0;"));
7298                    self.line("} else {");
7299                }
7300                self.line(&format!(
7301                    "{ty} *_s{t} = ({ty}*)lc_gc_alloc(sizeof({ty})); *_s{t} = seq_{infix}_new();"
7302                ));
7303                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_s{t};"));
7304                if reuses {
7305                    self.line("}");
7306                }
7307            }
7308            Op::ListPop { list, dst } => {
7309                // Pop → a boxed `[present, value]` optional: pop only when non-empty (else `nothing`), matching
7310                // the VM's `list_pop` (value when present, `RuntimeValue::Nothing` when the seq is empty).
7311                let lk = seq_kind_of(kind_at(kinds, *list));
7312                let infix = lk
7313                    .seq_infix()
7314                    .ok_or_else(|| format!("Pop from a non-sequence kind {lk:?}"))?;
7315                self.own_target(*list, lk)?;
7316                let ptr = Self::seq_ptr(*list, lk)?;
7317                let read = elem_to_reg(&format!("seq_{infix}_pop({ptr})"), self.elem_kind(lk));
7318                let n = self.fresh();
7319                self.line(&format!(
7320                    "int64_t *_opt{n} = (int64_t*)lc_gc_calloc(2, sizeof(int64_t));"
7321                ));
7322                self.line(&format!("if (({ptr})->len > 0) {{ _opt{n}[0] = 1; _opt{n}[1] = {read}; }} else {{ _opt{n}[0] = 0; }}"));
7323                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_opt{n};"));
7324            }
7325            // ── Sets (heap-boxed insertion-ordered dedup'd Set_i64/Set_str) ──────────────────────────────────
7326            Op::NewEmptySet { dst } => {
7327                // The fixpoint-refined element kind (an unpopulated set stays `SetUnknown` → box a `Set_i64`,
7328                // which renders `{}` like any empty set).
7329                let sk = {
7330                    let k = kind_at(after, *dst);
7331                    if is_concrete_set(k) {
7332                        k
7333                    } else {
7334                        Kind::SetInt
7335                    }
7336                };
7337                let ty = sk.set_c_type().unwrap();
7338                let infix = sk.seq_infix().unwrap();
7339                let t = self.fresh();
7340                self.line(&format!(
7341                    "{ty} *_s{t} = ({ty}*)lc_gc_alloc(sizeof({ty})); *_s{t} = set_{infix}_new();"
7342                ));
7343                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_s{t};"));
7344            }
7345            Op::NewEmptyMap { dst } => {
7346                // Box the (fixpoint-refined) map struct; an unrefined empty map boxes a `Map_i64_i64` (renders `{}`).
7347                let (key, val) = match kind_at(after, *dst) {
7348                    Kind::Map(idx) => self.map_pair(idx),
7349                    _ => (Kind::Int, Kind::Int),
7350                };
7351                let (ty, pre) = (map_type_name(key, val).unwrap(), map_fn(key, val).unwrap());
7352                let t = self.fresh();
7353                self.line(&format!(
7354                    "{ty} *_m{t} = ({ty}*)lc_gc_alloc(sizeof({ty})); *_m{t} = {pre}_new();"
7355                ));
7356                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_m{t};"));
7357            }
7358            Op::NewEmptyMapWithCapacity { dst, capacity } => {
7359                let (key, val) = match kind_at(after, *dst) {
7360                    Kind::Map(idx) => self.map_pair(idx),
7361                    _ => (Kind::Int, Kind::Int),
7362                };
7363                let (ty, pre) = (map_type_name(key, val).unwrap(), map_fn(key, val).unwrap());
7364                let t = self.fresh();
7365                self.line(&format!("{ty} *_m{t} = ({ty}*)lc_gc_alloc(sizeof({ty})); *_m{t} = {pre}_with_capacity(r[{capacity}]);"));
7366                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_m{t};"));
7367            }
7368            Op::SetAdd { set, value } => {
7369                let sk = kind_at(kinds, *value).set_of();
7370                let infix = sk
7371                    .seq_infix()
7372                    .ok_or_else(|| format!("SetAdd of a non-scalar element kind {sk:?}"))?;
7373                self.own_target(*set, sk)?;
7374                let ptr = Self::set_ptr(*set, sk)?;
7375                self.line(&format!(
7376                    "set_{infix}_add({ptr}, {});",
7377                    as_elem_push(*value, sk.elem())
7378                ));
7379            }
7380            Op::RemoveFrom { collection, value } => {
7381                let ck = kind_at(kinds, *collection);
7382                let sk = resolve_set(ck)
7383                    .ok_or_else(|| format!("RemoveFrom on a non-set kind {ck:?}"))?;
7384                let infix = sk.seq_infix().unwrap();
7385                self.own_target(*collection, sk)?;
7386                let ptr = Self::set_ptr(*collection, sk)?;
7387                self.line(&format!(
7388                    "set_{infix}_remove({ptr}, {});",
7389                    as_elem_push(*value, sk.elem())
7390                ));
7391            }
7392            Op::UnionOp { dst, lhs, rhs } => {
7393                let sk = resolve_set(kind_at(kinds, *lhs))
7394                    .ok_or_else(|| format!("Union on a non-set kind {:?}", kind_at(kinds, *lhs)))?;
7395                let (ty, infix) = (sk.set_c_type().unwrap(), sk.seq_infix().unwrap());
7396                let (lp, rp) = (Self::set_ptr(*lhs, sk)?, Self::set_ptr(*rhs, sk)?);
7397                let t = self.fresh();
7398                self.line(&format!("{ty} *_su{t} = ({ty}*)lc_gc_alloc(sizeof({ty})); *_su{t} = set_{infix}_union({lp}, {rp});"));
7399                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_su{t};"));
7400            }
7401            Op::IntersectOp { dst, lhs, rhs } => {
7402                let sk = resolve_set(kind_at(kinds, *lhs)).ok_or_else(|| {
7403                    format!("Intersect on a non-set kind {:?}", kind_at(kinds, *lhs))
7404                })?;
7405                let (ty, infix) = (sk.set_c_type().unwrap(), sk.seq_infix().unwrap());
7406                let (lp, rp) = (Self::set_ptr(*lhs, sk)?, Self::set_ptr(*rhs, sk)?);
7407                let t = self.fresh();
7408                self.line(&format!("{ty} *_si{t} = ({ty}*)lc_gc_alloc(sizeof({ty})); *_si{t} = set_{infix}_intersect({lp}, {rp});"));
7409                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_si{t};"));
7410            }
7411            // ── Sequence slice / element-set / concat ────────────────────────────────────────────────────────
7412            Op::SliceOp {
7413                dst,
7414                collection,
7415                start,
7416                end,
7417            } => {
7418                let ck = seq_kind_of(kind_at(kinds, *collection));
7419                let ty = ck
7420                    .seq_c_type()
7421                    .ok_or_else(|| format!("Slice of a non-sequence kind {ck:?}"))?;
7422                let infix = ck.seq_infix().unwrap();
7423                let ptr = Self::seq_ptr(*collection, ck)?;
7424                let t = self.fresh();
7425                // `items X through Y` is 1-based inclusive; an out-of-range slice (`Y > len` or `X-1 >= Y`) is
7426                // EMPTY (the VM's `slice.get(start..end)` semantics), not clamped-to-available.
7427                let (from, to) = (
7428                    self.int_operand(kinds, *start, words::IntegerPlace::Slice),
7429                    self.int_operand(kinds, *end, words::IntegerPlace::Slice),
7430                );
7431                self.line(&format!("{ty} *_sl{t} = ({ty}*)lc_gc_alloc(sizeof({ty}));"));
7432                self.line(&format!("{{ int64_t _sf{t} = {from}, _st{t} = {to}; if (_sf{t} - 1 < _st{t} && _st{t} <= (int64_t){ptr}->len) *_sl{t} = seq_{infix}_slice({ptr}, _sf{t}, _st{t}); else *_sl{t} = seq_{infix}_new(); }}"));
7433                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_sl{t};"));
7434            }
7435            Op::SetIndex {
7436                collection,
7437                index,
7438                value,
7439            }
7440            | Op::SetIndexUnchecked {
7441                collection,
7442                index,
7443                value,
7444            } => {
7445                let unchecked = matches!(op, Op::SetIndexUnchecked { .. });
7446                let ck = kind_at(kinds, *collection);
7447                if matches!(ck, Kind::Struct(_) | Kind::StructSynth(_)) {
7448                    // `obj["field"] = v` — a field store by name (the take-out/put-back idiom around a change
7449                    // to a collection that lives in a field).
7450                    let name = self
7451                        .const_text_at(self.region_span.0, pc, *index)
7452                        .ok_or("a record indexed by something other than a field name")?;
7453                    let cell = self
7454                        .field_cell_of_name(ck, &name)
7455                        .ok_or_else(|| format!("a store to the unknown field `{name}`"))?;
7456                    self.store_field(*collection, ck, cell, *value, kind_at(kinds, *value), pc)?;
7457                } else if matches!(ck, Kind::Map(_) | Kind::MapUnknown) {
7458                    // `item k of m to v` — a map insert/update. Take the map type from THIS insert's actual
7459                    // key/value kinds (the value rides its i64/str cell — ANY kind), and REFUSE an unhashable
7460                    // key — never silently coerce it.
7461                    let (input_key, given) = (kind_at(kinds, *index), kind_at(kinds, *value));
7462                    let vk = match ck {
7463                        Kind::Map(mi) if self.map_pair(mi).1 == Kind::Tower => Kind::Tower,
7464                        _ => given,
7465                    };
7466                    let kk = if input_key == Kind::BigInt {
7467                        Kind::Int
7468                    } else {
7469                        input_key
7470                    };
7471                    if map_key_storage(kk).is_none() || map_val_storage(vk).is_none() {
7472                        return Err(format!(
7473                            "map insert with an unsupported key/value kind ({kk:?} -> {vk:?})"
7474                        ));
7475                    }
7476                    self.own_target(*collection, self.map_of(kk, vk))?;
7477                    let (pre, ptr) = (
7478                        map_fn(kk, vk).unwrap(),
7479                        map_ptr_kv(*collection, kk, vk).unwrap(),
7480                    );
7481                    // The key and value can name the same register. Narrow the
7482                    // key expression only; retain the exact value for its cell.
7483                    let key = if input_key == Kind::BigInt {
7484                        format!("lc_xnarrow(r[{index}])")
7485                    } else {
7486                        map_key_arg(*index, kk)
7487                    };
7488                    let val = if vk == Kind::Tower && given != Kind::Tower {
7489                        let cell = self.tower_of(&format!("r[{value}]"), given)?;
7490                        format!("(int64_t)(intptr_t)({cell})")
7491                    } else {
7492                        map_val_store(*value, vk)
7493                    };
7494                    self.line(&format!("{pre}_set({ptr}, {key}, {val});"));
7495                } else {
7496                    let sk = seq_kind_of(ck);
7497                    sk.seq_infix()
7498                        .ok_or_else(|| format!("SetIndex on a non-sequence kind {ck:?}"))?;
7499                    self.own_target(*collection, sk)?;
7500                    let ptr = Self::seq_ptr(*collection, sk)?;
7501                    let index_word = self.int_operand(kinds, *index, words::IntegerPlace::ListStore);
7502                    let at = if unchecked {
7503                        crate::optimize::mark_fired(crate::optimization::Opt::Unchecked);
7504                        format!("(size_t)({index_word} - 1LL)")
7505                    } else {
7506                        format!("logos_idx0({index_word}, {ptr}->len)")
7507                    };
7508                    let item = self.elem_value(*value, kind_at(kinds, *value), self.elem_kind(sk))?;
7509                    self.line(&format!("{ptr}->data[{at}] = {item};"));
7510                }
7511            }
7512            Op::SeqConcat { dst, lhs, rhs } => {
7513                let (lk, rk) = (kind_at(kinds, *lhs), kind_at(kinds, *rhs));
7514                // Two sequences meet at the element kind either declares; one made empty takes the other's.
7515                let lk = seq_kind_of(if lk == Kind::SeqUnknown { rk } else { lk });
7516                let ty = lk
7517                    .seq_c_type()
7518                    .ok_or_else(|| format!("SeqConcat of a non-sequence kind {lk:?}"))?;
7519                let infix = lk.seq_infix().unwrap();
7520                let (lp, rp) = (Self::seq_ptr(*lhs, lk)?, Self::seq_ptr(*rhs, lk)?);
7521                let t = self.fresh();
7522                self.line(&format!(
7523                    "{ty} *_sc{t} = ({ty}*)lc_gc_alloc(sizeof({ty})); *_sc{t} = seq_{infix}_new();"
7524                ));
7525                self.line(&format!("for (size_t _a{t}=0; _a{t}<{lp}->len; _a{t}++) seq_{infix}_push(_sc{t}, {lp}->data[_a{t}]);"));
7526                self.line(&format!("for (size_t _b{t}=0; _b{t}<{rp}->len; _b{t}++) seq_{infix}_push(_sc{t}, {rp}->data[_b{t}]);"));
7527                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_sc{t};"));
7528            }
7529            // A copy-on-write ownership barrier: a mutable collection aliased through the register file must be
7530            // cloned so an in-place mutation cannot leak back to an alias (matching the VM's `ensure_reg_owned`).
7531            // Immutable values (scalars, Text, Enums) need no copy.
7532            Op::EnsureOwned { reg } => self.own_target(*reg, kind_at(kinds, *reg))?,
7533            // ── Structs (a heap buffer of one i64 cell per field; the uniform register value IS the cell) ────
7534            Op::NewStruct { dst, type_name } => {
7535                let sk = self.struct_kind_of_type_name(*type_name);
7536                // One cell per field, then the field-ownership word ([`Emitter::field_owner_word`]).
7537                let n = self
7538                    .struct_cell_count(sk)
7539                    .ok_or("NewStruct of an unknown struct type")?
7540                    + 1;
7541                let t = self.fresh();
7542                // An exact field starts at nothing, as the VM's does: a cell of the runtime's nothing, not a null one.
7543                let exact_cells: String = match sk {
7544                    Kind::Struct(si) => self.prog.struct_types[si as usize]
7545                        .fields
7546                        .iter()
7547                        .enumerate()
7548                        .filter(|(_, (_, ty))| self.boundary_kind(&Some(ty.clone())) == Kind::Tower)
7549                        .map(|(i, _)| format!(" _st{t}[{i}] = (int64_t)(intptr_t)ltower_nothing();"))
7550                        .collect(),
7551                    _ => String::new(),
7552                };
7553                self.line(&format!(
7554                    "{{ int64_t *_st{t} = (int64_t*)lc_gc_calloc({n}, sizeof(int64_t));{exact_cells} r[{dst}] = (int64_t)(intptr_t)_st{t}; }}"
7555                ));
7556            }
7557            Op::StructInsert { obj, field, value } => {
7558                let ok = kind_at(kinds, *obj);
7559                if !matches!(ok, Kind::Struct(_) | Kind::StructSynth(_)) {
7560                    return Err(format!("StructInsert on a non-struct kind {ok:?}"));
7561                }
7562                let (cell, _) = self
7563                    .resolve_field_any(ok, *field)
7564                    .ok_or("StructInsert of an unknown field")?;
7565                self.store_field(*obj, ok, cell, *value, kind_at(kinds, *value), pc)?;
7566            }
7567            Op::GetField { dst, obj, field } => {
7568                let ok = kind_at(kinds, *obj);
7569                if !matches!(ok, Kind::Struct(_) | Kind::StructSynth(_)) {
7570                    return Err(format!("GetField on a non-struct kind {ok:?}"));
7571                }
7572                let (cell, _) = self
7573                    .resolve_field_any(ok, *field)
7574                    .ok_or("GetField of an unknown field")?;
7575                // A counter-CRDT field holds a `logos_crdt*` (cast to the `int64_t` cell); reading it yields the
7576                // SUM (an Int), exactly as the VM renders the counter's total. A plain field reads the cell verbatim.
7577                let is_counter = self
7578                    .struct_name_of_kind(ok)
7579                    .map(|n| self.counter_fields.contains(&(n, cell)))
7580                    .unwrap_or(false);
7581                if is_counter {
7582                    self.line(&format!(
7583                        "r[{dst}] = logos_crdt_sum((logos_crdt*)(intptr_t)((int64_t*)(intptr_t)r[{obj}])[{cell}]);"
7584                    ));
7585                } else {
7586                    self.line(&format!(
7587                        "r[{dst}] = ((int64_t*)(intptr_t)r[{obj}])[{cell}];"
7588                    ));
7589                    self.read_field_collection(*dst, *obj, ok, cell, pc);
7590                }
7591            }
7592            // ── Enums (a heap buffer `[variant-tag, payload…]`; the uniform register value IS each cell) ──────
7593            // A declared enum tags a variant by its declaration index; an undeclared one (`Option`, `Result`, …)
7594            // by its constructor's place in the program-wide order (`ctor_tags`).
7595            Op::NewInductive {
7596                dst,
7597                type_name,
7598                ctor,
7599                args_start,
7600                count,
7601            } => {
7602                let tag = match self.enum_index_of_type_name(*type_name) {
7603                    Some(ei) => self
7604                        .variant_index(ei, *ctor)
7605                        .ok_or("NewInductive of an unknown variant")?,
7606                    None => {
7607                        let (ty, c) = (self.const_text(*type_name), self.const_text(*ctor));
7608                        let (Some(ty), Some(c)) = (ty, c) else {
7609                            return Err("NewInductive with a non-Text name".into());
7610                        };
7611                        self.ctor_tag(ty, c)
7612                            .ok_or_else(|| format!("the constructor {ty}::{c} has no tag"))?
7613                    }
7614                };
7615                let n = *count as usize + 1;
7616                let t = self.fresh();
7617                if crate::optimize::active_config().is_on(crate::optimization::Opt::Scalarize) {
7618                    self.line(&format!("int64_t *_en{t} = lc_enum_alloc({n});"));
7619                    crate::optimize::mark_fired(crate::optimization::Opt::Scalarize);
7620                } else {
7621                    self.line(&format!(
7622                        "int64_t *_en{t} = (int64_t*)lc_gc_calloc({n}, sizeof(int64_t));"
7623                    ));
7624                }
7625                self.line(&format!("_en{t}[0] = {tag};"));
7626                for k in 0..*count {
7627                    self.line(&format!("_en{t}[{}] = r[{}];", k + 1, args_start + k));
7628                }
7629                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_en{t};"));
7630            }
7631            // `When V …` — does the value answer to constructor `V`? As the VM decides it: an enum by its
7632            // constructor, a record by its type's name, `nothing` only a `None` arm, anything else no arm.
7633            Op::TestArm {
7634                dst,
7635                target,
7636                variant,
7637            } => {
7638                let name = self
7639                    .const_text(*variant)
7640                    .ok_or("TestArm of a non-Text variant")?
7641                    .to_string();
7642                let tk = kind_at(kinds, *target);
7643                let test = match tk {
7644                    Kind::Enum(ei) => match self.variant_index(ei, *variant) {
7645                        Some(vi) => format!("(((int64_t*)(intptr_t)r[{target}])[0] == {vi})"),
7646                        None => "0".to_string(),
7647                    },
7648                    Kind::Inductive(ii) => match self.ctor_tag(&self.inductive(ii).0, &name) {
7649                        Some(tag) => format!("(((int64_t*)(intptr_t)r[{target}])[0] == {tag})"),
7650                        None => "0".to_string(),
7651                    },
7652                    Kind::Struct(_) | Kind::StructSynth(_) => {
7653                        ((self.struct_name_of_kind(tk).as_deref() == Some(name.as_str())) as i32)
7654                            .to_string()
7655                    }
7656                    Kind::Optional(oi) => match (name.as_str(), self.opt_inner(oi)) {
7657                        ("None", _) => format!("(!((int64_t*)(intptr_t)r[{target}])[0])"),
7658                        (_, inner @ (Kind::Struct(_) | Kind::StructSynth(_)))
7659                            if self.struct_name_of_kind(inner).as_deref()
7660                                == Some(name.as_str()) =>
7661                        {
7662                            format!("(((int64_t*)(intptr_t)r[{target}])[0] != 0)")
7663                        }
7664                        _ => "0".to_string(),
7665                    },
7666                    Kind::Nothing => ((name == "None") as i32).to_string(),
7667                    Kind::Unknown => return Err(format!("TestArm on a non-enum kind {tk:?}")),
7668                    _ => "0".to_string(),
7669                };
7670                self.line(&format!("r[{dst}] = {test};"));
7671            }
7672            // The bound payload of the arm just matched: an enum's payload cell, a record's field (or, for a
7673            // `Some` arm over a record, the record itself), a popped value's value; `nothing` binds nothing.
7674            Op::BindArm {
7675                dst,
7676                target,
7677                field,
7678                index,
7679            } => {
7680                let tk = kind_at(kinds, *target);
7681                match tk {
7682                    Kind::Enum(_) | Kind::Inductive(_) => {
7683                        self.line(&format!(
7684                            "r[{dst}] = ((int64_t*)(intptr_t)r[{target}])[{}];",
7685                            *index as usize + 1
7686                        ));
7687                    }
7688                    Kind::Struct(_) | Kind::StructSynth(_) => {
7689                        let fname = self
7690                            .const_text(*field)
7691                            .ok_or("BindArm of a non-Text field")?;
7692                        if fname == crate::vm::SOME_BINDING_MARKER {
7693                            self.line(&format!("r[{dst}] = r[{target}];"));
7694                        } else {
7695                            let cell = self.field_cell_of_name(tk, fname).ok_or_else(|| {
7696                                format!("an arm binds the field `{fname}` its record does not have")
7697                            })?;
7698                            self.line(&format!(
7699                                "r[{dst}] = ((int64_t*)(intptr_t)r[{target}])[{cell}];"
7700                            ));
7701                        }
7702                    }
7703                    Kind::Optional(_) => {
7704                        self.line(&format!(
7705                            "if (((int64_t*)(intptr_t)r[{target}])[0]) r[{dst}] = ((int64_t*)(intptr_t)r[{target}])[1];"
7706                        ));
7707                    }
7708                    Kind::Nothing => {}
7709                    Kind::Unknown => return Err(format!("BindArm on a non-enum kind {tk:?}")),
7710                    _ if *index == 0 => self.line(&format!("r[{dst}] = r[{target}];")),
7711                    _ => {}
7712                }
7713            }
7714            Op::Index {
7715                dst,
7716                collection,
7717                index,
7718            }
7719            | Op::IndexUnchecked {
7720                dst,
7721                collection,
7722                index,
7723            } => {
7724                let unchecked = matches!(op, Op::IndexUnchecked { .. });
7725                let ck = kind_at(kinds, *collection);
7726                if let Some((kk, vk)) = self.resolve_map_kv(ck) {
7727                    // `item k of m` — a map lookup; an absent key fails the run in the VM's words. The value
7728                    // rides its cell as any kind: a Text as the returned `char*`, everything else as its raw
7729                    // i64 register encoding.
7730                    let (pre, ptr) = (
7731                        map_fn(kk, vk).unwrap(),
7732                        map_ptr_kv(*collection, kk, vk).unwrap(),
7733                    );
7734                    let key = map_key_arg(*index, kk);
7735                    let shown = match kk {
7736                        Kind::Text => key.clone(),
7737                        _ => format!("logos_dyn_sprintf(\"%\" PRId64, r[{index}])"),
7738                    };
7739                    self.line(&format!(
7740                        "if (!{pre}_contains({ptr}, {key})) logos_fatalf({}, {shown});",
7741                        words::missing_key()
7742                    ));
7743                    let get = format!("{pre}_get({ptr}, {key})");
7744                    self.line(&format!("r[{dst}] = {};", map_val_load(&get, vk)));
7745                } else if let Kind::Tuple(ti) = ck {
7746                    // Positional tuple cell read — the cell already holds the register-encoded value (Text ptr /
7747                    // Float bits / Int), exactly like a struct field. A LOGOS index is 1-based or counts back from
7748                    // the end, and one naming no position fails as the VM's does.
7749                    let n = self.tuple_positions(ti).len();
7750                    let at = self.int_operand(kinds, *index, words::IntegerPlace::Tuple);
7751                    self.line(&format!("r[{dst}] = ((int64_t*)(intptr_t)r[{collection}])[logos_idx0({at}, {n})];"));
7752                } else if ck == Kind::Text {
7753                    if unchecked {
7754                        crate::optimize::mark_backend_decline(
7755                            crate::optimization::Opt::Unchecked,
7756                            "unchecked: a Text is indexed through its bounds-checked character accessor",
7757                        );
7758                    }
7759                    let owned = self.owned_flag(*collection);
7760                    let at = self.int_operand(kinds, *index, words::IntegerPlace::Text);
7761                    self.line(&format!("r[{dst}] = (int64_t)(intptr_t)lc_text_item_of(r[{collection}], {owned}, {at});"));
7762                } else {
7763                    let sk = seq_kind_of(ck);
7764                    sk.seq_infix()
7765                        .ok_or_else(|| format!("Index on a non-sequence kind {ck:?}"))?;
7766                    let ptr = Self::seq_ptr(*collection, sk)?;
7767                    let index_word = self.int_operand(kinds, *index, words::IntegerPlace::ListRead);
7768                    let at = if unchecked {
7769                        crate::optimize::mark_fired(crate::optimization::Opt::Unchecked);
7770                        format!("(size_t)({index_word} - 1LL)")
7771                    } else {
7772                        format!("logos_idx0({index_word}, {ptr}->len)")
7773                    };
7774                    let cell = format!("{ptr}->data[{at}]");
7775                    self.line(&format!(
7776                        "r[{dst}] = {};",
7777                        elem_to_reg(&cell, self.elem_kind(sk))
7778                    ));
7779                }
7780            }
7781            Op::Length { dst, collection } => {
7782                let ck = kind_at(kinds, *collection);
7783                if ck == Kind::Text {
7784                    let owned = self.owned_flag(*collection);
7785                    self.line(&format!(
7786                        "r[{dst}] = lc_text_len(r[{collection}], {owned});"
7787                    ));
7788                } else if let Some((kk, vk)) = self.resolve_map_kv(ck) {
7789                    // Distinct-entry count — the struct's `len` field (bumped once per key), matching the AST C.
7790                    self.line(&format!(
7791                        "r[{dst}] = (int64_t){}->len;",
7792                        map_ptr_kv(*collection, kk, vk).unwrap()
7793                    ));
7794                } else if let Some(sk) = resolve_set(ck) {
7795                    let infix = sk.seq_infix().unwrap();
7796                    let ptr = Self::set_ptr(*collection, sk)?;
7797                    self.line(&format!("r[{dst}] = set_{infix}_len({ptr});"));
7798                } else if let Kind::Tuple(ti) = ck {
7799                    self.line(&format!("r[{dst}] = {};", self.tuple_positions(ti).len()));
7800                } else {
7801                    let sk = seq_kind_of(ck);
7802                    sk.seq_infix()
7803                        .ok_or_else(|| format!("Length on a non-sequence kind {ck:?}"))?;
7804                    let ptr = Self::seq_ptr(*collection, sk)?;
7805                    self.line(&format!("r[{dst}] = (int64_t){ptr}->len;"));
7806                }
7807            }
7808            Op::Contains {
7809                dst,
7810                collection,
7811                value,
7812            } => {
7813                let ck = kind_at(kinds, *collection);
7814                if let Some((kk, vk)) = self.resolve_map_kv(ck) {
7815                    // `m contains k` — key membership.
7816                    let (pre, ptr) = (
7817                        map_fn(kk, vk).unwrap(),
7818                        map_ptr_kv(*collection, kk, vk).unwrap(),
7819                    );
7820                    self.line(&format!(
7821                        "r[{dst}] = {pre}_contains({ptr}, {});",
7822                        map_key_arg(*value, kk)
7823                    ));
7824                } else if let Some(sk) = resolve_set(ck) {
7825                    let infix = sk.seq_infix().unwrap();
7826                    let ptr = Self::set_ptr(*collection, sk)?;
7827                    self.line(&format!(
7828                        "r[{dst}] = set_{infix}_contains({ptr}, {});",
7829                        as_elem_push(*value, sk.elem())
7830                    ));
7831                } else if ck == Kind::Text {
7832                    // `text contains part` — a substring test; any other needle fails as the VM's does.
7833                    let vk = kind_at(kinds, *value);
7834                    if vk == Kind::Text {
7835                        self.line(&format!(
7836                            "r[{dst}] = (strstr({}, {}) != NULL);",
7837                            as_cstr(*collection),
7838                            as_cstr(*value)
7839                        ));
7840                    } else {
7841                        let sample =
7842                            sample_value(vk).ok_or_else(|| format!("Text contains a {vk:?}"))?;
7843                        self.line(&format!(
7844                            "logos_fatalf({});",
7845                            words::text_contains_refused(sample)
7846                        ));
7847                    }
7848                } else {
7849                    let sk = seq_kind_of(ck);
7850                    let infix = sk
7851                        .seq_infix()
7852                        .ok_or_else(|| format!("Contains on a non-sequence kind {ck:?}"))?;
7853                    let ptr = Self::seq_ptr(*collection, sk)?;
7854                    let (ek, vk) = (self.elem_kind(sk), kind_at(kinds, *value));
7855                    if ek == Kind::Rational || vk == Kind::Rational {
7856                        // A rational is found by its value, an integer among rationals by the rational it equals.
7857                        let n = self.fresh();
7858                        let item = elem_to_reg(&format!("{ptr}->data[_ci{n}]"), ek);
7859                        let same = self.eq_expr(&item, ek, &format!("r[{value}]"), vk)?;
7860                        self.line(&format!(
7861                            "{{ int64_t _cf{n} = 0; for (size_t _ci{n} = 0; _ci{n} < {ptr}->len && !_cf{n}; _ci{n}++) _cf{n} = {same}; r[{dst}] = _cf{n}; }}"
7862                        ));
7863                    } else {
7864                        self.line(&format!(
7865                            "r[{dst}] = seq_{infix}_contains({ptr}, {});",
7866                            as_elem_push(*value, ek)
7867                        ));
7868                    }
7869                }
7870            }
7871            Op::ListPush { list, value } => {
7872                // The list's kind AFTER this op is the pushed-element seq kind (its kind here, pre-op, may still
7873                // be a freshly-created seq), or the kind its variable holds when a number join lands the push as
7874                // that; an integer pushed among rationals goes in as the rational it equals.
7875                let vk = kind_at(kinds, *value);
7876                let seq_kind = match kind_at(after, *list) {
7877                    k @ Kind::SeqOf(_) if self.elem_kind(k) == Kind::Rational => k,
7878                    _ => self.seq_kind_for(vk),
7879                };
7880                let infix = seq_kind.seq_infix().ok_or_else(|| {
7881                    format!("Push of an element of unknown kind into {seq_kind:?}")
7882                })?;
7883                self.own_target(*list, seq_kind)?;
7884                let ptr = Self::seq_ptr(*list, seq_kind)?;
7885                let item = self.elem_value(*value, vk, self.elem_kind(seq_kind))?;
7886                self.line(&format!("seq_{infix}_push({ptr}, {item});"));
7887            }
7888            // `Push v to obj's field` — append into the Seq that lives in a struct field cell. Same typed push
7889            // helper as `ListPush`; the field's boundary kind gives the element type, and the cell holds the
7890            // boxed seq pointer (as every struct cell holds its value).
7891            Op::ListPushField { obj, field, src } => {
7892                let si = match kind_at(kinds, *obj) {
7893                    Kind::Struct(si) => si,
7894                    k => return Err(format!("ListPushField on a non-struct kind {k:?}")),
7895                };
7896                let (cell, fkind) = self
7897                    .resolve_field(si, *field)
7898                    .ok_or("ListPushField of an unknown field")?;
7899                if fkind == Kind::Tower {
7900                    return self.push_to_exact_field(*obj, si, cell, *src, kinds);
7901                }
7902                if kind_at(kinds, *src) == Kind::Tower {
7903                    return Err(format!("an exact value pushed onto a field of kind {fkind:?} is not yet lowered"));
7904                }
7905                let infix = fkind.seq_infix().ok_or_else(|| {
7906                    format!("ListPushField into a non-sequence field kind {fkind:?}")
7907                })?;
7908                let ty = fkind.seq_c_type().ok_or_else(|| {
7909                    format!("ListPushField into a non-sequence field kind {fkind:?}")
7910                })?;
7911                // The record becomes the register's own, then the field's list the record's own: a list the record
7912                // may share with another holder is copied into the field before the push lands.
7913                self.own_target(*obj, Kind::Struct(si))?;
7914                let cell_val = format!("((int64_t*)(intptr_t)r[{obj}])[{cell}]");
7915                let word = self.field_owner_word(*obj, Kind::Struct(si)).unwrap();
7916                let held_alone = if cell < 64 {
7917                    format!("(({word} >> {cell}) & 1)")
7918                } else {
7919                    "0".to_string()
7920                };
7921                let n = self.fresh();
7922                self.line(&format!(
7923                    "if (!{held_alone}) {{ {ty} *_fl{n} = ({ty}*)lc_gc_alloc(sizeof({ty})); *_fl{n} = seq_{infix}_copy(({ty}*)(intptr_t){cell_val}); {cell_val} = (int64_t)(intptr_t)_fl{n};{} }}",
7924                    if cell < 64 { format!(" {word} |= 1ULL << {cell};") } else { String::new() }
7925                ));
7926                self.line(&format!(
7927                    "seq_{infix}_push(({ty}*)(intptr_t){cell_val}, {});",
7928                    as_elem_push(*src, self.elem_kind(fkind))
7929                ));
7930            }
7931            // ── for-each iterator machine (stack of frames; IterNext/IterPop act on the innermost) ──────────
7932            Op::IterPrepare { iterable } => {
7933                // The loop walks the collection the register held when it began: a snapshot of the handle, so
7934                // a change the body makes to the register (its own copy, under value semantics) is not seen.
7935                let raw = kind_at(kinds, *iterable);
7936                let id = self.fresh();
7937                if self.text_byte_iters.contains(&pc) {
7938                    self.line(&format!(
7939                        "const unsigned char *_itv{id} = (const unsigned char*)(intptr_t)r[{iterable}]; size_t _it{id} = 0;"
7940                    ));
7941                    self.iter_stack.push(IterFrame {
7942                        seq_kind: Kind::SeqInt,
7943                        text_bytes: true,
7944                        id,
7945                    });
7946                } else if raw == Kind::Text {
7947                    self.line(&format!(
7948                        "const char *_itv{id} = {}; size_t _it{id} = 0;",
7949                        as_cstr(*iterable)
7950                    ));
7951                    self.iter_stack.push(IterFrame {
7952                        seq_kind: Kind::Text,
7953                        text_bytes: false,
7954                        id,
7955                    });
7956                } else if let Some((kk, vk)) = self.resolve_map_kv(raw) {
7957                    // A map walks a snapshot of its entries, in insertion order, each a `(key, value)` tuple.
7958                    let ti = self.intern_tuple(vec![kk, vk]);
7959                    let (ty, ptr) = (
7960                        map_type_name(kk, vk).unwrap(),
7961                        map_ptr_kv(*iterable, kk, vk).unwrap(),
7962                    );
7963                    let key = if kk == Kind::Text {
7964                        format!("(int64_t)(intptr_t)_m{id}->keys[_si]")
7965                    } else {
7966                        format!("_m{id}->keys[_si]")
7967                    };
7968                    let val = map_val_load(&format!("_m{id}->vals[_si]"), vk);
7969                    self.line(&format!(
7970                        "{ty} *_m{id} = {ptr}; Seq_i64 *_itv{id} = (Seq_i64*)lc_gc_alloc(sizeof(Seq_i64)); *_itv{id} = seq_i64_new(); size_t _it{id} = 0;"
7971                    ));
7972                    self.line(&format!(
7973                        "for (size_t _oi = 0; _oi < _m{id}->ord_len; _oi++) {{ size_t _si = _m{id}->order[_oi]; if (!_m{id}->state[_si]) continue; int64_t *_kv = (int64_t*)lc_gc_calloc(2, sizeof(int64_t)); _kv[0] = {key}; _kv[1] = {val}; seq_i64_push(_itv{id}, (int64_t)(intptr_t)_kv); }}"
7974                    ));
7975                    self.iter_stack.push(IterFrame {
7976                        seq_kind: self.seq_kind_for(Kind::Tuple(ti)),
7977                        text_bytes: false,
7978                        id,
7979                    });
7980                } else if let Some(sk) = resolve_set(raw) {
7981                    // A set walks a snapshot of its elements, in insertion order.
7982                    let (set_ty, infix) = (sk.set_c_type().unwrap(), sk.seq_infix().unwrap());
7983                    let seq_kind = self.seq_kind_for(sk.elem());
7984                    let seq_ty = seq_kind.seq_c_type().unwrap();
7985                    self.line(&format!(
7986                        "{set_ty} *_st{id} = ({set_ty}*)(intptr_t)r[{iterable}]; {seq_ty} *_itv{id} = ({seq_ty}*)lc_gc_alloc(sizeof({seq_ty})); *_itv{id} = seq_{infix}_new(); size_t _it{id} = 0;"
7987                    ));
7988                    self.line(&format!(
7989                        "for (size_t _oi = 0; _oi < _st{id}->ord_len; _oi++) {{ size_t _si = _st{id}->order[_oi]; if (_st{id}->state[_si]) seq_{infix}_push(_itv{id}, _st{id}->keys[_si]); }}"
7990                    ));
7991                    self.iter_stack.push(IterFrame {
7992                        seq_kind,
7993                        text_bytes: false,
7994                        id,
7995                    });
7996                } else {
7997                    let seq_kind = seq_kind_of(raw);
7998                    let ty = seq_kind
7999                        .seq_c_type()
8000                        .ok_or_else(|| format!("iteration over a non-sequence kind {raw:?}"))?;
8001                    self.line(&format!(
8002                        "{ty} *_itv{id} = ({ty}*)(intptr_t)r[{iterable}]; size_t _it{id} = 0;"
8003                    ));
8004                    self.iter_stack.push(IterFrame {
8005                        seq_kind,
8006                        text_bytes: false,
8007                        id,
8008                    });
8009                }
8010            }
8011            Op::IterNext { dst, exit } => {
8012                let frame = self
8013                    .iter_stack
8014                    .last()
8015                    .ok_or("IterNext with no active iterator")?;
8016                let (seq_kind, id) = (frame.seq_kind, frame.id);
8017                if frame.text_bytes {
8018                    self.line(&format!(
8019                        "if (!_itv{id}[_it{id}]) goto {}{exit};",
8020                        self.label
8021                    ));
8022                    self.line(&format!(
8023                        "r[{dst}] = (int64_t)_itv{id}[_it{id}]; _it{id}++;"
8024                    ));
8025                } else if seq_kind == Kind::Text {
8026                    // The next character, as its own Text.
8027                    self.line(&format!(
8028                        "if (!_itv{id}[_it{id}]) goto {}{exit};",
8029                        self.label
8030                    ));
8031                    self.line(&format!(
8032                        "{{ size_t _n = lc_utf8_len((unsigned char)_itv{id}[_it{id}]); char *_c = (char*)lc_gc_alloc_atomic(_n + 1); memcpy(_c, _itv{id} + _it{id}, _n); _c[_n] = 0; r[{dst}] = (int64_t)(intptr_t)_c; _it{id} += _n; }}"
8033                    ));
8034                } else {
8035                    self.line(&format!(
8036                        "if (_it{id} >= _itv{id}->len) goto {}{exit};",
8037                        self.label
8038                    ));
8039                    let read = elem_to_reg(
8040                        &format!("_itv{id}->data[_it{id}]"),
8041                        self.elem_kind(seq_kind),
8042                    );
8043                    self.line(&format!("r[{dst}] = {read}; _it{id}++;"));
8044                }
8045            }
8046            Op::IterPop => {
8047                self.iter_stack.pop();
8048            }
8049            // ── Show renders by the source register's inferred kind ────────────────────────────────────────
8050            Op::Show { src } => match kind_at(kinds, *src) {
8051                Kind::Int => self.line(&format!("show_i64(r[{src}]);")),
8052                Kind::Bool => self.line(&format!("show_bool(r[{src}]);")),
8053                Kind::Float => self.line(&format!("show_f64(as_f64(r[{src}]));")),
8054                Kind::Text => self.line(&format!("show_str({});", as_cstr(*src))),
8055                // Arbitrary-precision integer renders its exact value via `lint_to_string` (+ newline).
8056                Kind::BigInt => self.line(&format!("show_lint(lc_xget(r[{src}]));")),
8057                Kind::Word(_) => self.line(&format!(
8058                    "printf(\"%\" PRIu64 \"\\n\", (uint64_t)r[{src}]);"
8059                )),
8060                Kind::Unknown => {
8061                    return Err(
8062                        "Show of a register whose kind is not yet modeled (Unknown)".to_string()
8063                    )
8064                }
8065                k => {
8066                    let text = self.render(&format!("r[{src}]"), k)?;
8067                    self.line(&format!("show_str({text});"));
8068                }
8069            },
8070            // "error WHEN EXECUTED" (a non-exhaustive `Inspect` fallthrough, an unbound `Set`, …): trap exactly
8071            // where the VM would error. The message is stderr-only (never compared to the VM's stdout).
8072            Op::FailWith { msg } => {
8073                let m = match &self.prog.constants[*msg as usize] {
8074                    Constant::Text(s) => c_escape(s),
8075                    _ => "FailWith".to_string(),
8076                };
8077                self.line(&format!("logos_fatal(\"{m}\");"));
8078            }
8079            // Optimizer/JIT artifacts with no AOT effect: a region bound guard (the emitted AOT code IS the
8080            // correct slow path, so the guard always passes — a deopt only exists for the JIT tier) and an
8081            // observation site (a JIT profiling marker). Both lower to nothing, exactly as `codegen_riscv` does.
8082            Op::RegionBoundsGuard { .. } | Op::Obs { .. } => {}
8083            // `today` / `now` — the injected fixed clock the differential harness sets, else the real one.
8084            Op::LoadToday { dst } => self.line(&format!("r[{dst}] = logos_today();")),
8085            Op::LoadNow { dst } => self.line(&format!("r[{dst}] = logos_now();")),
8086            // `args()` — the program's argument vector, program name first.
8087            Op::Args { dst } => {
8088                let n = self.fresh();
8089                self.line(&format!(
8090                    "Seq_str *_av{n} = (Seq_str*)lc_gc_alloc(sizeof(Seq_str)); *_av{n} = seq_str_new();"
8091                ));
8092                self.line(&format!(
8093                    "for (int _a = 0; _a < lc_argc; _a++) seq_str_push(_av{n}, lc_argv[_a]);"
8094                ));
8095                self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_av{n};"));
8096            }
8097            // Reversible-block primitives (uncompute-safe integer updates) — mirror `codegen_riscv`.
8098            Op::RevSwap { a, b } => {
8099                let t = self.fresh();
8100                self.line("/* ping-pong buffer swap */");
8101                self.line(&format!(
8102                    "{{ int64_t _rs{t} = r[{a}]; r[{a}] = r[{b}]; r[{b}] = _rs{t}; }}"
8103                ));
8104                // The ownership bits travel with the handles; a register the region does not track holds a
8105                // handle that may be shared, so the tracked side that receives it is no longer its only holder.
8106                match (self.owned.contains(a), self.owned.contains(b)) {
8107                    (true, true) => self.line(&format!("{{ unsigned char _rso{t} = own[{a}]; own[{a}] = own[{b}]; own[{b}] = _rso{t}; }}")),
8108                    (true, false) => self.line(&format!("own[{a}] = 0;")),
8109                    (false, true) => self.line(&format!("own[{b}] = 0;")),
8110                    (false, false) => {}
8111                }
8112                // A side held exact that received a machine word holds it tagged.
8113                for (side, from) in [(*a, *b), (*b, *a)] {
8114                    if kind_at(after, side) == Kind::BigInt
8115                        && matches!(kind_at(kinds, from), Kind::Int | Kind::Bool)
8116                    {
8117                        self.line(&format!("r[{side}] = lc_xof(r[{side}]);"));
8118                    }
8119                }
8120            }
8121            // Module-level globals ride a file-scope `int64_t g[]`.
8122            // A global holds a collection like a record's field does: the bytecode takes it out (reading it,
8123            // then emptying the global) to change it in a register and puts it back, and the register holds it
8124            // alone exactly when the global did. Any other read leaves it shared.
8125            Op::GlobalGet { dst, idx } => {
8126                self.line(&format!("r[{dst}] = g[{idx}];"));
8127                if self.takes_out_global(pc) {
8128                    if self.owned.contains(dst) {
8129                        self.line(&format!("own[{dst}] = gown[{idx}];"));
8130                    }
8131                } else if !self.stays_an_inspection(*dst) {
8132                    self.line(&format!("gown[{idx}] = 0;"));
8133                }
8134            }
8135            Op::GlobalSet { idx, src } => {
8136                let value = match (
8137                    self.global_kinds
8138                        .get(*idx as usize)
8139                        .copied()
8140                        .unwrap_or(Kind::Unknown),
8141                    kind_at(kinds, *src),
8142                ) {
8143                    (Kind::BigInt, Kind::Int | Kind::Bool) => format!("lc_xof(r[{src}])"),
8144                    (Kind::Int, Kind::BigInt) => format!("lc_xnarrow(r[{src}])"),
8145                    (Kind::Tower, k) if k != Kind::Tower => format!(
8146                        "(int64_t)(intptr_t)({})",
8147                        self.tower_of(&format!("r[{src}]"), k)?
8148                    ),
8149                    _ => format!("r[{src}]"),
8150                };
8151                self.line(&format!("g[{idx}] = {value};"));
8152                let sole = if self.owned.contains(src) && self.dead_after(pc, *src) {
8153                    format!("own[{src}]")
8154                } else {
8155                    "0".to_string()
8156                };
8157                self.line(&format!("gown[{idx}] = {sole};"));
8158            }
8159            Op::RevXor { place, delta } => self.line(&format!("r[{place}] ^= r[{delta}];")),
8160            Op::RevNeg { place } => self.line(&format!("r[{place}] = -r[{place}];")),
8161            Op::RevAdd { place, delta } => self.line(&format!("r[{place}] += r[{delta}];")),
8162            Op::RevSub { place, delta } => self.line(&format!("r[{place}] -= r[{delta}];")),
8163            // Deep copy: box a fresh collection (its value semantics), or copy the scalar.
8164            Op::DeepClone { dst, src } => {
8165                let e = self
8166                    .clone_collection_expr(*src, kind_at(kinds, *src))?
8167                    .unwrap_or_else(|| format!("r[{src}]"));
8168                self.line(&format!("r[{dst}] = {e};"));
8169            }
8170            Op::Halt => {}
8171            Op::Return { src } => {
8172                if in_function {
8173                    self.store_inout_params(kinds)?;
8174                    let owner = self.owned_flag(*src);
8175                    self.line(&format!("if (__ret_own) *__ret_own = {owner};"));
8176                    self.release_owned_scalar_sequences(Some(*src));
8177                    // A function whose result is exact returns a machine integer as an exact one; its word entry
8178                    // returns a machine integer as itself and an exact one marked `escaped`.
8179                    let exact = self
8180                        .current_fn
8181                        .is_some_and(|fi| self.fn_ret[fi] == Kind::BigInt);
8182                    let word = matches!(kind_at(kinds, *src), Kind::Int | Kind::Bool);
8183                    let tower = self
8184                        .current_fn
8185                        .is_some_and(|fi| self.fn_ret[fi] == Kind::Tower);
8186                    let declared = self.current_fn.map(|fi| self.fn_ret[fi]).unwrap_or(Kind::Unknown);
8187                    if tower && kind_at(kinds, *src) != Kind::Tower {
8188                        // An exact result is the runtime's value of what the function hands back.
8189                        let cell = self.tower_of(&format!("r[{src}]"), kind_at(kinds, *src))?;
8190                        self.line(&format!("return (int64_t)(intptr_t)({cell});"));
8191                    } else if self.widens_to(kind_at(kinds, *src), declared) {
8192                        // A rational result handed back as an integer is the rational it equals.
8193                        let v = self.convert_number(&format!("r[{src}]"), kind_at(kinds, *src), declared)?;
8194                        self.line(&format!("return {v};"));
8195                    } else if self.word_entry {
8196                        self.line(&self.ret(&format!("r[{src}]"), exact && !word));
8197                    } else if exact && word {
8198                        self.line(&format!("return lc_xof(r[{src}]);"));
8199                    } else {
8200                        self.line(&format!("return r[{src}];"));
8201                    }
8202                }
8203            }
8204            Op::ReturnNothing => {
8205                if in_function {
8206                    self.store_inout_params(kinds)?;
8207                    self.line("if (__ret_own) *__ret_own = 0;");
8208                    self.release_owned_scalar_sequences(None);
8209                    self.line(&self.ret("0", false));
8210                }
8211            }
8212            // ── CRDT lattice (single-replica; mirrors `codegen_riscv`, but over the C `int64_t*` struct layout) ──
8213            // A counter CRDT (`ConvergentCount`/`Tally`, kind 4/5) → a fresh `logos_crdt*` per-replica map; a
8214            // sequence CRDT (`SharedSequence`/RGA, kind 1) IS a plain empty seq on the single replica (its field
8215            // lays out as `Seq of T`, so `CrdtAppend` pushes in place, exactly like a shared-`Seq` field). Set /
8216            // register CRDTs (0/2/3) need their own lattices → refused.
8217            Op::NewCrdt { dst, kind } => {
8218                match kind {
8219                    4 | 5 => {
8220                        self.line(&format!("r[{dst}] = (int64_t)(intptr_t)logos_crdt_new();"));
8221                    }
8222                    1 => {
8223                        let t = self.fresh();
8224                        // The converged log is a plain seq behind a stable handle; its concrete element kind is
8225                        // refined once the first `CrdtAppend` (or a `GetField` typed by the field) lands. Box an empty
8226                        // `Seq_i64` — its handle rides the slot, and the field's declared `Seq of T` retypes accesses.
8227                        self.line(&format!("Seq_i64 *_cr{t} = (Seq_i64*)lc_gc_alloc(sizeof(Seq_i64)); *_cr{t} = seq_i64_new();"));
8228                        self.line(&format!("r[{dst}] = (int64_t)(intptr_t)_cr{t};"));
8229                    }
8230                    k => {
8231                        return Err(format!(
8232                        "NewCrdt of a non-counter/sequence CRDT kind {k} (set/register lattice)"
8233                    ))
8234                    }
8235                }
8236            }
8237            Op::CrdtBump {
8238                obj,
8239                field,
8240                amount,
8241                negate,
8242            } => {
8243                // `Increase/Decrease s's counter by n`. The VM's `crdt_counter_bump`: a LIVE counter field bumps
8244                // its own replica slot (`logos_crdt_bump`); a field that is a plain Int / `Nothing` (never a
8245                // `NewCrdt` value flowed in) just ADDS the (signed) amount to the cell, IN PLACE (visible through
8246                // every struct alias, which all share the same heap buffer pointer).
8247                let ok = kind_at(kinds, *obj);
8248                let (cell, _) = self
8249                    .resolve_field_any(ok, *field)
8250                    .ok_or("CrdtBump of an unknown struct field")?;
8251                let is_counter = self
8252                    .struct_name_of_kind(ok)
8253                    .map(|n| self.counter_fields.contains(&(n, cell)))
8254                    .unwrap_or(false);
8255                let cell_lv = format!("((int64_t*)(intptr_t)r[{obj}])[{cell}]");
8256                if is_counter {
8257                    let amount = if kind_at(kinds, *amount) == Kind::BigInt {
8258                        format!("lc_xnarrow(r[{amount}])")
8259                    } else {
8260                        format!("r[{amount}]")
8261                    };
8262                    self.line(&format!(
8263                        "logos_crdt_bump((logos_crdt*)(intptr_t){cell_lv}, {amount}, {});",
8264                        if *negate { 1 } else { 0 }
8265                    ));
8266                } else {
8267                    let op = if *negate { "lc_xsub" } else { "lc_xadd" };
8268                    let amount = as_exact(*amount, kind_at(kinds, *amount))?;
8269                    self.line(&format!("{cell_lv} = {op}({cell_lv}, {amount});"));
8270                }
8271            }
8272            Op::CrdtMerge { target, source } | Op::CrdtJoin { target, source } => {
8273                // Field-wise lattice join of `source` into `target`, in place. A counter field takes the
8274                // per-replica MAX (idempotent, `logos_crdt_merge`); a plain field ADDs (`Merge`, a grow-counter)
8275                // or MAXes (`Join`, the state-based join) — the VM's `crdt_merge_field`. Both structs share the
8276                // type, so iterate its cells.
8277                let is_join = matches!(op, Op::CrdtJoin { .. });
8278                let tk = kind_at(kinds, *target);
8279                let tname = self
8280                    .struct_name_of_kind(tk)
8281                    .ok_or("CRDT merge target is not a struct")?;
8282                let n = self
8283                    .struct_cell_count(tk)
8284                    .ok_or("CRDT merge of an unknown struct type")?;
8285                for cell in 0..n {
8286                    let t_lv = format!("((int64_t*)(intptr_t)r[{target}])[{cell}]");
8287                    let s_lv = format!("((int64_t*)(intptr_t)r[{source}])[{cell}]");
8288                    if self.counter_fields.contains(&(tname.clone(), cell)) {
8289                        self.line(&format!(
8290                            "logos_crdt_merge((logos_crdt*)(intptr_t){t_lv}, (logos_crdt*)(intptr_t){s_lv});"
8291                        ));
8292                    } else if self.field_storage_kind(tk, cell) == Some(Kind::BigInt) {
8293                        if is_join {
8294                            self.line(&format!(
8295                                "if (lc_xcmp({s_lv}, {t_lv}) > 0) {t_lv} = {s_lv};"
8296                            ));
8297                        } else {
8298                            self.line(&format!("{t_lv} = lc_xadd({t_lv}, {s_lv});"));
8299                        }
8300                    } else if is_join {
8301                        self.line(&format!("if ({s_lv} > {t_lv}) {t_lv} = {s_lv};"));
8302                    } else {
8303                        self.line(&format!("{t_lv} += {s_lv};"));
8304                    }
8305                }
8306            }
8307            Op::CrdtResolve { obj, field, value } => {
8308                // `Resolve s's reg to v` — a multi-value register's LWW resolution. Single-replica = the last
8309                // value written wins, so set the field's value cell in place (visible through every struct alias).
8310                let ok = kind_at(kinds, *obj);
8311                let (cell, _) = self
8312                    .resolve_field_any(ok, *field)
8313                    .ok_or("CrdtResolve of an unknown struct field")?;
8314                let stored = self.field_store_value(ok, cell, *value, kind_at(kinds, *value))?;
8315                self.line(&format!(
8316                    "((int64_t*)(intptr_t)r[{obj}])[{cell}] = {stored};"
8317                ));
8318            }
8319            Op::CrdtAppend { seq, value } => {
8320                // `Append v to <seq field>` — the sequence is shared behind a stable seq handle in the struct
8321                // field, so append IN PLACE; the mutation propagates to every alias (no write-back). The pushed
8322                // value's kind picks the typed seq call (the field was a `NewCrdt` kind-1 empty `Seq_i64` handle).
8323                let seq_kind = self.seq_kind_for(kind_at(kinds, *value));
8324                let infix = seq_kind.seq_infix().ok_or_else(|| {
8325                    format!("CrdtAppend of an element of unknown kind {seq_kind:?}")
8326                })?;
8327                let ty = seq_kind.seq_c_type().unwrap();
8328                self.line(&format!(
8329                    "seq_{infix}_push(({ty}*)(intptr_t)r[{seq}], {});",
8330                    as_elem_push(*value, self.elem_kind(seq_kind))
8331                ));
8332            }
8333            // ── Peer messaging (the VM's offline `NetInbox`, see `c_net_runtime`) ──────────────────────────
8334            // `Connect` validates its address and dials nothing; `Listen` names this node's inbox; `Sync` of a
8335            // value is offline identity, so the register keeps what it holds.
8336            Op::NetConnect { url, .. } => {
8337                let text = self.text_of(*url, kind_at(kinds, *url))?;
8338                self.line(&format!("logos_net_connect({text});"));
8339            }
8340            Op::NetListen { topic } => {
8341                let text = self.text_of(*topic, kind_at(kinds, *topic))?;
8342                self.line(&format!("logos_net_listen({text});"));
8343            }
8344            Op::NetSync { .. } => {}
8345            // A peer handle is its address's canonical topic.
8346            Op::NetMakePeer { dst, addr } => {
8347                let text = self.text_of(*addr, kind_at(kinds, *addr))?;
8348                self.line(&format!(
8349                    "r[{dst}] = (int64_t)logos_net_makepeer((long long)(intptr_t)({text}));"
8350                ));
8351            }
8352            // A message travels as its register encoding — the value itself, or its heap handle — and arrives
8353            // as the kind every `Send` of the program sends (`net_message`).
8354            Op::NetSend { to, msg } => {
8355                if let Some(to) = self.peer_address(*to, kinds)? {
8356                    self.line(&format!("logos_net_send({to}, (long long)r[{msg}]);"));
8357                }
8358            }
8359            Op::NetStream { to, values } => {
8360                if let Some(to) = self.peer_address(*to, kinds)? {
8361                    self.line(&format!("logos_net_stream({to}, (long long)r[{values}]);"));
8362                }
8363            }
8364            Op::NetAwait { dst, from, stream } => {
8365                if let Some(from) = self.peer_address(*from, kinds)? {
8366                    let take = if *stream {
8367                        "logos_net_await_stream"
8368                    } else {
8369                        "logos_net_await"
8370                    };
8371                    self.line(&format!("r[{dst}] = (int64_t){take}({from});"));
8372                }
8373            }
8374            // ── Real-socket TCP client (`tcp.lg` natives) ──────────────────────────────────────────────────
8375            // Each op calls its `C_TCP_RUNTIME` helper and BOXES the raw result into the LOGOS `Result` value
8376            // `r[dst]` — a heap `[tag, payload]` buffer (tag 0 = `Ok`, tag 1 = `Err`), the SAME layout a two-
8377            // variant `Kind::Enum` uses, so `Inspect`'s `TestArm`/`BindArm` read it uniformly. The `Err` payload
8378            // is the shared error `char*` (byte-identical to the Rust tiers); the `Ok` payload is the handle /
8379            // byte-count Int, the received `Seq of Int`, or `Nothing` (for the `Unit`-returning close/timeout).
8380            // `kind` is the `TcpOp::code()` value (connect=0, send=1, recv=2, recvExact=3, close=4, setTimeout=5,
8381            // listen=6, accept=7, listenerPort=8, shutdownWrite=9); operands ride `r[args_start..+arg_count]`.
8382            Op::Tcp {
8383                dst,
8384                kind,
8385                args_start,
8386                arg_count,
8387            } => {
8388                let a = |i: u16| args_start + i;
8389                let n = self.fresh();
8390                let err = format!("_tcperr{n}");
8391                self.line(&format!("char *{err} = NULL;"));
8392                // Emit `r[dst] = <Ok box of `ok_expr`>` OR `r[dst] = <Err box of `err`>` guarded by `cond` (true →
8393                // Err). The Result box is the one `NewInductive` builds: `lc_gc_calloc(2, int64_t)`, cell[0] = the tag.
8394                let (ok_tag, err_tag) = (
8395                    self.ctor_tag("Result", "Ok").unwrap(),
8396                    self.ctor_tag("Result", "Err").unwrap(),
8397                );
8398                let box_result = |em: &mut Self, ok_expr: &str, cond: &str| {
8399                    let b = em.fresh();
8400                    em.line(&format!(
8401                        "int64_t *_res{b} = (int64_t*)lc_gc_calloc(2, sizeof(int64_t));"
8402                    ));
8403                    em.line(&format!("if ({cond}) {{ _res{b}[0] = {err_tag}; _res{b}[1] = (int64_t)(intptr_t){err}; }}"));
8404                    em.line(&format!(
8405                        "else {{ _res{b}[0] = {ok_tag}; _res{b}[1] = (int64_t){ok_expr}; }}"
8406                    ));
8407                    em.line(&format!("r[{dst}] = (int64_t)(intptr_t)_res{b};"));
8408                };
8409                match *kind {
8410                    // Connect(host: Text, port: Int) -> Result of Int and Text — Ok = the connection handle.
8411                    0 => {
8412                        let c = self.fresh();
8413                        self.line(&format!(
8414                            "long long _tc{c} = logos_tcp_connect({}, r[{}], &{err});",
8415                            as_cstr(a(0)),
8416                            a(1)
8417                        ));
8418                        box_result(self, &format!("_tc{c}"), &format!("_tc{c} < 0"));
8419                    }
8420                    // Send(conn: Int, data: Seq of Int) -> Result of Int and Text — Ok = the byte count sent.
8421                    1 => {
8422                        let dptr = Self::seq_ptr(a(1), Kind::SeqInt)?;
8423                        let c = self.fresh();
8424                        self.line(&format!(
8425                            "long long _tc{c} = logos_tcp_send(r[{}], {dptr}, &{err});",
8426                            a(0)
8427                        ));
8428                        box_result(self, &format!("_tc{c}"), &format!("_tc{c} < 0"));
8429                    }
8430                    // Recv(conn, maxLen) / RecvExact(conn, len) -> Result of (Seq of Int) and Text — Ok = the bytes.
8431                    2 | 3 => {
8432                        let f = if *kind == 2 {
8433                            "logos_tcp_recv"
8434                        } else {
8435                            "logos_tcp_recv_exact"
8436                        };
8437                        let c = self.fresh();
8438                        self.line(&format!(
8439                            "Seq_i64 *_tc{c} = {f}(r[{}], r[{}], &{err});",
8440                            a(0),
8441                            a(1)
8442                        ));
8443                        box_result(
8444                            self,
8445                            &format!("(intptr_t)_tc{c}"),
8446                            &format!("_tc{c} == NULL"),
8447                        );
8448                    }
8449                    // Close(conn) -> Result of Unit and Text — Ok = `Nothing` (0), like the VM's `RuntimeValue::Nothing`.
8450                    4 => {
8451                        let c = self.fresh();
8452                        self.line(&format!(
8453                            "long long _tc{c} = logos_tcp_close(r[{}], &{err});",
8454                            a(0)
8455                        ));
8456                        box_result(self, "0", &format!("_tc{c} < 0"));
8457                    }
8458                    // Listen(port) -> Result of Int and Text — Ok = the listener handle.
8459                    6 => {
8460                        let c = self.fresh();
8461                        self.line(&format!(
8462                            "long long _tc{c} = logos_tcp_listen(r[{}], &{err});",
8463                            a(0)
8464                        ));
8465                        box_result(self, &format!("_tc{c}"), &format!("_tc{c} < 0"));
8466                    }
8467                    // Accept(listener) -> Result of Int and Text — Ok = the new connection handle.
8468                    7 => {
8469                        let c = self.fresh();
8470                        self.line(&format!(
8471                            "long long _tc{c} = logos_tcp_accept(r[{}], &{err});",
8472                            a(0)
8473                        ));
8474                        box_result(self, &format!("_tc{c}"), &format!("_tc{c} < 0"));
8475                    }
8476                    // ListenerPort(listener) -> Result of Int and Text — Ok = the bound local port.
8477                    8 => {
8478                        let c = self.fresh();
8479                        self.line(&format!(
8480                            "long long _tc{c} = logos_tcp_listener_port(r[{}], &{err});",
8481                            a(0)
8482                        ));
8483                        box_result(self, &format!("_tc{c}"), &format!("_tc{c} < 0"));
8484                    }
8485                    // SetTimeout(conn, millis) -> Result of Unit and Text — Ok = `Nothing` (0). Real POSIX
8486                    // `setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)` on the live fd, matching the VM's per-handle deadline.
8487                    5 => {
8488                        let c = self.fresh();
8489                        self.line(&format!(
8490                            "long long _tc{c} = logos_tcp_set_timeout(r[{}], r[{}], &{err});",
8491                            a(0),
8492                            a(1)
8493                        ));
8494                        box_result(self, "0", &format!("_tc{c} < 0"));
8495                    }
8496                    // ShutdownWrite(conn) -> Result of Unit and Text — Ok = `Nothing` (0). Real `shutdown(fd, SHUT_WR)`
8497                    // half-close (the peer reads EOF), matching the VM's `shutdown_write`.
8498                    9 => {
8499                        let c = self.fresh();
8500                        self.line(&format!(
8501                            "long long _tc{c} = logos_tcp_shutdown_write(r[{}], &{err});",
8502                            a(0)
8503                        ));
8504                        box_result(self, "0", &format!("_tc{c} < 0"));
8505                    }
8506                    other => return Err(format!("Tcp op with unknown kind {other}")),
8507                }
8508                let _ = arg_count;
8509            }
8510            // `Check that <subject> is <predicate>` / capability — resolve the predicate to its condition from
8511            // the policy registry, compile the condition against the subject struct's field cells, and TRAP on a
8512            // false result exactly where the VM's `check_policy` errors (`Security Check Failed` / undefined rule
8513            // / missing registry). Mirrors `codegen_riscv`'s `emit_policy_condition`, in a C boolean expression.
8514            Op::CheckPolicy {
8515                subject,
8516                predicate,
8517                is_capability,
8518                object,
8519                source_text,
8520            } => {
8521                let src_msg: String = self
8522                    .const_text(*source_text)
8523                    .unwrap_or_default()
8524                    .to_string();
8525                let msg_lit = c_escape(&src_msg);
8526                let trap = |em: &mut Self| {
8527                    em.line("fflush(stdout);");
8528                    em.line(&format!(
8529                        "fprintf(stderr, \"Security Check Failed: {msg_lit}\\n\"); exit(1);"
8530                    ));
8531                };
8532                let Some((interner, policies)) = self.policy_ctx else {
8533                    // The VM traps: "Security Check requires policies. Use compiled Rust or add ## Policy block."
8534                    trap(self);
8535                    return Ok(());
8536                };
8537                let subj_kind = kind_at(kinds, *subject);
8538                let Some(subj_type) = self.struct_name_of_kind(subj_kind) else {
8539                    trap(self); // VM: "Check subject must be a struct"
8540                    return Ok(());
8541                };
8542                let Some(type_sym) = interner.lookup(&subj_type) else {
8543                    trap(self); // VM: "Unknown type in Check statement"
8544                    return Ok(());
8545                };
8546                let obj_type = if *object != u16::MAX {
8547                    self.struct_name_of_kind(kind_at(kinds, *object))
8548                } else {
8549                    None
8550                };
8551                let cond = if *is_capability {
8552                    policies.get_capabilities(type_sym).and_then(|caps| {
8553                        caps.iter()
8554                            .find(|c| c.action == *predicate)
8555                            .map(|c| c.condition.clone())
8556                    })
8557                } else {
8558                    policies.get_predicates(type_sym).and_then(|preds| {
8559                        preds
8560                            .iter()
8561                            .find(|p| p.predicate_name == *predicate)
8562                            .map(|p| p.condition.clone())
8563                    })
8564                };
8565                match cond {
8566                    Some(c) => {
8567                        let expr = self.policy_condition_expr(
8568                            &c,
8569                            *subject,
8570                            &subj_type,
8571                            *object,
8572                            obj_type.as_deref(),
8573                            interner,
8574                        )?;
8575                        self.line(&format!("if (!({expr})) {{"));
8576                        trap(self);
8577                        self.line("}");
8578                    }
8579                    None => trap(self), // VM: "No predicate/capability defined for type"
8580                }
8581            }
8582            other => return Err(format!("op not yet lowered: {}", other.name())),
8583        }
8584        Ok(())
8585    }
8586
8587    /// `r[dst] = <lhs> <cop> <rhs>` over words: both operands of one width, the result wrapped to it (a
8588    /// division by zero, or two widths, or a word beside another kind, fails as the VM's does).
8589    fn word_arith(
8590        &mut self,
8591        dst: u16,
8592        lhs: u16,
8593        rhs: u16,
8594        cop: &str,
8595        lk: Kind,
8596        rk: Kind,
8597    ) -> Result<(), String> {
8598        let refused = |em: &mut Self| -> Result<(), String> {
8599            let (Some(l), Some(r)) = (sample_value(lk), sample_value(rk)) else {
8600                return Err(format!("`{cop}` over {lk:?} and {rk:?}"));
8601            };
8602            let result = match cop {
8603                "+" => crate::semantics::arith::add(l, r),
8604                "-" => crate::semantics::arith::subtract(l, r),
8605                "*" => crate::semantics::arith::multiply(l, r),
8606                "/" => crate::semantics::arith::divide(l, r),
8607                "%" => crate::semantics::arith::modulo(l, r),
8608                "&" => {
8609                    crate::semantics::arith::binary_op(crate::ast::stmt::BinaryOpKind::BitAnd, l, r)
8610                }
8611                "|" => {
8612                    crate::semantics::arith::binary_op(crate::ast::stmt::BinaryOpKind::BitOr, l, r)
8613                }
8614                _ => {
8615                    crate::semantics::arith::binary_op(crate::ast::stmt::BinaryOpKind::BitXor, l, r)
8616                }
8617            };
8618            let sentence = match result {
8619                Err(sentence) => sentence,
8620                Ok(_) => return Err(format!("the VM combines {lk:?} and {rk:?} with `{cop}`")),
8621            };
8622            em.line(&format!("logos_fatalf({});", words::literal(&sentence)));
8623            Ok(())
8624        };
8625        let (Kind::Word(a), Kind::Word(b)) = (lk, rk) else {
8626            return refused(self);
8627        };
8628        if a != b {
8629            return refused(self);
8630        }
8631        let mask = if a == 64 {
8632            "UINT64_MAX".to_string()
8633        } else {
8634            format!("((1ULL << {a}) - 1)")
8635        };
8636        let (x, y) = (format!("(uint64_t)r[{lhs}]"), format!("(uint64_t)r[{rhs}]"));
8637        match cop {
8638            "/" | "%" => {
8639                let zero = if cop == "/" {
8640                    words::division_by_zero()
8641                } else {
8642                    words::modulo_by_zero()
8643                };
8644                self.line(&format!("if ({y} == 0) logos_fatalf({zero});"));
8645                self.line(&format!("r[{dst}] = (int64_t)({x} {cop} {y});"));
8646            }
8647            _ => self.line(&format!("r[{dst}] = (int64_t)(({x} {cop} {y}) & {mask});")),
8648        }
8649        Ok(())
8650    }
8651
8652    /// `r[dst] = <lhs> <cop> <rhs>` at the right kind: float when either operand is float, else int.
8653    fn arith(
8654        &mut self,
8655        dst: u16,
8656        lhs: u16,
8657        rhs: u16,
8658        cop: &str,
8659        kinds: &[Kind],
8660    ) -> Result<(), String> {
8661        let (lk, rk) = (kind_at(kinds, lhs), kind_at(kinds, rhs));
8662        // Arithmetic on a time value (a date and a span, an instant and a duration) is the runtime's own; machine
8663        // arithmetic on the numbers a slot holds them as would answer something the VM never does.
8664        if [lk, rk].iter().any(|k| matches!(k, Kind::Date | Kind::Moment | Kind::Time | Kind::Span)) {
8665            return Err(format!("`{cop}` over {lk:?} and {rk:?} is not yet lowered"));
8666        }
8667        // A Text operand makes `+` string concatenation — the VM emits a plain `Add` whenever it cannot
8668        // prove both sides are Text (a `format(…)` result, a parameter) — exactly as `Concat` lowers it, a word
8669        // rendered unsigned as `Show` gives it. Any other arithmetic on Text is refused, never computed on the
8670        // pointers.
8671        if lk == Kind::Text || rk == Kind::Text {
8672            if cop != "+" {
8673                return Err(format!("`{cop}` on a Text operand"));
8674            }
8675            let (l, r) = (self.text_of(lhs, lk)?, self.text_of(rhs, rk)?);
8676            self.line(&format!(
8677                "r[{dst}] = (int64_t)(intptr_t)str_concat({l}, {r});"
8678            ));
8679            return Ok(());
8680        }
8681        if matches!(lk, Kind::Word(_)) || matches!(rk, Kind::Word(_)) {
8682            return self.word_arith(dst, lhs, rhs, cop, lk, rk);
8683        }
8684        // A rational is the VM's exact number: `+ - *` exact, `/` truncating two whole values and exact otherwise, each
8685        // result boxed; the bitwise operators take two whole values as their integers. A Real operand makes the
8686        // operation a real one, as the VM's.
8687        if lk == Kind::Rational || rk == Kind::Rational {
8688            if lk == Kind::Float || rk == Kind::Float {
8689                let (l, r) = (self.real_of(lhs, lk), self.real_of(rhs, rk));
8690                if cop == "/" {
8691                    self.line(&format!("r[{dst}] = as_bits(logos_rdiv({l}, {r}));"));
8692                } else if matches!(cop, "+" | "-" | "*") {
8693                    self.line(&format!("r[{dst}] = as_bits({l} {cop} {r});"));
8694                } else {
8695                    return Err(format!("`{cop}` over {lk:?} and {rk:?} is not yet lowered"));
8696                }
8697                return Ok(());
8698            }
8699            let (l, r) = (as_ldec(lhs, lk)?, as_ldec(rhs, rk)?);
8700            if matches!(cop, "&" | "|" | "^") {
8701                self.line(&format!("r[{dst}] = ldec_bitwise({l}, {r}, '{cop}');"));
8702                return Ok(());
8703            }
8704            let f = match cop {
8705                "+" => "ldec_add",
8706                "-" => "ldec_sub",
8707                "*" => "ldec_mul",
8708                "/" => "ldec_quotient",
8709                _ => return Err(format!("`{cop}` over {lk:?} and {rk:?} is not yet lowered")),
8710            };
8711            self.box_ldec(dst, &format!("{f}({l}, {r})"));
8712            return Ok(());
8713        }
8714        if cop == "/" && (lk == Kind::Float || rk == Kind::Float) {
8715            self.line(&format!(
8716                "r[{dst}] = as_bits(logos_rdiv({}, {}));",
8717                as_double(lhs, lk),
8718                as_double(rhs, rk)
8719            ));
8720        } else if lk == Kind::Float || rk == Kind::Float {
8721            self.line(&format!(
8722                "r[{dst}] = as_bits({} {cop} {});",
8723                as_double(lhs, lk),
8724                as_double(rhs, rk)
8725            ));
8726        } else if cop == "/" {
8727            self.line(&format!("r[{dst}] = logos_idiv(r[{lhs}], r[{rhs}]);"));
8728        } else {
8729            self.line(&format!("r[{dst}] = r[{lhs}] {cop} r[{rhs}];"));
8730        }
8731        Ok(())
8732    }
8733
8734    /// A `char*` rendering register `src` (kind `sk`) under interpolation format spec `spec`, mirroring the AST
8735    /// backend's `c_format_spec_for_hole` byte-for-byte — `$` money (`$%.2f`), `.N` precision, `<`/`>`/`^`
8736    /// alignment to a width, and a bare `N` right-aligned width — reusing the runtime's `logos_dyn_sprintf` /
8737    /// `logos_center_*`. A spec/kind combination neither models is a clean refusal.
8738    fn format_spec_body(&self, src: u16, sk: Kind, spec: &str) -> Result<String, String> {
8739        if spec == "$" {
8740            return Ok(format!(
8741                "logos_dyn_sprintf(\"$%.2f\", {})",
8742                as_double(src, sk)
8743            ));
8744        }
8745        if let Some(rest) = spec.strip_prefix('.') {
8746            let prec: usize = rest
8747                .parse()
8748                .map_err(|_| format!("bad precision spec `{spec}`"))?;
8749            return Ok(format!(
8750                "logos_dyn_sprintf(\"%.{prec}f\", {})",
8751                as_double(src, sk)
8752            ));
8753        }
8754        if let Some(align) = spec.chars().next().filter(|c| matches!(c, '<' | '>' | '^')) {
8755            let width: usize = spec[1..]
8756                .parse()
8757                .map_err(|_| format!("bad width in spec `{spec}`"))?;
8758            if align == '^' {
8759                return match sk {
8760                    Kind::Int => Ok(format!("logos_center_i64(r[{src}], {width})")),
8761                    Kind::Text => Ok(format!("logos_center_str({}, {width})", as_cstr(src))),
8762                    Kind::Word(_) => Ok(format!(
8763                        "logos_center_str(logos_dyn_sprintf(\"%\" PRIu64, (uint64_t)r[{src}]), {width})"
8764                    )),
8765                    _ => Err(format!("center-format of kind {sk:?} unsupported")),
8766                };
8767            }
8768            let flag = if align == '<' { "-" } else { "" };
8769            return match sk {
8770                Kind::Int => Ok(format!(
8771                    "logos_dyn_sprintf(\"%{flag}{width}\" PRId64, r[{src}])"
8772                )),
8773                Kind::Text => Ok(format!(
8774                    "logos_dyn_sprintf(\"%{flag}{width}s\", {})",
8775                    as_cstr(src)
8776                )),
8777                Kind::Float => Ok(format!(
8778                    "logos_dyn_sprintf(\"%{flag}{width}g\", as_f64(r[{src}]))"
8779                )),
8780                Kind::Word(_) => Ok(format!(
8781                    "logos_dyn_sprintf(\"%{flag}{width}\" PRIu64, (uint64_t)r[{src}])"
8782                )),
8783                _ => Err(format!("aligned-format of kind {sk:?} unsupported")),
8784            };
8785        }
8786        if let Ok(width) = spec.parse::<usize>() {
8787            return match sk {
8788                Kind::Int => Ok(format!("logos_dyn_sprintf(\"%{width}\" PRId64, r[{src}])")),
8789                Kind::Text => Ok(format!(
8790                    "logos_dyn_sprintf(\"%{width}s\", {})",
8791                    as_cstr(src)
8792                )),
8793                Kind::Float => Ok(format!(
8794                    "logos_dyn_sprintf(\"%{width}g\", as_f64(r[{src}]))"
8795                )),
8796                Kind::Word(_) => Ok(format!(
8797                    "logos_dyn_sprintf(\"%{width}\" PRIu64, (uint64_t)r[{src}])"
8798                )),
8799                _ => Err(format!("width-format of kind {sk:?} unsupported")),
8800            };
8801        }
8802        Err(format!("unsupported interpolation format spec `{spec}`"))
8803    }
8804
8805    /// `r[dst] = (<lhs> <cop> <rhs>)` (a Bool result): float compare when either operand is float.
8806    fn cmp(&mut self, dst: u16, lhs: u16, rhs: u16, cop: &str, kinds: &[Kind]) -> Result<(), String> {
8807        let (lk, rk) = (kind_at(kinds, lhs), kind_at(kinds, rhs));
8808        // Two time values of one kind order by the number their slots hold; a span has no order, and a time value
8809        // beside anything else is the runtime's to compare.
8810        let time = |k: Kind| matches!(k, Kind::Date | Kind::Moment | Kind::Time | Kind::Span);
8811        if (time(lk) || time(rk)) && (lk != rk || lk == Kind::Span) {
8812            return Err(format!("`{cop}` over {lk:?} and {rk:?} is not yet lowered"));
8813        }
8814        if let (Kind::Word(a), Kind::Word(b)) = (lk, rk) {
8815            if a == b {
8816                self.line(&format!(
8817                    "r[{dst}] = ((uint64_t)r[{lhs}] {cop} (uint64_t)r[{rhs}]);"
8818                ));
8819                return Ok(());
8820            }
8821        }
8822        // A word orders only against a word of its width: any other pairing fails in the VM's words.
8823        if matches!(lk, Kind::Word(_)) || matches!(rk, Kind::Word(_)) {
8824            if let (Some(l), Some(r)) = (sample_value(lk), sample_value(rk)) {
8825                use crate::ast::stmt::BinaryOpKind;
8826                let op = match cop {
8827                    "<" => BinaryOpKind::Lt,
8828                    ">" => BinaryOpKind::Gt,
8829                    "<=" => BinaryOpKind::LtEq,
8830                    _ => BinaryOpKind::GtEq,
8831                };
8832                if let Err(sentence) = crate::semantics::arith::binary_op(op, l, r) {
8833                    self.line(&format!("logos_fatalf({});", words::literal(&sentence)));
8834                    return Ok(());
8835                }
8836            }
8837        }
8838        // A rational orders exactly against any exact number, and against a real by the real's exact value; a NaN
8839        // orders against nothing and equals nothing.
8840        if lk == Kind::Rational || rk == Kind::Rational {
8841            if lk == Kind::Float || rk == Kind::Float {
8842                let (x, f, flip) = if lk == Kind::Rational {
8843                    (as_ldec(lhs, lk)?, as_double(rhs, rk), "")
8844                } else {
8845                    (as_ldec(rhs, rk)?, as_double(lhs, lk), "-")
8846                };
8847                let n = self.fresh();
8848                let holds = if cop == "!=" {
8849                    format!("_c{n} == 2 || _c{n} != 0")
8850                } else {
8851                    format!("_c{n} != 2 && ({flip}_c{n}) {cop} 0")
8852                };
8853                self.line(&format!("{{ int _c{n} = ldec_cmp_f64({x}, {f}); r[{dst}] = ({holds}); }}"));
8854            } else {
8855                let (l, r) = (as_ldec(lhs, lk)?, as_ldec(rhs, rk)?);
8856                self.line(&format!("r[{dst}] = (ldec_cmp({l}, {r}) {cop} 0);"));
8857            }
8858            return Ok(());
8859        }
8860        if (lk == Kind::BigInt || rk == Kind::BigInt)
8861            && !matches!(lk, Kind::Float | Kind::Rational)
8862            && !matches!(rk, Kind::Float | Kind::Rational)
8863        {
8864            // `lc_xcmp` orders two exact integers as -1/0/1, so `<cmp> 0` realizes all six relations.
8865            let tagged = |reg: u16, k: Kind| {
8866                if k == Kind::BigInt {
8867                    format!("r[{reg}]")
8868                } else {
8869                    format!("lc_xof(r[{reg}])")
8870                }
8871            };
8872            self.line(&format!(
8873                "r[{dst}] = (lc_xcmp({}, {}) {cop} 0);",
8874                tagged(lhs, lk),
8875                tagged(rhs, rk)
8876            ));
8877        } else if lk == Kind::Float || rk == Kind::Float {
8878            self.line(&format!(
8879                "r[{dst}] = ({} {cop} {});",
8880                as_double(lhs, lk),
8881                as_double(rhs, rk)
8882            ));
8883        } else {
8884            self.line(&format!("r[{dst}] = (r[{lhs}] {cop} r[{rhs}]);"));
8885        }
8886        Ok(())
8887    }
8888}
8889
8890/// Every undeclared enum type's constructors, in the tag order every site agrees on: `Option` is `Some, None`,
8891/// `Result` is `Ok, Err` (the order the native `Tcp` results are boxed in), and any other type not in
8892/// `prog.enum_types` lists its constructors as the program first builds them.
8893fn ctor_tags(prog: &CompiledProgram) -> std::collections::HashMap<String, Vec<String>> {
8894    let mut tags: std::collections::HashMap<String, Vec<String>> = std::collections::HashMap::new();
8895    tags.insert(
8896        "Option".to_string(),
8897        vec!["Some".to_string(), "None".to_string()],
8898    );
8899    tags.insert(
8900        "Result".to_string(),
8901        vec!["Ok".to_string(), "Err".to_string()],
8902    );
8903    let text = |idx: u32| match prog.constants.get(idx as usize) {
8904        Some(Constant::Text(t)) => Some(t.clone()),
8905        _ => None,
8906    };
8907    for op in &prog.code {
8908        if let Op::NewInductive {
8909            type_name, ctor, ..
8910        } = op
8911        {
8912            let (Some(ty), Some(c)) = (text(*type_name), text(*ctor)) else {
8913                continue;
8914            };
8915            if prog.enum_types.iter().any(|e| e.name == ty) {
8916                continue;
8917            }
8918            let ctors = tags.entry(ty).or_default();
8919            if !ctors.contains(&c) {
8920                ctors.push(c);
8921            }
8922        }
8923    }
8924    tags
8925}
8926
8927/// How an op changes a register's collection in place.
8928#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8929enum Mutation {
8930    /// Value semantics: the register's own collection changes, never another holder's — the walker copies it
8931    /// first unless the register's owned flag says the register is its only holder.
8932    Owned(u16),
8933    /// Shared semantics: the change reaches every holder — a CRDT's interior, which aliases share by design.
8934    Shared,
8935}
8936
8937/// Whether the VM refuses `op` for the kinds its operands hold in `kinds`, whatever their values: words of two
8938/// widths or a word beside a number (as the VM's arithmetic answers), the remainder of a Real, `is approximately`
8939/// over anything but numbers.
8940fn refused_for_kinds(op: &Op, kinds: &[Kind]) -> bool {
8941    use crate::ast::stmt::BinaryOpKind;
8942    // A word beside another kind is the VM's own arithmetic's to judge: words of two widths and a word beside a
8943    // number are refused, a word joined to a Text is the text. A divisor is handed as a nonzero value, so the
8944    // answer is about the kinds alone.
8945    let words_clash = |bop: BinaryOpKind, a: Kind, b: Kind| {
8946        if !matches!(a, Kind::Word(_)) && !matches!(b, Kind::Word(_)) {
8947            return false;
8948        }
8949        match (sample_value(a), sample_divisor(b)) {
8950            (Some(x), Some(y)) => crate::semantics::arith::binary_op(bop, x, y).is_err(),
8951            _ => false,
8952        }
8953    };
8954    let pair = |lhs: u16, rhs: u16| (kind_at(kinds, lhs), kind_at(kinds, rhs));
8955    match *op {
8956        Op::Add { lhs, rhs, .. } => {
8957            let (l, r) = pair(lhs, rhs);
8958            words_clash(BinaryOpKind::Add, l, r)
8959        }
8960        Op::Sub { lhs, rhs, .. } => {
8961            let (l, r) = pair(lhs, rhs);
8962            words_clash(BinaryOpKind::Subtract, l, r)
8963        }
8964        Op::Mul { lhs, rhs, .. } => {
8965            let (l, r) = pair(lhs, rhs);
8966            words_clash(BinaryOpKind::Multiply, l, r)
8967        }
8968        Op::Div { lhs, rhs, .. } => {
8969            let (l, r) = pair(lhs, rhs);
8970            words_clash(BinaryOpKind::Divide, l, r)
8971        }
8972        Op::BitAnd { lhs, rhs, .. } => {
8973            let (l, r) = pair(lhs, rhs);
8974            words_clash(BinaryOpKind::BitAnd, l, r)
8975        }
8976        Op::BitOr { lhs, rhs, .. } => {
8977            let (l, r) = pair(lhs, rhs);
8978            words_clash(BinaryOpKind::BitOr, l, r)
8979        }
8980        Op::BitXor { lhs, rhs, .. } => {
8981            let (l, r) = pair(lhs, rhs);
8982            words_clash(BinaryOpKind::BitXor, l, r)
8983        }
8984        Op::Mod { lhs, rhs, .. } => {
8985            let (l, r) = pair(lhs, rhs);
8986            words_clash(BinaryOpKind::Modulo, l, r) || l == Kind::Float || r == Kind::Float
8987        }
8988        Op::ApproxEq { lhs, rhs, .. } => {
8989            let numeric = |k: Kind| matches!(k, Kind::Int | Kind::Float | Kind::Unknown);
8990            !numeric(kind_at(kinds, lhs)) || !numeric(kind_at(kinds, rhs))
8991        }
8992        _ => false,
8993    }
8994}
8995
8996/// The class of every op that changes a register's collection in place — the ISA's own list
8997/// (`vm::peephole::mutates`), each decided between value and shared semantics; `None` for every other op.
8998fn mutation(op: &Op) -> Option<Mutation> {
8999    match *op {
9000        Op::ListPush { list, .. } | Op::ListPop { list, .. } => Some(Mutation::Owned(list)),
9001        Op::SetIndex { collection, .. }
9002        | Op::SetIndexUnchecked { collection, .. }
9003        | Op::RemoveFrom { collection, .. } => Some(Mutation::Owned(collection)),
9004        Op::SetAdd { set, .. } => Some(Mutation::Owned(set)),
9005        Op::StructInsert { obj, .. } | Op::ListPushField { obj, .. } => Some(Mutation::Owned(obj)),
9006        Op::EnsureOwned { reg } => Some(Mutation::Owned(reg)),
9007        Op::CrdtBump { .. }
9008        | Op::CrdtMerge { .. }
9009        | Op::CrdtJoin { .. }
9010        | Op::CrdtAppend { .. }
9011        | Op::CrdtResolve { .. } => Some(Mutation::Shared),
9012        _ => None,
9013    }
9014}
9015
9016/// The registers `op` reads without leaving their handles anywhere: a measure, a lookup, a comparison, a
9017/// rendering, a copy made from them.
9018fn inspected_regs(op: &Op) -> Vec<u16> {
9019    match *op {
9020        Op::RegionBoundsGuard {
9021            array, bound, iv, ..
9022        } => vec![array, bound, iv],
9023        Op::Length { collection, .. } => vec![collection],
9024        Op::Index {
9025            collection, index, ..
9026        }
9027        | Op::IndexUnchecked {
9028            collection, index, ..
9029        } => vec![collection, index],
9030        Op::Contains {
9031            collection, value, ..
9032        } => vec![collection, value],
9033        Op::Show { src }
9034        | Op::FormatValue { src, .. }
9035        | Op::Not { src, .. }
9036        | Op::DeepClone { src, .. } => vec![src],
9037        Op::Concat { lhs, rhs, .. }
9038        | Op::Eq { lhs, rhs, .. }
9039        | Op::NotEq { lhs, rhs, .. }
9040        | Op::Lt { lhs, rhs, .. }
9041        | Op::Gt { lhs, rhs, .. }
9042        | Op::LtEq { lhs, rhs, .. }
9043        | Op::GtEq { lhs, rhs, .. }
9044        | Op::ApproxEq { lhs, rhs, .. }
9045        | Op::SeqConcat { lhs, rhs, .. }
9046        | Op::UnionOp { lhs, rhs, .. }
9047        | Op::IntersectOp { lhs, rhs, .. } => vec![lhs, rhs],
9048        Op::SliceOp {
9049            collection,
9050            start,
9051            end,
9052            ..
9053        } => vec![collection, start, end],
9054        Op::GetField { obj, .. } => vec![obj],
9055        Op::TestArm { target, .. } | Op::BindArm { target, .. } => vec![target],
9056        Op::JumpIfFalse { cond, .. } | Op::JumpIfTrue { cond, .. } => vec![cond],
9057        Op::AddAssign { src, .. } => vec![src],
9058        Op::CheckPolicy {
9059            subject, object, ..
9060        } => vec![subject, object],
9061        Op::NewEmptyListWithCapacity { capacity, .. }
9062        | Op::NewEmptyMapWithCapacity { capacity, .. } => vec![capacity],
9063        _ => Vec::new(),
9064    }
9065}
9066
9067/// Whether a register of kind `k` holds a value with no heap handle an owned flag could ride.
9068fn carries_no_handle(k: Kind) -> bool {
9069    matches!(
9070        k,
9071        Kind::Int
9072            | Kind::Bool
9073            | Kind::Float
9074            | Kind::BigInt
9075            | Kind::Word(_)
9076            | Kind::Nothing
9077            | Kind::Never
9078    )
9079}
9080
9081/// The registers `op` writes with a collection it just made, which nothing else holds.
9082fn fresh_regs(op: &Op, after: &[Kind]) -> Vec<u16> {
9083    use crate::semantics::builtins::BuiltinId;
9084    match *op {
9085        Op::NewList { dst, .. }
9086        | Op::NewEmptyList { dst }
9087        | Op::NewEmptyListWithCapacity { dst, .. }
9088        | Op::NewEmptyListFixed { dst, .. }
9089        | Op::NewEmptyListI32 { dst }
9090        | Op::NewEmptySet { dst }
9091        | Op::NewEmptyMap { dst }
9092        | Op::NewEmptyMapWithCapacity { dst, .. }
9093        | Op::NewRange { dst, .. }
9094        | Op::SeqConcat { dst, .. }
9095        | Op::SliceOp { dst, .. }
9096        | Op::UnionOp { dst, .. }
9097        | Op::IntersectOp { dst, .. }
9098        | Op::NewStruct { dst, .. }
9099        | Op::Args { dst } => vec![dst],
9100        Op::DeepClone { dst, .. } if !matches!(kind_at(after, dst), Kind::CrdtCounter) => vec![dst],
9101        Op::CallBuiltin {
9102            dst,
9103            builtin: BuiltinId::TextBytes,
9104            ..
9105        } => vec![dst],
9106        _ => Vec::new(),
9107    }
9108}
9109
9110fn jump_target(op: &Op) -> Option<usize> {
9111    match op {
9112        Op::Jump { target } | Op::JumpIfFalse { target, .. } | Op::JumpIfTrue { target, .. } => {
9113            Some(*target)
9114        }
9115        // A `for-each` loop's exhaustion edge: `IterNext` falls through to `L{exit}` when the iterator is empty.
9116        Op::IterNext { exit, .. } => Some(*exit),
9117        _ => None,
9118    }
9119}
9120
9121/// The VM's refusal when `op` reads exact register `reg` as the index of a list or a Text and it is past a machine
9122/// word; `None` where it reads `reg` some other way, whose narrowing fails as any place holding a machine word does.
9123fn index_too_wide(
9124    op: &Op,
9125    kinds: &[Kind],
9126    reg: u16,
9127) -> Option<crate::semantics::failcode::FailCode> {
9128    use crate::semantics::failcode::FailCode;
9129    match *op {
9130        Op::Index {
9131            collection, index, ..
9132        }
9133        | Op::IndexUnchecked {
9134            collection, index, ..
9135        } if index == reg => match kind_at(kinds, collection) {
9136            Kind::Text => Some(FailCode::TextIndexTooWide),
9137            k if is_sequence(k) => Some(FailCode::ListIndexTooWide),
9138            _ => None,
9139        },
9140        Op::SetIndex {
9141            collection, index, ..
9142        }
9143        | Op::SetIndexUnchecked {
9144            collection, index, ..
9145        } if index == reg && is_sequence(kind_at(kinds, collection)) => {
9146            Some(FailCode::ListStoreIndexTooWide)
9147        }
9148        _ => None,
9149    }
9150}
9151
9152/// A map's value is stored as an exact integer, independently of its key.
9153fn reads_exact_reg(op: &Op, kinds: &[Kind], reg: u16) -> bool {
9154    reads_exact(op, kinds)
9155        || matches!(op, Op::CrdtBump { amount, .. } if *amount == reg)
9156        || matches!(op, Op::StructInsert { value, .. } | Op::CrdtResolve { value, .. } if *value == reg)
9157        || matches!(op,
9158            Op::SetIndex { collection, value, .. }
9159            | Op::SetIndexUnchecked { collection, value, .. }
9160            if *value == reg && matches!(kind_at(kinds, *collection), Kind::Map(_) | Kind::MapUnknown | Kind::Struct(_) | Kind::StructSynth(_))
9161        )
9162}
9163
9164/// Whether `op`'s lowering reads an exact-integer operand as one (a tagged word) — for these kinds of its operands.
9165fn reads_exact(op: &Op, kinds: &[Kind]) -> bool {
9166    match op {
9167        Op::Shl { lhs, .. } | Op::Shr { lhs, .. } => !matches!(kind_at(kinds, *lhs), Kind::Word(_)),
9168        Op::FormatValue { spec, .. } => *spec == u32::MAX,
9169        Op::Move { .. }
9170        | Op::DeepClone { .. }
9171        | Op::Add { .. }
9172        | Op::Sub { .. }
9173        | Op::Mul { .. }
9174        | Op::Pow { .. }
9175        | Op::Div { .. }
9176        | Op::FloorDiv { .. }
9177        | Op::Mod { .. }
9178        | Op::DivPow2 { .. }
9179        | Op::MagicDivU { .. }
9180        | Op::AddAssign { .. }
9181        | Op::BitXor { .. }
9182        | Op::BitAnd { .. }
9183        | Op::BitOr { .. }
9184        | Op::Lt { .. }
9185        | Op::Gt { .. }
9186        | Op::LtEq { .. }
9187        | Op::GtEq { .. }
9188        | Op::Eq { .. }
9189        | Op::NotEq { .. }
9190        | Op::Show { .. }
9191        | Op::Concat { .. }
9192        | Op::Return { .. }
9193        | Op::Call { .. }
9194        | Op::EnsureOwned { .. }
9195        | Op::GlobalSet { .. }
9196        | Op::RevSwap { .. } => true,
9197        // A word built from an exact integer takes its low bits, however wide it is.
9198        Op::CallBuiltin {
9199            builtin:
9200                crate::semantics::builtins::BuiltinId::Word8Make
9201                | crate::semantics::builtins::BuiltinId::Word16Make
9202                | crate::semantics::builtins::BuiltinId::Word32
9203                | crate::semantics::builtins::BuiltinId::Word64,
9204            ..
9205        } => true,
9206        _ => false,
9207    }
9208}
9209
9210/// The operands `x` for which `c * x` is within i64, as the closed range `(lo, hi)` (the whole range when `c` is zero).
9211fn product_domain(c: i128) -> (i128, i128) {
9212    let (min, max) = (i64::MIN as i128, i64::MAX as i128);
9213    let floor_div = |n: i128, d: i128| {
9214        let q = n / d;
9215        if n % d != 0 && ((n < 0) != (d < 0)) {
9216            q - 1
9217        } else {
9218            q
9219        }
9220    };
9221    let ceil_div = |n: i128, d: i128| -floor_div(-n, d);
9222    let (lo, hi) = match c {
9223        0 => (min, max),
9224        c if c > 0 => (ceil_div(min, c), floor_div(max, c)),
9225        c => (ceil_div(max, c), floor_div(min, c)),
9226    };
9227    (lo.max(min), hi.min(max))
9228}
9229
9230/// Whether `op` can make an integer past i64 out of machine words: a sum, difference, product, power or quotient.
9231fn overflows(op: &Op) -> bool {
9232    matches!(
9233        op,
9234        Op::Add { .. }
9235            | Op::Sub { .. }
9236            | Op::Mul { .. }
9237            | Op::AddAssign { .. }
9238            | Op::Pow { .. }
9239            | Op::Div { .. }
9240            | Op::FloorDiv { .. }
9241    )
9242}
9243
9244/// Whether `op`'s lowering writes its result tagged when the result is exact (it reads the result's kind).
9245fn makes_exact(op: &Op) -> bool {
9246    matches!(
9247        op,
9248        Op::Add { .. }
9249            | Op::Sub { .. }
9250            | Op::Mul { .. }
9251            | Op::Pow { .. }
9252            | Op::Div { .. }
9253            | Op::FloorDiv { .. }
9254            | Op::Mod { .. }
9255            | Op::DivPow2 { .. }
9256            | Op::MagicDivU { .. }
9257            | Op::AddAssign { .. }
9258    )
9259}
9260
9261/// A single-replica CRDT-lattice op (needs the `C_CRDT_RUNTIME` counter map + `logos_crdt_*` helpers).
9262fn op_is_crdt(op: &Op) -> bool {
9263    matches!(
9264        op,
9265        Op::NewCrdt { .. }
9266            | Op::CrdtBump { .. }
9267            | Op::CrdtMerge { .. }
9268            | Op::CrdtJoin { .. }
9269            | Op::CrdtResolve { .. }
9270            | Op::CrdtAppend { .. }
9271    )
9272}
9273
9274/// A file statement's builtin (needs the `c_vfs_runtime` sandboxed file system + `logos_vfs_*` helpers).
9275fn op_is_file(op: &Op) -> bool {
9276    use crate::semantics::builtins::BuiltinId;
9277    matches!(
9278        op,
9279        Op::CallBuiltin {
9280            builtin: BuiltinId::VfsReadFile | BuiltinId::VfsWriteFile | BuiltinId::VfsMount,
9281            ..
9282        }
9283    )
9284}
9285
9286/// An offline-networking op (needs the `c_net_runtime` loopback transport + `logos_net_*` helpers).
9287fn op_is_net(op: &Op) -> bool {
9288    matches!(
9289        op,
9290        Op::NetConnect { .. }
9291            | Op::NetListen { .. }
9292            | Op::NetMakePeer { .. }
9293            | Op::NetSend { .. }
9294            | Op::NetStream { .. }
9295            | Op::NetAwait { .. }
9296            | Op::NetSync { .. }
9297    )
9298}
9299
9300/// A real-socket TCP-client op (needs the `C_TCP_RUNTIME` POSIX socket runtime + `logos_tcp_*` helpers).
9301fn op_is_tcp(op: &Op) -> bool {
9302    matches!(op, Op::Tcp { .. })
9303}
9304
9305#[cfg(test)]
9306#[path = "../../../logicaffeine_tests/unit/logicaffeine_compile/codegen_c/from_program/tests.rs"]
9307mod tests;