Skip to main content

logicaffeine_forge/
jit.rs

1//! J1: the straight-line micro-op compiler — bytecode in, native code out.
2//!
3//! [`MicroOp`] is the JIT's input IR: the integer subset of the bytecode VM's
4//! register operations. [`compile_straightline`] lowers each op to a fixed
5//! stencil micro-sequence over the frame/operand-stack machine model and glues
6//! the result into one executable chain:
7//!
8//! ```text
9//! LoadConst{dst,v}  →  const(v); slot_set(dst)
10//! Move{dst,src}     →  slot_get(src); slot_set(dst)
11//! Add{dst,l,r}      →  slot_get(l); slot_get(r); addi; slot_set(dst)
12//! …                    (Sub/Mul/Lt/Eq identical shape)
13//! Gt{dst,l,r}       →  slot_get(r); slot_get(l); lti; slot_set(dst)   (swap)
14//! Return{src}       →  slot_get(src); return
15//! ```
16//!
17//! Anything outside this subset is the caller's tier-up bail (`None` from the
18//! adapter) — the VM keeps running it.
19
20use std::sync::atomic::{AtomicI64, Ordering};
21
22use crate::buffer::{HoleValue, JitBuffer, JitChain};
23use crate::{
24    V_BINOP, V_BRANCH, V_CONST, V_FBINOP, V_FMOV, V_MOV, V_RET, V_SQRTF, V_DIVF, V_I2F, V_BRANCHF,
25    V_ARRLD_RPTR, V_ARRLD_RPTR_C, V_ARRST_RPTR, V_ARRST_RPTR_C,
26    ST_ADD3, ST_ADDF3, ST_AND3, ST_ARRLD, ST_ARRLD2_ADDF, ST_ARRLD2_MULF, ST_ARRLD2_SUBF, ST_ARRLDB, ST_ARRLDB_U, ST_ARRLD_U, ST_ARRST, ST_ARRSTB, ST_ARRSTB_U, ST_ARRST_U, ST_BREQ, ST_BREQF, ST_BRLEF, ST_BRLT, ST_CALL, ST_PUSH, ST_LIST_CLEAR,
27    ST_ARRLD_I32, ST_ARRLD_I32_U, ST_ARRST_I32, ST_ARRST_I32_U,
28    ST_ARRLD2_ADD, ST_ARRLD2_ADD_U, ST_ARRLD2_SUB, ST_ARRLD2_SUB_U, ST_ARRLD2_MUL, ST_ARRLD2_MUL_U,
29    ST_ARRLDAFF_NONE, ST_ARRLDAFF_NONE_U, ST_ARRLDAFF_ADD, ST_ARRLDAFF_ADD_U,
30    ST_ARRLDAFF_SUB, ST_ARRLDAFF_SUB_U, ST_ARRLDAFF_MUL, ST_ARRLDAFF_MUL_U,
31    ST_ARRRMW_ADD, ST_ARRRMW_ADD_U, ST_ARRRMW_SUB, ST_ARRRMW_SUB_U, ST_ARRRMW_MUL, ST_ARRRMW_MUL_U,
32    ST_ARRRMW_AND, ST_ARRRMW_AND_U, ST_ARRRMW_OR, ST_ARRRMW_OR_U, ST_ARRRMW_XOR, ST_ARRRMW_XOR_U,
33    ST_ARRRMW_ADDF, ST_ARRRMW_ADDF_U, ST_ARRRMW_SUBF, ST_ARRRMW_SUBF_U, ST_ARRRMW_MULF, ST_ARRRMW_MULF_U,
34    ST_FMAF,
35    ST_ARRCONDSWAP_GT, ST_ARRCONDSWAP_GT_U, ST_ARRCONDSWAP_LT, ST_ARRCONDSWAP_LT_U,
36    ST_ARRCONDSWAP_GE, ST_ARRCONDSWAP_GE_U, ST_ARRCONDSWAP_LE, ST_ARRCONDSWAP_LE_U,
37    ST_ARRSWAP, ST_ARRSWAP_U,
38    ST_BRLTF, ST_BRZ, ST_CALL_PRECISE, ST_CONSTST, ST_DEOPT, ST_DEOPT_AT, ST_DIV3C,
39    ST_DIVPOW2, ST_MAGICDIV,
40    ST_DIVF3C, ST_EQ3, ST_EQF3, ST_I2F2,
41    ST_JUMP, ST_LE3, ST_LEF3, ST_LT3, ST_LTF3, ST_MOD3C, ST_MOVSS, ST_MUL3, ST_MULF3, ST_NE3,
42    ST_ALLOCLIST, ST_CALL_SELF, ST_CALL_SELF_COPY, ST_LISTTRIPLE, ST_MAPHGET, ST_MAPHHAS, ST_MAPHSET, ST_MEMMEM, ST_NEF3,
43    ST_NOTB2,
44    ST_NOTI2, ST_OR3, ST_RET2,
45    ST_SHL3, ST_SHR3, ST_SQRTF2, ST_SUB3,
46    ST_SUBF3, ST_XOR3,
47};
48
49/// Frame slot index (a VM register number).
50pub type Slot = u16;
51
52/// The kernel's locked maximum LOGOS call depth, BAKED into the precise and
53/// self call stencils (their holes are all spoken for, so the limit cannot be
54/// a runtime hole). It mirrors `logicaffeine_compile::semantics::MAX_CALL_DEPTH`
55/// and the matching constant in `stencils/int_stencils.rs`; the callers in the
56/// JIT crate pass that same value, asserted here at stencil selection so a
57/// drift fails loudly instead of silently capping native recursion at the wrong
58/// depth (which would diverge from the kernel's depth-exceeded error).
59pub const BAKED_CALL_DEPTH: i64 = 2_500;
60
61/// Comparison kinds for [`MicroOp::Branch`].
62#[derive(Clone, Copy, Debug, PartialEq, Eq)]
63pub enum Cmp {
64    /// `lhs < rhs`
65    Lt,
66    /// `lhs > rhs`
67    Gt,
68    /// `lhs <= rhs`
69    LtEq,
70    /// `lhs >= rhs`
71    GtEq,
72    /// `lhs == rhs`
73    Eq,
74    /// `lhs != rhs`
75    NotEq,
76}
77
78impl Cmp {
79    /// Evaluate over two i64s.
80    pub fn eval(self, a: i64, b: i64) -> bool {
81        match self {
82            Cmp::Lt => a < b,
83            Cmp::Gt => a > b,
84            Cmp::LtEq => a <= b,
85            Cmp::GtEq => a >= b,
86            Cmp::Eq => a == b,
87            Cmp::NotEq => a != b,
88        }
89    }
90
91    /// The comparison with the opposite truth value.
92    pub fn negated(self) -> Cmp {
93        match self {
94            Cmp::Lt => Cmp::GtEq,
95            Cmp::Gt => Cmp::LtEq,
96            Cmp::LtEq => Cmp::Gt,
97            Cmp::GtEq => Cmp::Lt,
98            Cmp::Eq => Cmp::NotEq,
99            Cmp::NotEq => Cmp::Eq,
100        }
101    }
102}
103
104/// The float binary operation of a fused [`MicroOp::ArrLoad2F`].
105#[derive(Clone, Copy, Debug, PartialEq, Eq)]
106pub enum FOp {
107    /// IEEE addition.
108    Add,
109    /// IEEE subtraction.
110    Sub,
111    /// IEEE multiplication.
112    Mul,
113}
114
115impl FOp {
116    /// Apply over two f64s.
117    pub fn eval(self, a: f64, b: f64) -> f64 {
118        match self {
119            FOp::Add => a + b,
120            FOp::Sub => a - b,
121            FOp::Mul => a * b,
122        }
123    }
124}
125
126/// The integer binary operation of a fused two-buffer [`MicroOp::ArrLoad2`]:
127/// `frame[dst] = a[i-1] <op> b[j-1]`, the two elements loaded from two
128/// (possibly distinct) pinned 8-byte int buffers. Add/Mul commute; Sub does
129/// not (it only fuses `a[i] - b[j]`).
130#[derive(Clone, Copy, Debug, PartialEq, Eq)]
131pub enum IOp {
132    /// Wrapping i64 addition.
133    Add,
134    /// Wrapping i64 subtraction.
135    Sub,
136    /// Wrapping i64 multiplication.
137    Mul,
138}
139
140impl IOp {
141    /// Apply over two i64s with the kernel's wrapping semantics.
142    pub fn eval(self, a: i64, b: i64) -> i64 {
143        match self {
144            IOp::Add => a.wrapping_add(b),
145            IOp::Sub => a.wrapping_sub(b),
146            IOp::Mul => a.wrapping_mul(b),
147        }
148    }
149}
150
151/// The index-arithmetic shape folded into a fused [`MicroOp::ArrLoadAffine`].
152/// The computed 1-based index is `(frame[a] OP frame[b]).wrapping_add(c)` for a
153/// two-slot op, or `frame[a].wrapping_add(c)` for [`AffOp::None`] (a single slot
154/// plus a constant). The op wraps with the kernel's exact i64 semantics so the
155/// load is bit-identical to the un-fused index arithmetic + `ArrLoad`.
156#[derive(Clone, Copy, Debug, PartialEq, Eq)]
157pub enum AffOp {
158    /// No second slot: `idx = frame[a] + c` (the `w + 1` shape).
159    None,
160    /// `idx = (frame[a] + frame[b]) + c` (the `i*n + j + 1` tail).
161    Add,
162    /// `idx = (frame[a] - frame[b]) + c` (the `w - wi + 1` shape).
163    Sub,
164    /// `idx = (frame[a] * frame[b]) + c`.
165    Mul,
166}
167
168impl AffOp {
169    /// Compute the 1-based index with the kernel's wrapping i64 semantics.
170    pub fn eval(self, a: i64, b: i64, c: i64) -> i64 {
171        match self {
172            AffOp::None => a.wrapping_add(c),
173            AffOp::Add => a.wrapping_add(b).wrapping_add(c),
174            AffOp::Sub => a.wrapping_sub(b).wrapping_add(c),
175            AffOp::Mul => a.wrapping_mul(b).wrapping_add(c),
176        }
177    }
178}
179
180/// The operation of a fused read-modify-write [`MicroOp::ArrRMW`]:
181/// `buf[idx-1] = buf[idx-1] <op> operand`. The INT ops (Add/Sub/Mul wrap as the
182/// kernel's i64 arithmetic; And/Or/Xor bitwise) treat the element and operand as
183/// raw i64; the FLOAT ops (AddF/SubF/MulF) reinterpret both as f64 (the nbody
184/// velocity/position-update idiom). Sub/SubF are the only non-commutative
185/// members — the peephole fuses them only when the loaded element is the LEFT
186/// operand.
187#[derive(Clone, Copy, Debug, PartialEq, Eq)]
188pub enum RmwOp {
189    /// Wrapping addition.
190    Add,
191    /// Wrapping subtraction (`buf[i] - operand`).
192    Sub,
193    /// Wrapping multiplication.
194    Mul,
195    /// Bitwise AND.
196    And,
197    /// Bitwise OR.
198    Or,
199    /// Bitwise XOR.
200    Xor,
201    /// IEEE addition (operands reinterpreted as f64).
202    AddF,
203    /// IEEE subtraction (`buf[i] - operand`, as f64).
204    SubF,
205    /// IEEE multiplication (as f64).
206    MulF,
207}
208
209impl RmwOp {
210    /// Whether the op reinterprets its operands as f64.
211    pub fn is_float(self) -> bool {
212        matches!(self, RmwOp::AddF | RmwOp::SubF | RmwOp::MulF)
213    }
214
215    /// Apply over the raw i64 bits of the element and operand — the bit-exact
216    /// spec every stencil must match. Float ops reinterpret the bits as f64,
217    /// compute, and re-encode (copy-and-patch never reassociates, so ordering is
218    /// exact).
219    pub fn eval(self, a: i64, b: i64) -> i64 {
220        match self {
221            RmwOp::Add => a.wrapping_add(b),
222            RmwOp::Sub => a.wrapping_sub(b),
223            RmwOp::Mul => a.wrapping_mul(b),
224            RmwOp::And => a & b,
225            RmwOp::Or => a | b,
226            RmwOp::Xor => a ^ b,
227            RmwOp::AddF => (f64::from_bits(a as u64) + f64::from_bits(b as u64)).to_bits() as i64,
228            RmwOp::SubF => (f64::from_bits(a as u64) - f64::from_bits(b as u64)).to_bits() as i64,
229            RmwOp::MulF => (f64::from_bits(a as u64) * f64::from_bits(b as u64)).to_bits() as i64,
230        }
231    }
232}
233
234/// The source operand appended by a [`MicroOp::StrAppend`] — the right-hand side
235/// of a `Set text to text + <s>` in a pinned mutable-Text build loop.
236#[derive(Clone, Copy, Debug, PartialEq, Eq)]
237pub enum StrSrc {
238    /// A single ASCII byte VALUE living in a frame slot (the `Kind::TextByte`
239    /// lane: `text + ch` where `ch` is a 1-char ASCII text). The helper appends
240    /// `byte as char` — bit-identical to the VM concatenating the 1-char `Text`.
241    Byte(Slot),
242    /// A constant ASCII byte slice baked into the chain (`text + "XXXXX"`): a
243    /// `'static` pointer + length. The helper appends the whole slice — identical
244    /// to the VM's `Text + Text` concatenation of the literal.
245    Const {
246        /// `'static` pointer to the constant's bytes.
247        ptr: i64,
248        /// The constant's byte length.
249        len: i64,
250    },
251}
252
253/// The integer straight-line subset the J1 compiler accepts.
254#[derive(Clone, Copy, Debug, PartialEq, Eq)]
255pub enum MicroOp {
256    /// `frame[dst] = value`.
257    LoadConst {
258        /// Destination slot.
259        dst: Slot,
260        /// Immediate value.
261        value: i64,
262    },
263    /// `frame[dst] = frame[src]`.
264    Move {
265        /// Destination slot.
266        dst: Slot,
267        /// Source slot.
268        src: Slot,
269    },
270    /// `frame[dst] = frame[lhs] + frame[rhs]` (wrapping).
271    Add {
272        /// Destination slot.
273        dst: Slot,
274        /// Left operand slot.
275        lhs: Slot,
276        /// Right operand slot.
277        rhs: Slot,
278    },
279    /// `frame[dst] = frame[lhs] - frame[rhs]` (wrapping).
280    Sub {
281        /// Destination slot.
282        dst: Slot,
283        /// Left operand slot.
284        lhs: Slot,
285        /// Right operand slot.
286        rhs: Slot,
287    },
288    /// `frame[dst] = frame[lhs] * frame[rhs]` (wrapping).
289    Mul {
290        /// Destination slot.
291        dst: Slot,
292        /// Left operand slot.
293        lhs: Slot,
294        /// Right operand slot.
295        rhs: Slot,
296    },
297    /// `frame[dst] = (frame[lhs] < frame[rhs]) as i64`.
298    Lt {
299        /// Destination slot.
300        dst: Slot,
301        /// Left operand slot.
302        lhs: Slot,
303        /// Right operand slot.
304        rhs: Slot,
305    },
306    /// `frame[dst] = (frame[lhs] > frame[rhs]) as i64`.
307    Gt {
308        /// Destination slot.
309        dst: Slot,
310        /// Left operand slot.
311        lhs: Slot,
312        /// Right operand slot.
313        rhs: Slot,
314    },
315    /// `frame[dst] = (frame[lhs] == frame[rhs]) as i64`.
316    Eq {
317        /// Destination slot.
318        dst: Slot,
319        /// Left operand slot.
320        lhs: Slot,
321        /// Right operand slot.
322        rhs: Slot,
323    },
324    /// `frame[dst] = frame[lhs] / frame[rhs]` (wrapping; `MIN / -1 = MIN`).
325    /// A zero divisor SIDE-EXITS the chain ([`ChainOutcome::Deopt`]) before
326    /// any effect — the caller replays on bytecode, where the kernel raises
327    /// the exact "Division by zero" error.
328    Div {
329        /// Destination slot.
330        dst: Slot,
331        /// Left operand slot.
332        lhs: Slot,
333        /// Right operand slot.
334        rhs: Slot,
335    },
336    /// `frame[dst] = frame[lhs] / 2^k` (signed, round toward zero) via a
337    /// sign-correcting shift — bit-exact with [`MicroOp::Div`] but with NO
338    /// side-exit (a power-of-two divisor is never `0` or `-1`). Emitted in
339    /// place of `Div` when the divisor is a region-constant power of two and
340    /// the dividend is integer-typed.
341    DivPow2 {
342        /// Destination slot.
343        dst: Slot,
344        /// Dividend slot.
345        lhs: Slot,
346        /// Shift amount `k` (divisor = `2^k`, `1 <= k <= 62`).
347        k: u32,
348    },
349    /// `frame[dst] = frame[lhs] / c` (`mul_back == 0`) or `frame[lhs] % c`
350    /// (`mul_back == c`) by the Granlund–Montgomery / libdivide UNSIGNED
351    /// magic-reciprocal sequence (`mulhi(magic, x)` + add-fixup + shift, then
352    /// `x - q*c` for the remainder) — a `mul`+`shr` (~3 cycles) instead of
353    /// `idiv` (~25). NO side-exit (a literal `c > 0` is never `0` or `-1`).
354    /// Emitted in place of `Div`/`Mod` when the divisor is a compile-time
355    /// constant non-power-of-two and the dividend is proven Int and NON-NEGATIVE
356    /// (the unsigned magic equals the signed truncating result only there). The
357    /// `more` byte is the `logicaffeine_data::LogosDivU64` encoding (low 6 bits
358    /// = shift, `0x40` = the 65-bit add-marker path, `0x80` = pure-shift pow2).
359    MagicDivU {
360        /// Destination slot.
361        dst: Slot,
362        /// Dividend slot.
363        lhs: Slot,
364        /// Precomputed magic multiplier `M`.
365        magic: u64,
366        /// Shift / path encoding (see `logicaffeine_data::LogosDivU64`).
367        more: u8,
368        /// `0` selects the quotient; otherwise the divisor `c` selects the
369        /// remainder `x - (x/c)*c`.
370        mul_back: i64,
371    },
372    /// `frame[dst] = frame[lhs] % frame[rhs]` (wrapping; `MIN % -1 = 0`).
373    /// Zero divisor side-exits exactly like [`MicroOp::Div`].
374    Mod {
375        /// Destination slot.
376        dst: Slot,
377        /// Left operand slot.
378        lhs: Slot,
379        /// Right operand slot.
380        rhs: Slot,
381    },
382    /// `frame[dst] = (frame[lhs] <= frame[rhs]) as i64`.
383    LtEq {
384        /// Destination slot.
385        dst: Slot,
386        /// Left operand slot.
387        lhs: Slot,
388        /// Right operand slot.
389        rhs: Slot,
390    },
391    /// `frame[dst] = (frame[lhs] >= frame[rhs]) as i64`.
392    GtEq {
393        /// Destination slot.
394        dst: Slot,
395        /// Left operand slot.
396        lhs: Slot,
397        /// Right operand slot.
398        rhs: Slot,
399    },
400    /// `frame[dst] = (frame[lhs] != frame[rhs]) as i64`.
401    Neq {
402        /// Destination slot.
403        dst: Slot,
404        /// Left operand slot.
405        lhs: Slot,
406        /// Right operand slot.
407        rhs: Slot,
408    },
409    /// Fused compare-and-branch: transfer to `target` when `cmp(lhs, rhs)` is
410    /// FALSE (the `JumpIfFalse`-on-a-fresh-comparison shape); fall through
411    /// when TRUE. No comparison value is materialized — the adapter proves
412    /// the scratch dead before fusing.
413    Branch {
414        /// The comparison.
415        cmp: Cmp,
416        /// Left operand slot.
417        lhs: Slot,
418        /// Right operand slot.
419        rhs: Slot,
420        /// Micro-op index to transfer to when the comparison is FALSE.
421        target: usize,
422    },
423    /// `frame[dst] = frame[lhs] & frame[rhs]` (bitwise; logical for 0/1 Bools).
424    BitAnd {
425        /// Destination slot.
426        dst: Slot,
427        /// Left operand slot.
428        lhs: Slot,
429        /// Right operand slot.
430        rhs: Slot,
431    },
432    /// `frame[dst] = frame[lhs] | frame[rhs]` (bitwise; logical for 0/1 Bools).
433    BitOr {
434        /// Destination slot.
435        dst: Slot,
436        /// Left operand slot.
437        lhs: Slot,
438        /// Right operand slot.
439        rhs: Slot,
440    },
441    /// `frame[dst] = frame[lhs] ^ frame[rhs]`.
442    BitXor {
443        /// Destination slot.
444        dst: Slot,
445        /// Left operand slot.
446        lhs: Slot,
447        /// Right operand slot.
448        rhs: Slot,
449    },
450    /// `frame[dst] = frame[lhs].wrapping_shl(frame[rhs] as u32)` — the
451    /// kernel's locked shift spec (count truncates to u32, masks mod 64).
452    Shl {
453        /// Destination slot.
454        dst: Slot,
455        /// Left operand slot.
456        lhs: Slot,
457        /// Right operand slot.
458        rhs: Slot,
459    },
460    /// `frame[dst] = frame[lhs].wrapping_shr(frame[rhs] as u32)` (arithmetic).
461    Shr {
462        /// Destination slot.
463        dst: Slot,
464        /// Left operand slot.
465        lhs: Slot,
466        /// Right operand slot.
467        rhs: Slot,
468    },
469    /// `frame[dst] = !frame[src]` — bitwise NOT (the kernel's Int `not`).
470    NotInt {
471        /// Destination slot.
472        dst: Slot,
473        /// Source slot.
474        src: Slot,
475    },
476    /// `frame[dst] = frame[src] ^ 1` — logical NOT over 0/1 Bools.
477    NotBool {
478        /// Destination slot.
479        dst: Slot,
480        /// Source slot.
481        src: Slot,
482    },
483    /// Float ops: f64 values as raw bits in the i64 slots. Arithmetic is
484    /// IEEE; equality is the kernel's EPSILON rule.
485    AddF {
486        /// Destination slot.
487        dst: Slot,
488        /// Left operand slot.
489        lhs: Slot,
490        /// Right operand slot.
491        rhs: Slot,
492    },
493    /// `bits(f(lhs) - f(rhs))`.
494    SubF {
495        /// Destination slot.
496        dst: Slot,
497        /// Left operand slot.
498        lhs: Slot,
499        /// Right operand slot.
500        rhs: Slot,
501    },
502    /// `bits(f(lhs) * f(rhs))`.
503    MulF {
504        /// Destination slot.
505        dst: Slot,
506        /// Left operand slot.
507        lhs: Slot,
508        /// Right operand slot.
509        rhs: Slot,
510    },
511    /// `bits(f(lhs) / f(rhs))`; divisor `== 0.0` side-exits like [`MicroOp::Div`].
512    DivF {
513        /// Destination slot.
514        dst: Slot,
515        /// Left operand slot.
516        lhs: Slot,
517        /// Right operand slot.
518        rhs: Slot,
519    },
520    /// `(f(lhs) < f(rhs)) as i64` (IEEE, NaN → 0).
521    LtF {
522        /// Destination slot.
523        dst: Slot,
524        /// Left operand slot.
525        lhs: Slot,
526        /// Right operand slot.
527        rhs: Slot,
528    },
529    /// `(f(lhs) > f(rhs)) as i64`.
530    GtF {
531        /// Destination slot.
532        dst: Slot,
533        /// Left operand slot.
534        lhs: Slot,
535        /// Right operand slot.
536        rhs: Slot,
537    },
538    /// `(f(lhs) <= f(rhs)) as i64`.
539    LtEqF {
540        /// Destination slot.
541        dst: Slot,
542        /// Left operand slot.
543        lhs: Slot,
544        /// Right operand slot.
545        rhs: Slot,
546    },
547    /// `(f(lhs) >= f(rhs)) as i64`.
548    GtEqF {
549        /// Destination slot.
550        dst: Slot,
551        /// Left operand slot.
552        lhs: Slot,
553        /// Right operand slot.
554        rhs: Slot,
555    },
556    /// The kernel's epsilon equality as a value.
557    EqF {
558        /// Destination slot.
559        dst: Slot,
560        /// Left operand slot.
561        lhs: Slot,
562        /// Right operand slot.
563        rhs: Slot,
564    },
565    /// Negated epsilon equality as a value.
566    NeqF {
567        /// Destination slot.
568        dst: Slot,
569        /// Left operand slot.
570        lhs: Slot,
571        /// Right operand slot.
572        rhs: Slot,
573    },
574    /// Fused FLOAT compare-and-branch: transfer to `target` when
575    /// `cmp(f(lhs), f(rhs))` is FALSE — which, under IEEE, includes every
576    /// NaN-unordered comparison (matching the kernel's relations exactly).
577    BranchF {
578        /// The comparison.
579        cmp: Cmp,
580        /// Left operand slot.
581        lhs: Slot,
582        /// Right operand slot.
583        rhs: Slot,
584        /// Micro-op index to transfer to when the comparison is FALSE.
585        target: usize,
586    },
587    /// `frame[dst] = bits(frame[src] as f64)` — the kernel's Int→Float
588    /// promotion.
589    IntToFloat {
590        /// Destination slot.
591        dst: Slot,
592        /// Source slot.
593        src: Slot,
594    },
595    /// `frame[dst] = bits(sqrt(f(frame[src])))` — the kernel's Float sqrt
596    /// builtin (IEEE: negative → NaN, no error path).
597    SqrtF {
598        /// Destination slot.
599        dst: Slot,
600        /// Source slot.
601        src: Slot,
602    },
603    /// Map get through the pinned storage: an Int hit lands in `dst`; a
604    /// miss or non-Int value side-exits (the replay raises the kernel's
605    /// exact error or re-runs the boxed path).
606    MapGet {
607        /// Destination slot.
608        dst: Slot,
609        /// Key slot.
610        key: Slot,
611        /// The pinned `*mut MapStorage` slot.
612        map_slot: Slot,
613        /// Runtime helper address (indirect call, no relocation).
614        helper_addr: i64,
615    },
616    /// Map insert (always succeeds; same storage, same iteration order).
617    MapSet {
618        /// Value slot.
619        src: Slot,
620        /// Key slot.
621        key: Slot,
622        /// The pinned `*mut MapStorage` slot.
623        map_slot: Slot,
624        /// Runtime helper address.
625        helper_addr: i64,
626    },
627    /// DIRECT self-call (mode A): entry patched post-layout into the
628    /// literal pool; static frame-size bound; plain replay deopt.
629    CallSelf {
630        /// Caller register receiving the result.
631        dst: Slot,
632        /// First slot of the staged argument window.
633        args_start: Slot,
634        /// Live-depth cell address.
635        depth_addr: i64,
636        /// Shared status cell address.
637        status_addr: i64,
638        /// MY arena-limit slot (the bound source).
639        limit_slot: Slot,
640        /// The callee's (own) full frame size in slots.
641        frame_size: i64,
642    },
643    /// DIRECT self-call with a FUSED argument copy (Lever A: the pinned-arg
644    /// self-call ABI). Same model as [`MicroOp::CallSelf`], but it stages the
645    /// `arg_count` contiguous scalar arguments from `src_start..` into the
646    /// callee window itself rather than relying on `arg_count` separate
647    /// `Move` pieces before the call — one piece replaces `arg_count + 1`.
648    /// Emitted only for all-scalar (Int/Bool/Float) self-calls; list-param
649    /// and pin-triple staging stay on the per-`Move` path.
650    CallSelfCopy {
651        /// Caller register receiving the result.
652        dst: Slot,
653        /// First slot of the staged argument window (the callee frame base).
654        args_start: Slot,
655        /// First slot of the SOURCE argument block in this frame.
656        src_start: Slot,
657        /// Number of contiguous scalar arguments to copy.
658        arg_count: u16,
659        /// Live-depth cell address.
660        depth_addr: i64,
661        /// Shared status cell address.
662        status_addr: i64,
663        /// MY arena-limit slot (the bound source).
664        limit_slot: Slot,
665        /// The callee's (own) full frame size in slots.
666        frame_size: i64,
667    },
668    /// Allocate a registry-owned fresh list and plant its pin triple.
669    NewList {
670        /// Pin handle slot.
671        vec_slot: Slot,
672        /// Pin buffer-pointer slot.
673        ptr_slot: Slot,
674        /// Pin length slot.
675        len_slot: Slot,
676        /// Runtime allocator address.
677        helper_addr: i64,
678    },
679    /// Plant a pin triple from a list handle already in `handle_slot`
680    /// (a self-call's returned list, seen from the caller).
681    ListTriple {
682        /// Slot holding the `*mut Vec<i64>` handle.
683        handle_slot: Slot,
684        /// Pin handle slot.
685        vec_slot: Slot,
686        /// Pin buffer-pointer slot.
687        ptr_slot: Slot,
688        /// Pin length slot.
689        len_slot: Slot,
690        /// Runtime helper address.
691        helper_addr: i64,
692    },
693    /// Map membership into `dst` (0/1).
694    MapHas {
695        /// Destination slot.
696        dst: Slot,
697        /// Key slot.
698        key: Slot,
699        /// The pinned `*mut MapStorage` slot.
700        map_slot: Slot,
701        /// Runtime helper address.
702        helper_addr: i64,
703    },
704    /// Native SELF-CALL through the program's entry table (callee frame
705    /// windowed at `base + args_start` like the VM). Missing entry, a
706    /// MAX_CALL_DEPTH crossing, or arena overflow side-exits the WHOLE
707    /// native stack via the shared status cell.
708    Call {
709        /// Result slot.
710        dst: Slot,
711        /// Callee frame offset within the caller's frame.
712        args_start: Slot,
713        /// Address of the [entry, regcount] table slot pair.
714        table_addr: i64,
715        /// Address of the shared live-depth cell.
716        depth_addr: i64,
717        /// Address of the shared deopt-status cell.
718        status_addr: i64,
719        /// Frame slot holding the arena END address.
720        limit_slot: Slot,
721        /// MAX_CALL_DEPTH.
722        depth_limit: i64,
723    },
724    /// Pinned-array push. The contiguous regalloc backend lowers this to an
725    /// INLINE fast path — `len < cap ? buffer[len++] = v` straight in registers,
726    /// reading the capacity from the live `*mut Vec` (a codegen-probed field
727    /// offset, never a hardcoded layout) — and calls the runtime helper ONLY on
728    /// the cold realloc boundary (`len == cap`), where it reallocates and
729    /// refreshes the pinned pointer/length slots. The per-piece stencil tier
730    /// always calls the helper.
731    ArrPush {
732        /// Value slot.
733        src: Slot,
734        /// Frame slot holding the `*mut Vec` handle.
735        vec_slot: Slot,
736        /// Pinned pointer slot to refresh.
737        ptr_slot: Slot,
738        /// Pinned length slot to refresh.
739        len_slot: Slot,
740        /// The runtime helper's address.
741        helper_addr: i64,
742        /// 1-BYTE (`Seq of Bool`) element: the inline fast-path store writes the
743        /// boolean normalization `(v != 0) as u8` (matching `logos_rt_push_bool`),
744        /// not 8 raw bytes. `false` for Int/Float (8-byte raw bits).
745        byte: bool,
746        /// 4-byte Int element (`ListRepr::IntsI32`): the inline fast-path store
747        /// TRUNCATES the value to its low 4 bytes (lossless under the narrowing
748        /// proof), matching `logos_rt_push_i32`. Mutually exclusive with `byte`.
749        narrow32: bool,
750    },
751    /// Pinned-array in-place clear through a runtime helper: truncate the buffer
752    /// to empty (keep capacity) and refresh the pinned pointer/length slots
753    /// (length → 0). Lowers an in-region `NewEmptyList` on a pinned array; an
754    /// in-place mutation, so a PRECISE region resumes over it soundly.
755    ListClear {
756        /// Frame slot holding the `*mut Vec` handle.
757        vec_slot: Slot,
758        /// Pinned pointer slot to refresh.
759        ptr_slot: Slot,
760        /// Pinned length slot to refresh (set to 0).
761        len_slot: Slot,
762        /// The runtime helper's address.
763        helper_addr: i64,
764    },
765    /// Pinned MUTABLE-Text append (`Set text to text + <s>`) through a runtime
766    /// helper. `text_handle_slot` holds a `*mut Value` to the VM register cell
767    /// holding the accumulator (planted at region entry); the helper grows the
768    /// accumulator THROUGH that cell with EXACTLY the VM's `add_assign`
769    /// semantics — in place when the `Rc<String>` is sole-owned, copy-on-write
770    /// (a fresh `Rc` written back into the cell, the alias untouched) otherwise.
771    /// `src` is the appended operand (a 1-char frame byte, or a baked constant
772    /// slice). Bit-identical to the tree-walker for every alias case. The buffer
773    /// reaches into the VM register file, so — like the other helper calls — it
774    /// clobbers the caller-saved registers (the residents are spilled/reloaded).
775    StrAppend {
776        /// Frame slot holding the `*mut Value` accumulator-cell handle.
777        text_handle_slot: Slot,
778        /// The appended source operand.
779        src: StrSrc,
780        /// The runtime helper's address (`logos_rt_str_append`).
781        helper_addr: i64,
782    },
783    /// COUNT OVERLAPPING SUBSTRING MATCHES through a runtime helper — the whole
784    /// naive-search nest (string_search) collapsed to one piece. The helper
785    /// reads the pinned haystack/needle byte buffers, counts overlapping needle
786    /// occurrences over the outer range `[frame[i_slot], textLen - needleLen + 1]`
787    /// (1-based, matching the nest), ADDS the count into `frame[count_slot]`, and
788    /// advances `frame[i_slot]` to the loop's exit value. On a recoverable
789    /// disagreement (a checked needle index past the needle buffer) it touches
790    /// nothing and takes the deopt continuation, so the VM replays the exact
791    /// nest on bytecode and raises the same error. Holes: 0 = haystack ptr slot,
792    /// 1 = haystack len slot, 2 = needle ptr slot, 3 = needle len slot, 4 =
793    /// needleLen value slot, 5 = i (start) slot, 6 = count accumulator slot,
794    /// 7 = helper address.
795    MemMem {
796        /// Frame slot holding the pinned haystack buffer pointer.
797        h_ptr_slot: Slot,
798        /// Frame slot holding the pinned haystack length.
799        h_len_slot: Slot,
800        /// Frame slot holding the pinned needle buffer pointer.
801        n_ptr_slot: Slot,
802        /// Frame slot holding the pinned needle length.
803        n_len_slot: Slot,
804        /// Frame slot holding the program's `needleLen` value (inner bound).
805        needle_len_slot: Slot,
806        /// Frame slot holding the 1-based outer index `i` (start; written to the
807        /// exit value on success).
808        i_slot: Slot,
809        /// Frame slot holding the running `count` accumulator (added into).
810        count_slot: Slot,
811        /// The runtime helper's address.
812        helper_addr: i64,
813    },
814    /// Pinned-array load: `frame[dst] = buffer[frame[idx] - 1]` (1-based;
815    /// bits regardless of element kind). Out-of-bounds (incl. 0/negative)
816    /// SIDE-EXITS before any effect.
817    ArrLoad {
818        /// Destination slot.
819        dst: Slot,
820        /// Index slot (1-based value).
821        idx: Slot,
822        /// Frame slot holding the pinned buffer pointer.
823        ptr_slot: Slot,
824        /// Frame slot holding the pinned length.
825        len_slot: Slot,
826        /// 1-byte elements (Bool buffers) instead of 8-byte.
827        byte: bool,
828        /// 4-byte SIGN-EXTENDED Int elements (`ListRepr::IntsI32`): the load is a
829        /// `movsxd` widening the stored `i32` to a full i64 (lossless). Mutually
830        /// exclusive with `byte`; both `false` ⇒ the default 8-byte element.
831        narrow32: bool,
832        /// Bounds-checked (`true`) or elided (`false`, the Oracle proved
833        /// the index in range — V8/LLVM bounds-check elimination). Unchecked
834        /// loads have no out-of-bounds continuation.
835        checked: bool,
836    },
837    /// FUSED affine-index load: `frame[dst] = buffer[idx - 1]` where the 1-based
838    /// `idx = (frame[a] OP frame[b]).wrapping_add(const_offset)` (or
839    /// `frame[a] + const_offset` for [`AffOp::None`]) is computed INSIDE the
840    /// stencil — the peephole collapses the index-arithmetic chain
841    /// (`<binop>(t = a OP b); [Add(t2 = t + Kc);] ArrLoad(t2)`, or
842    /// `Add(t = a + Kc); ArrLoad(t)`) plus the load into ONE piece when the
843    /// intermediate index temps are single-use scratch. The dominant indexed
844    /// inner loops (matrix_mult `i*n+k+1`, knapsack `w - wi + 1`, the `w + 1`
845    /// row read) lose worst to V8 precisely on this per-op dispatch; folding the
846    /// 2-3 index ops into the load is a direct dispatch-reduction win. Out of
847    /// bounds (incl. the 0/negative index the wrapping-sub trick catches)
848    /// SIDE-EXITS before any effect, exactly like [`MicroOp::ArrLoad`].
849    ArrLoadAffine {
850        /// Destination slot.
851        dst: Slot,
852        /// First index operand slot.
853        a: Slot,
854        /// The index op (`None` = single slot + const; else two-slot binop).
855        op: AffOp,
856        /// Second index operand slot (ignored for [`AffOp::None`]).
857        b: Slot,
858        /// The constant folded into the index (the trailing `+ Kc`; `0` if none).
859        const_offset: i64,
860        /// Frame slot holding the pinned buffer pointer.
861        ptr_slot: Slot,
862        /// Frame slot holding the pinned length.
863        len_slot: Slot,
864        /// Bounds-checked (`true`) or elided (`false`, the Oracle proved the
865        /// computed index in range). Unchecked has no out-of-bounds continuation.
866        checked: bool,
867    },
868    /// FUSED two-load float binop: `frame[dst] = bits(f(buf[i-1]) <op>
869    /// f(buf[j-1]))`, BOTH elements from the SAME pinned 8-byte buffer (one
870    /// pointer slot, one length slot). The peephole collapses `ArrLoad(t1 =
871    /// arr[i]); ArrLoad(t2 = arr[j]); {Add,Sub,Mul}F(dst = t1 OP t2)` when both
872    /// loads hit the same array and the two scratch loads are single-use — so
873    /// the two f64s never round-trip through the frame. EITHER index out of
874    /// bounds SIDE-EXITS before any effect, exactly like [`MicroOp::ArrLoad`].
875    ArrLoad2F {
876        /// Destination slot.
877        dst: Slot,
878        /// First index slot (1-based value).
879        i: Slot,
880        /// Second index slot (1-based value).
881        j: Slot,
882        /// Frame slot holding the pinned buffer pointer.
883        ptr_slot: Slot,
884        /// Frame slot holding the pinned length.
885        len_slot: Slot,
886        /// The float operation applied to the two loaded values.
887        op: FOp,
888    },
889    /// FUSED two-load INTEGER binop: `frame[dst] = a[i-1] <op> b[j-1]`, the two
890    /// 8-byte elements loaded from TWO (possibly distinct) pinned int buffers
891    /// (each with its own pointer + length slot). The peephole collapses
892    /// `ArrLoad(t1 = a[i]); ArrLoad(t2 = b[j]); {Add,Sub,Mul}(dst = t1 OP t2)`
893    /// — the matrix-multiply / dot-product idiom `c += a[..] * b[..]` — when the
894    /// two scratch loads are single-use, so the loaded i64s never round-trip the
895    /// frame and three dispatches collapse to one. EITHER index out of bounds
896    /// SIDE-EXITS before any effect, exactly like [`MicroOp::ArrLoad`]. The two
897    /// buffers may be the SAME (one self-referential dot product) or distinct.
898    ArrLoad2 {
899        /// Destination slot.
900        dst: Slot,
901        /// First index slot (1-based value), addressing buffer `a`.
902        i: Slot,
903        /// Second index slot (1-based value), addressing buffer `b`.
904        j: Slot,
905        /// Frame slot holding the pinned pointer of the first buffer (`a`).
906        ptr_a: Slot,
907        /// Frame slot holding the pinned length of the first buffer (`a`).
908        len_a: Slot,
909        /// Frame slot holding the pinned pointer of the second buffer (`b`).
910        ptr_b: Slot,
911        /// Frame slot holding the pinned length of the second buffer (`b`).
912        len_b: Slot,
913        /// The integer operation applied to the two loaded values.
914        op: IOp,
915        /// Bounds-checked (`true`) or elided (`false`, the Oracle proved both
916        /// indices in range). Unchecked has no out-of-bounds continuation.
917        checked: bool,
918    },
919    /// Pinned-array store: `buffer[frame[idx] - 1] = frame[src]`; bounds
920    /// side-exit BEFORE the store.
921    ArrStore {
922        /// Source slot.
923        src: Slot,
924        /// Index slot (1-based value).
925        idx: Slot,
926        /// Frame slot holding the pinned buffer pointer.
927        ptr_slot: Slot,
928        /// Frame slot holding the pinned length.
929        len_slot: Slot,
930        /// 1-byte elements (Bool buffers; stores normalize to 0/1).
931        byte: bool,
932        /// 4-byte SIGN-EXTENDED Int elements (`ListRepr::IntsI32`, the VM's
933        /// `LOGOS_NARROW_VM` half-width buffers): a store TRUNCATES the value to
934        /// its low 4 bytes (lossless under the narrowing proof). Mutually
935        /// exclusive with `byte`; both `false` ⇒ the default 8-byte element.
936        narrow32: bool,
937        /// Bounds-checked (`true`) or elided (`false`, the Oracle proved
938        /// the index in range). Unchecked stores have no side-exit.
939        checked: bool,
940    },
941    /// FUSED read-modify-write on a pinned 8-byte int array:
942    /// `buffer[frame[idx] - 1] = buffer[frame[idx] - 1] <op> frame[operand]`
943    /// in ONE stencil. The peephole collapses `ArrLoad(t = arr[idx]);
944    /// <int ALU>(t2 = t OP operand); ArrStore(arr[idx] = t2)` when both array
945    /// ops hit the SAME pinned buffer + index and the two scratch values
946    /// (`t`, `t2`) are single-use — so the element never round-trips the frame
947    /// and ONE bounds check covers the load+store. Out-of-bounds (incl.
948    /// 0/negative) SIDE-EXITS before any effect, exactly like [`MicroOp::ArrStore`].
949    ArrRMW {
950        /// Index slot (1-based value).
951        idx: Slot,
952        /// Operand slot (the RHS of the op; `frame[operand]`).
953        operand: Slot,
954        /// Frame slot holding the pinned buffer pointer.
955        ptr_slot: Slot,
956        /// Frame slot holding the pinned length.
957        len_slot: Slot,
958        /// The integer operation applied in place.
959        op: RmwOp,
960        /// Bounds-checked (`true`) or elided (`false`). Unchecked has no
961        /// out-of-bounds continuation.
962        checked: bool,
963    },
964    /// FUSED conditional adjacent swap on a pinned 8-byte int array:
965    /// `let a = buf[idx1-1]; let b = buf[idx2-1]; if cmp(a, b) { buf[idx1-1] = b;
966    /// buf[idx2-1] = a }` in ONE stencil. The peephole collapses the sort
967    /// inner-loop idiom `ArrLoad(a,i1); ArrLoad(b,i2); Branch(cmp,a,b,skip);
968    /// ArrStore(i1,b); ArrStore(i2,a)` (bubble/insertion/quick sort) — 5 ops → 1,
969    /// the two loaded values never round-trip the frame, the compare-branch is
970    /// gone. The swap is ATOMIC (both writes or neither); a checked variant
971    /// bounds-checks BOTH indices up front and SIDE-EXITS before any effect, the
972    /// unchecked variant (Oracle-proven indices) has no side-exit.
973    ArrCondSwap {
974        /// First index slot (1-based value).
975        idx1: Slot,
976        /// Second index slot (1-based value).
977        idx2: Slot,
978        /// Frame slot holding the pinned buffer pointer.
979        ptr_slot: Slot,
980        /// Frame slot holding the pinned length.
981        len_slot: Slot,
982        /// Swap when `cmp(buf[idx1-1], buf[idx2-1])` is TRUE.
983        cmp: Cmp,
984        /// Bounds-checked (`true`) or elided (`false`).
985        checked: bool,
986    },
987    /// FUSED unconditional adjacent swap on a pinned 8-byte int array:
988    /// `let a = buf[idx1-1]; let b = buf[idx2-1]; buf[idx1-1] = b; buf[idx2-1] =
989    /// a` in ONE stencil. The peephole collapses the `Let tmp = arr[i]; arr[i] =
990    /// arr[j]; arr[j] = tmp` exchange (quicksort/heap_sort/mergesort partition &
991    /// sift) — `ArrLoad(a,i); ArrLoad(b,j); ArrStore(i,b); ArrStore(j,a)` — 4 ops
992    /// → 1. The swap is ATOMIC; a checked variant bounds-checks BOTH indices up
993    /// front and SIDE-EXITS before any effect.
994    ArrSwap {
995        /// First index slot (1-based value).
996        idx1: Slot,
997        /// Second index slot (1-based value).
998        idx2: Slot,
999        /// Frame slot holding the pinned buffer pointer.
1000        ptr_slot: Slot,
1001        /// Frame slot holding the pinned length.
1002        len_slot: Slot,
1003        /// Bounds-checked (`true`) or elided (`false`).
1004        checked: bool,
1005    },
1006    /// FUSED float multiply-add: `frame[dst] = bits((f(a) * f(b)) + f(c))` — the
1007    /// peephole collapses `MulF(t = a*b); AddF(d = t + c)` (the product `t`
1008    /// single-use) into one stencil, cutting a dispatch on the float arithmetic
1009    /// chains that dominate the float cluster (nbody's `dz*dz + s`, dot
1010    /// products). The product and the add round SEPARATELY (`(a*b)+c`, two
1011    /// roundings) — NOT a hardware single-rounding FMA — so it stays bit-identical
1012    /// to the unfused `MulF`+`AddF`. Mem-form: every operand reads from the frame,
1013    /// so it preserves the threaded XMM pins (the caller only fuses when the
1014    /// operands are frame-resident, keeping it a spill-free win).
1015    FmaF {
1016        /// Destination slot.
1017        dst: Slot,
1018        /// First product operand slot.
1019        a: Slot,
1020        /// Second product operand slot.
1021        b: Slot,
1022        /// Addend slot.
1023        c: Slot,
1024    },
1025    /// Unconditional transfer to the micro-op at `target` (an index into the
1026    /// program; forward or backward).
1027    Jump {
1028        /// Micro-op index to transfer to.
1029        target: usize,
1030    },
1031    /// Transfer to `target` when frame\[cond\] is ZERO; fall through otherwise.
1032    JumpIfFalse {
1033        /// Condition slot (zero = jump).
1034        cond: Slot,
1035        /// Micro-op index to transfer to.
1036        target: usize,
1037    },
1038    /// Transfer to `target` when frame\[cond\] is NONZERO; fall through
1039    /// otherwise (the same brz stencil with swapped continuations).
1040    JumpIfTrue {
1041        /// Condition slot (nonzero = jump).
1042        cond: Slot,
1043        /// Micro-op index to transfer to.
1044        target: usize,
1045    },
1046    /// Terminate the chain, returning frame\[src\].
1047    Return {
1048        /// Slot whose value is returned.
1049        src: Slot,
1050    },
1051}
1052
1053/// Compile errors — structural, found before any code is emitted.
1054#[derive(Debug, PartialEq, Eq)]
1055pub enum JitCompileError {
1056    /// The program is empty.
1057    Empty,
1058    /// Execution can run off the end: the final op must be Return or Jump.
1059    FallsOffTheEnd,
1060    /// A jump target is outside the program.
1061    BadJumpTarget {
1062        /// Index of the offending op.
1063        op_index: usize,
1064        /// The out-of-range target.
1065        target: usize,
1066    },
1067    /// Assembly failed (missing hole/patch/map errors).
1068    Assembly(String),
1069    /// An op the per-piece stencil tier has no lowering for (e.g.
1070    /// [`MicroOp::StrAppend`], which is emitted ONLY for the contiguous regalloc
1071    /// backend). The caller declines (falls back to bytecode), so this is never a
1072    /// hard failure — it just routes around the stencil tier.
1073    Unsupported(&'static str),
1074}
1075
1076impl std::fmt::Display for JitCompileError {
1077    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1078        match self {
1079            JitCompileError::Empty => write!(f, "jit: empty program"),
1080            JitCompileError::FallsOffTheEnd => {
1081                write!(f, "jit: the final op must be Return or Jump")
1082            }
1083            JitCompileError::BadJumpTarget { op_index, target } => {
1084                write!(f, "jit: op {op_index} jumps to {target}, outside the program")
1085            }
1086            JitCompileError::Assembly(e) => write!(f, "jit: assembly failed: {e}"),
1087            JitCompileError::Unsupported(name) => {
1088                write!(f, "jit: the stencil tier has no lowering for {name}")
1089            }
1090        }
1091    }
1092}
1093
1094impl std::error::Error for JitCompileError {}
1095
1096/// What one chain run produced.
1097#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1098pub enum ChainOutcome {
1099    /// The chain reached its Return; this is the returned value.
1100    Return(i64),
1101    /// A checked op side-exited; the payload is the RAW status-cell value.
1102    /// `1` is the plain marker (replay from the entry state — every effect
1103    /// confined to the private frame). `(pc << 2) | 3` is a PRECISE exit:
1104    /// effects already landed, resume at bytecode `pc`.
1105    Deopt(i64),
1106}
1107
1108impl ChainOutcome {
1109    /// The returned value; panics on [`ChainOutcome::Deopt`] — for tests and
1110    /// callers of chains that contain no checked ops.
1111    pub fn expect_return(self) -> i64 {
1112        match self {
1113            ChainOutcome::Return(v) => v,
1114            ChainOutcome::Deopt(_) => panic!("chain side-exited (deopt) — no return value"),
1115        }
1116    }
1117
1118    /// True when the run side-exited.
1119    pub fn is_deopt(&self) -> bool {
1120        matches!(self, ChainOutcome::Deopt(_))
1121    }
1122}
1123
1124/// A compiled chain plus its side-exit status cell (present only when the
1125/// program contains checked ops). The deopt stencil stores 1 through the
1126/// patched cell address; [`CompiledChain::run_with_frame`] reads-and-resets
1127/// it after every run.
1128#[derive(Debug)]
1129pub struct CompiledChain {
1130    chain: JitChain,
1131    status: Option<std::sync::Arc<AtomicI64>>,
1132    /// Extra cells the chain's machine code reads by baked address and that must
1133    /// therefore outlive the chain (the contiguous FUNCTION backend's self-call
1134    /// ENTRY cell: an `Arc<AtomicI64>` holding this chain's own entry address,
1135    /// loaded by every `CallSelf`/`CallSelfCopy` and written after mapping). The
1136    /// run/deopt contract is unchanged — these are pure keep-alives.
1137    #[allow(dead_code)]
1138    keepalive: Vec<std::sync::Arc<AtomicI64>>,
1139}
1140
1141impl CompiledChain {
1142    /// Wrap a raw `JitChain` (from the contiguous regalloc backend) and its
1143    /// shared status cell as a runnable `CompiledChain` — same run/deopt
1144    /// contract as a stencil chain.
1145    pub(crate) fn from_chain(
1146        chain: JitChain,
1147        status: Option<std::sync::Arc<AtomicI64>>,
1148    ) -> Self {
1149        CompiledChain { chain, status, keepalive: Vec::new() }
1150    }
1151
1152    /// Wrap a `JitChain` plus its status cell AND any extra cells the code reads
1153    /// by baked address (the self-call entry cell), which the `CompiledChain`
1154    /// then keeps alive for the chain's whole lifetime.
1155    pub(crate) fn from_chain_keepalive(
1156        chain: JitChain,
1157        status: Option<std::sync::Arc<AtomicI64>>,
1158        keepalive: Vec<std::sync::Arc<AtomicI64>>,
1159    ) -> Self {
1160        CompiledChain { chain, status, keepalive }
1161    }
1162
1163    /// Run over the given frame (slot 0 = VM register 0, …) with a fresh
1164    /// operand stack.
1165    pub fn run_with_frame(&self, frame: &mut [i64]) -> ChainOutcome {
1166        let v = self.chain.run_with_frame(frame);
1167        if let Some(cell) = &self.status {
1168            let raw = cell.swap(0, Ordering::Relaxed);
1169            if raw != 0 {
1170                return ChainOutcome::Deopt(raw);
1171            }
1172        }
1173        ChainOutcome::Return(v)
1174    }
1175
1176    /// The mapped code+pool bytes (diagnostics/tests).
1177    pub fn bytes(&self) -> &[u8] {
1178        self.chain.bytes()
1179    }
1180
1181    /// See [`crate::buffer::JitChain::patch_marked`].
1182    pub fn patch_marked(&self, value: u64) -> Result<(), crate::JitError> {
1183        self.chain.patch_marked(value)
1184    }
1185
1186    /// See [`crate::buffer::JitChain::has_patch_marks`].
1187    pub fn has_patch_marks(&self) -> bool {
1188        self.chain.has_patch_marks()
1189    }
1190
1191    /// The runtime base address (diagnostics/tests).
1192    pub fn base(&self) -> u64 {
1193        self.chain.base()
1194    }
1195
1196    /// Stencil pieces in the chain (diagnostics/tests).
1197    pub fn piece_count(&self) -> usize {
1198        self.chain.piece_count()
1199    }
1200}
1201
1202/// Lower a straight-line micro-op program into one executable stencil chain.
1203///
1204/// Emit ONE memory-form piece (an op outside the variant families) with
1205/// explicit continuation and deopt labels — the pinned compiler's bridge
1206/// to the classic stencils. Pinned operands were spilled before this
1207/// piece and pinned destinations reload after it.
1208/// The fused read-modify-write stencil for an op + bounds-check mode. The
1209/// checked twins side-exit through cont 1 on out-of-bounds; the unchecked `_u`
1210/// twins have no length hole and no out-of-bounds continuation.
1211fn rmw_stencil(op: RmwOp, checked: bool) -> &'static crate::Stencil {
1212    match (op, checked) {
1213        (RmwOp::Add, true) => &ST_ARRRMW_ADD,
1214        (RmwOp::Add, false) => &ST_ARRRMW_ADD_U,
1215        (RmwOp::Sub, true) => &ST_ARRRMW_SUB,
1216        (RmwOp::Sub, false) => &ST_ARRRMW_SUB_U,
1217        (RmwOp::Mul, true) => &ST_ARRRMW_MUL,
1218        (RmwOp::Mul, false) => &ST_ARRRMW_MUL_U,
1219        (RmwOp::And, true) => &ST_ARRRMW_AND,
1220        (RmwOp::And, false) => &ST_ARRRMW_AND_U,
1221        (RmwOp::Or, true) => &ST_ARRRMW_OR,
1222        (RmwOp::Or, false) => &ST_ARRRMW_OR_U,
1223        (RmwOp::Xor, true) => &ST_ARRRMW_XOR,
1224        (RmwOp::Xor, false) => &ST_ARRRMW_XOR_U,
1225        (RmwOp::AddF, true) => &ST_ARRRMW_ADDF,
1226        (RmwOp::AddF, false) => &ST_ARRRMW_ADDF_U,
1227        (RmwOp::SubF, true) => &ST_ARRRMW_SUBF,
1228        (RmwOp::SubF, false) => &ST_ARRRMW_SUBF_U,
1229        (RmwOp::MulF, true) => &ST_ARRRMW_MULF,
1230        (RmwOp::MulF, false) => &ST_ARRRMW_MULF_U,
1231    }
1232}
1233
1234/// The fused two-buffer integer-load binop stencil for an op + bounds-check
1235/// mode. The checked twins side-exit through cont 1 when EITHER index is out of
1236/// bounds; the unchecked `_u` twins have no length holes and no continuation.
1237fn ld2_int_stencil(op: IOp, checked: bool) -> &'static crate::Stencil {
1238    match (op, checked) {
1239        (IOp::Add, true) => &ST_ARRLD2_ADD,
1240        (IOp::Add, false) => &ST_ARRLD2_ADD_U,
1241        (IOp::Sub, true) => &ST_ARRLD2_SUB,
1242        (IOp::Sub, false) => &ST_ARRLD2_SUB_U,
1243        (IOp::Mul, true) => &ST_ARRLD2_MUL,
1244        (IOp::Mul, false) => &ST_ARRLD2_MUL_U,
1245    }
1246}
1247
1248/// The fused affine-index load stencil for an index op + bounds-check mode. The
1249/// checked twins side-exit through cont 1 when the COMPUTED index is out of
1250/// bounds; the unchecked `_u` twins have no length hole and no continuation.
1251/// The pinned-array LOAD stencil for an element width: 1-byte (Bool, zero-ext),
1252/// 4-byte (`IntsI32`, sign-ext), or the default 8-byte (Int/Float raw bits).
1253/// `byte` and `narrow32` are mutually exclusive (the adapter never sets both).
1254fn arrld_stencil(byte: bool, narrow32: bool, checked: bool) -> &'static crate::Stencil {
1255    match (byte, narrow32, checked) {
1256        (true, _, true) => &ST_ARRLDB,
1257        (true, _, false) => &ST_ARRLDB_U,
1258        (_, true, true) => &ST_ARRLD_I32,
1259        (_, true, false) => &ST_ARRLD_I32_U,
1260        (false, false, true) => &ST_ARRLD,
1261        (false, false, false) => &ST_ARRLD_U,
1262    }
1263}
1264
1265/// The pinned-array STORE stencil for an element width (see [`arrld_stencil`]).
1266fn arrst_stencil(byte: bool, narrow32: bool, checked: bool) -> &'static crate::Stencil {
1267    match (byte, narrow32, checked) {
1268        (true, _, true) => &ST_ARRSTB,
1269        (true, _, false) => &ST_ARRSTB_U,
1270        (_, true, true) => &ST_ARRST_I32,
1271        (_, true, false) => &ST_ARRST_I32_U,
1272        (false, false, true) => &ST_ARRST,
1273        (false, false, false) => &ST_ARRST_U,
1274    }
1275}
1276
1277fn affine_stencil(op: AffOp, checked: bool) -> &'static crate::Stencil {
1278    match (op, checked) {
1279        (AffOp::None, true) => &ST_ARRLDAFF_NONE,
1280        (AffOp::None, false) => &ST_ARRLDAFF_NONE_U,
1281        (AffOp::Add, true) => &ST_ARRLDAFF_ADD,
1282        (AffOp::Add, false) => &ST_ARRLDAFF_ADD_U,
1283        (AffOp::Sub, true) => &ST_ARRLDAFF_SUB,
1284        (AffOp::Sub, false) => &ST_ARRLDAFF_SUB_U,
1285        (AffOp::Mul, true) => &ST_ARRLDAFF_MUL,
1286        (AffOp::Mul, false) => &ST_ARRLDAFF_MUL_U,
1287    }
1288}
1289
1290/// The conditional-swap stencil for a comparison + bounds-check mode. The
1291/// peephole only ever emits the four orderings (Eq/NotEq swaps are nonsensical).
1292fn condswap_stencil(cmp: Cmp, checked: bool) -> &'static crate::Stencil {
1293    match (cmp, checked) {
1294        (Cmp::Gt, true) => &ST_ARRCONDSWAP_GT,
1295        (Cmp::Gt, false) => &ST_ARRCONDSWAP_GT_U,
1296        (Cmp::Lt, true) => &ST_ARRCONDSWAP_LT,
1297        (Cmp::Lt, false) => &ST_ARRCONDSWAP_LT_U,
1298        (Cmp::GtEq, true) => &ST_ARRCONDSWAP_GE,
1299        (Cmp::GtEq, false) => &ST_ARRCONDSWAP_GE_U,
1300        (Cmp::LtEq, true) => &ST_ARRCONDSWAP_LE,
1301        (Cmp::LtEq, false) => &ST_ARRCONDSWAP_LE_U,
1302        (Cmp::Eq | Cmp::NotEq, _) => unreachable!("cond-swap peephole never emits Eq/NotEq"),
1303    }
1304}
1305
1306fn emit_mem_form(
1307    buf: &mut JitBuffer,
1308    op: &MicroOp,
1309    next: crate::buffer::Label,
1310    deopt_piece: usize,
1311    status: &Option<std::sync::Arc<AtomicI64>>,
1312) {
1313    match *op {
1314        MicroOp::Div { dst, lhs, rhs } | MicroOp::Mod { dst, lhs, rhs } => {
1315            let stencil = if matches!(op, MicroOp::Div { .. }) { &ST_DIV3C } else { &ST_MOD3C };
1316            buf.push_stencil(
1317                stencil,
1318                &[
1319                    HoleValue::Const(0, lhs as i64),
1320                    HoleValue::Const(1, rhs as i64),
1321                    HoleValue::Const(2, dst as i64),
1322                    HoleValue::Cont(0, next),
1323                    HoleValue::Cont(1, buf.label(deopt_piece)),
1324                ],
1325            );
1326        }
1327        MicroOp::DivPow2 { dst, lhs, k } => {
1328            buf.push_stencil(
1329                &ST_DIVPOW2,
1330                &[
1331                    HoleValue::Const(0, lhs as i64),
1332                    HoleValue::Const(1, k as i64),
1333                    HoleValue::Const(2, (1i64 << k) - 1),
1334                    HoleValue::Const(3, dst as i64),
1335                    HoleValue::Cont(0, next),
1336                ],
1337            );
1338        }
1339        MicroOp::MagicDivU { dst, lhs, magic, more, mul_back } => {
1340            buf.push_stencil(
1341                &ST_MAGICDIV,
1342                &[
1343                    HoleValue::Const(0, lhs as i64),
1344                    HoleValue::Const(1, magic as i64),
1345                    HoleValue::Const(2, more as i64),
1346                    HoleValue::Const(3, mul_back),
1347                    HoleValue::Const(4, dst as i64),
1348                    HoleValue::Cont(0, next),
1349                ],
1350            );
1351        }
1352        MicroOp::AddF { dst, lhs, rhs }
1353        | MicroOp::SubF { dst, lhs, rhs }
1354        | MicroOp::MulF { dst, lhs, rhs } => {
1355            let stencil = match op {
1356                MicroOp::AddF { .. } => &ST_ADDF3,
1357                MicroOp::SubF { .. } => &ST_SUBF3,
1358                _ => &ST_MULF3,
1359            };
1360            buf.push_stencil(
1361                stencil,
1362                &[
1363                    HoleValue::Const(0, lhs as i64),
1364                    HoleValue::Const(1, rhs as i64),
1365                    HoleValue::Const(2, dst as i64),
1366                    HoleValue::Cont(0, next),
1367                ],
1368            );
1369        }
1370        MicroOp::DivF { dst, lhs, rhs } => {
1371            buf.push_stencil(
1372                &ST_DIVF3C,
1373                &[
1374                    HoleValue::Const(0, lhs as i64),
1375                    HoleValue::Const(1, rhs as i64),
1376                    HoleValue::Const(2, dst as i64),
1377                    HoleValue::Cont(0, next),
1378                    HoleValue::Cont(1, buf.label(deopt_piece)),
1379                ],
1380            );
1381        }
1382        MicroOp::LtF { dst, lhs, rhs }
1383        | MicroOp::LtEqF { dst, lhs, rhs }
1384        | MicroOp::EqF { dst, lhs, rhs }
1385        | MicroOp::NeqF { dst, lhs, rhs } => {
1386            let stencil = match op {
1387                MicroOp::LtF { .. } => &ST_LTF3,
1388                MicroOp::LtEqF { .. } => &ST_LEF3,
1389                MicroOp::EqF { .. } => &ST_EQF3,
1390                _ => &ST_NEF3,
1391            };
1392            buf.push_stencil(
1393                stencil,
1394                &[
1395                    HoleValue::Const(0, lhs as i64),
1396                    HoleValue::Const(1, rhs as i64),
1397                    HoleValue::Const(2, dst as i64),
1398                    HoleValue::Cont(0, next),
1399                ],
1400            );
1401        }
1402        // a > b ⇔ b < a; a >= b ⇔ b <= a (Lever 2a): a VALUE-form float ordering
1403        // compare lowers mem-form by SWAPPING operands and reusing the LtF/LeF
1404        // stencil — IEEE-exact, NaN-false on both sides of the swap, bit-identical
1405        // to the tree-walker. (Float EQUALITY is epsilon-fuzzy and stays on its
1406        // own EqF/NeqF path — never swapped here.) This gives the pinned compiler
1407        // a lowering for `GtF`/`GtEqF`, so a region carrying a value-form float
1408        // `>`/`>=` no longer has to drop ALL its XMM pins.
1409        MicroOp::GtF { dst, lhs, rhs } | MicroOp::GtEqF { dst, lhs, rhs } => {
1410            let stencil = match op {
1411                MicroOp::GtF { .. } => &ST_LTF3,
1412                _ => &ST_LEF3,
1413            };
1414            buf.push_stencil(
1415                stencil,
1416                &[
1417                    HoleValue::Const(0, rhs as i64),
1418                    HoleValue::Const(1, lhs as i64),
1419                    HoleValue::Const(2, dst as i64),
1420                    HoleValue::Cont(0, next),
1421                ],
1422            );
1423        }
1424        MicroOp::NotInt { dst, src } | MicroOp::NotBool { dst, src }
1425        | MicroOp::IntToFloat { dst, src } | MicroOp::SqrtF { dst, src } => {
1426            let stencil = match op {
1427                MicroOp::NotInt { .. } => &ST_NOTI2,
1428                MicroOp::NotBool { .. } => &ST_NOTB2,
1429                MicroOp::SqrtF { .. } => &ST_SQRTF2,
1430                _ => &ST_I2F2,
1431            };
1432            buf.push_stencil(
1433                stencil,
1434                &[
1435                    HoleValue::Const(0, src as i64),
1436                    HoleValue::Const(1, dst as i64),
1437                    HoleValue::Cont(0, next),
1438                ],
1439            );
1440        }
1441        MicroOp::Call {
1442            dst,
1443            args_start,
1444            table_addr,
1445            depth_addr,
1446            status_addr,
1447            limit_slot,
1448            depth_limit,
1449        } => {
1450            debug_assert!(status.is_some(), "calls require the status cell");
1451            buf.push_stencil(
1452                &ST_CALL,
1453                &[
1454                    HoleValue::Const(0, table_addr),
1455                    HoleValue::Const(1, args_start as i64),
1456                    HoleValue::Const(2, dst as i64),
1457                    HoleValue::Const(3, depth_addr),
1458                    HoleValue::Const(4, status_addr),
1459                    HoleValue::Const(5, limit_slot as i64),
1460                    HoleValue::Const(6, depth_limit),
1461                    HoleValue::Cont(0, next),
1462                ],
1463            );
1464        }
1465        MicroOp::MapGet { dst, key, map_slot, helper_addr } => {
1466            buf.push_stencil(
1467                &ST_MAPHGET,
1468                &[
1469                    HoleValue::Const(0, map_slot as i64),
1470                    HoleValue::Const(1, key as i64),
1471                    HoleValue::Const(2, dst as i64),
1472                    HoleValue::Const(3, helper_addr),
1473                    HoleValue::Cont(0, next),
1474                    HoleValue::Cont(1, buf.label(deopt_piece)),
1475                ],
1476            );
1477        }
1478        MicroOp::MapSet { src, key, map_slot, helper_addr } => {
1479            buf.push_stencil(
1480                &ST_MAPHSET,
1481                &[
1482                    HoleValue::Const(0, map_slot as i64),
1483                    HoleValue::Const(1, key as i64),
1484                    HoleValue::Const(2, src as i64),
1485                    HoleValue::Const(3, helper_addr),
1486                    HoleValue::Cont(0, next),
1487                ],
1488            );
1489        }
1490        MicroOp::MemMem {
1491            h_ptr_slot,
1492            h_len_slot,
1493            n_ptr_slot,
1494            n_len_slot,
1495            needle_len_slot,
1496            i_slot,
1497            count_slot,
1498            helper_addr,
1499        } => {
1500            debug_assert!(status.is_some(), "memmem requires the status cell for its deopt exit");
1501            buf.push_stencil(
1502                &ST_MEMMEM,
1503                &[
1504                    HoleValue::Const(0, h_ptr_slot as i64),
1505                    HoleValue::Const(1, h_len_slot as i64),
1506                    HoleValue::Const(2, n_ptr_slot as i64),
1507                    HoleValue::Const(3, n_len_slot as i64),
1508                    HoleValue::Const(4, needle_len_slot as i64),
1509                    HoleValue::Const(5, i_slot as i64),
1510                    HoleValue::Const(6, count_slot as i64),
1511                    HoleValue::Const(7, helper_addr),
1512                    HoleValue::Cont(0, next),
1513                    HoleValue::Cont(1, buf.label(deopt_piece)),
1514                ],
1515            );
1516        }
1517        MicroOp::CallSelf { dst, args_start, depth_addr, status_addr, limit_slot, frame_size } => {
1518            buf.push_stencil(
1519                &ST_CALL_SELF,
1520                &[
1521                    HoleValue::Const(0, 0),
1522                    HoleValue::Const(1, args_start as i64),
1523                    HoleValue::Const(2, dst as i64),
1524                    HoleValue::Const(3, depth_addr),
1525                    HoleValue::Const(4, status_addr),
1526                    HoleValue::Const(5, limit_slot as i64),
1527                    HoleValue::Const(6, frame_size),
1528                    HoleValue::Cont(0, next),
1529                ],
1530            );
1531            buf.mark_patch_hole(0);
1532        }
1533        MicroOp::CallSelfCopy {
1534            dst,
1535            args_start,
1536            src_start,
1537            arg_count,
1538            depth_addr,
1539            status_addr,
1540            limit_slot,
1541            frame_size,
1542        } => {
1543            buf.push_stencil(
1544                &ST_CALL_SELF_COPY,
1545                &[
1546                    HoleValue::Const(0, 0),
1547                    HoleValue::Const(1, args_start as i64),
1548                    HoleValue::Const(2, dst as i64),
1549                    HoleValue::Const(3, depth_addr),
1550                    HoleValue::Const(4, status_addr),
1551                    HoleValue::Const(5, limit_slot as i64),
1552                    HoleValue::Const(6, frame_size),
1553                    HoleValue::Const(7, src_start as i64),
1554                    HoleValue::Const(8, arg_count as i64),
1555                    HoleValue::Cont(0, next),
1556                ],
1557            );
1558            buf.mark_patch_hole(0);
1559        }
1560        MicroOp::NewList { vec_slot, ptr_slot, len_slot, helper_addr } => {
1561            buf.push_stencil(
1562                &ST_ALLOCLIST,
1563                &[
1564                    HoleValue::Const(0, vec_slot as i64),
1565                    HoleValue::Const(1, ptr_slot as i64),
1566                    HoleValue::Const(2, len_slot as i64),
1567                    HoleValue::Const(3, helper_addr),
1568                    HoleValue::Cont(0, next),
1569                ],
1570            );
1571        }
1572        MicroOp::ListTriple { handle_slot, vec_slot, ptr_slot, len_slot, helper_addr } => {
1573            buf.push_stencil(
1574                &ST_LISTTRIPLE,
1575                &[
1576                    HoleValue::Const(0, handle_slot as i64),
1577                    HoleValue::Const(1, vec_slot as i64),
1578                    HoleValue::Const(2, ptr_slot as i64),
1579                    HoleValue::Const(3, len_slot as i64),
1580                    HoleValue::Const(4, helper_addr),
1581                    HoleValue::Cont(0, next),
1582                ],
1583            );
1584        }
1585        MicroOp::MapHas { dst, key, map_slot, helper_addr } => {
1586            buf.push_stencil(
1587                &ST_MAPHHAS,
1588                &[
1589                    HoleValue::Const(0, map_slot as i64),
1590                    HoleValue::Const(1, key as i64),
1591                    HoleValue::Const(2, dst as i64),
1592                    HoleValue::Const(3, helper_addr),
1593                    HoleValue::Cont(0, next),
1594                ],
1595            );
1596        }
1597        // Array load/store: 1-based, bounds-checked. A failed bound jumps to
1598        // the shared deopt terminal (region replay / function side-exit).
1599        // The pinned mem-form path has already spilled the pinned operands
1600        // (idx/ptr/len/src) to the frame, so these read frame slots exactly
1601        // like the unpinned chain.
1602        MicroOp::ArrLoad { dst, idx, ptr_slot, len_slot, byte, narrow32, checked } => {
1603            if checked {
1604                buf.push_stencil(
1605                    arrld_stencil(byte, narrow32, true),
1606                    &[
1607                        HoleValue::Const(0, idx as i64),
1608                        HoleValue::Const(1, ptr_slot as i64),
1609                        HoleValue::Const(2, len_slot as i64),
1610                        HoleValue::Const(3, dst as i64),
1611                        HoleValue::Cont(0, next),
1612                        HoleValue::Cont(1, buf.label(deopt_piece)),
1613                    ],
1614                );
1615            } else {
1616                buf.push_stencil(
1617                    arrld_stencil(byte, narrow32, false),
1618                    &[
1619                        HoleValue::Const(0, idx as i64),
1620                        HoleValue::Const(1, ptr_slot as i64),
1621                        HoleValue::Const(3, dst as i64),
1622                        HoleValue::Cont(0, next),
1623                    ],
1624                );
1625            }
1626        }
1627        MicroOp::ArrLoadAffine { dst, a, op, b, const_offset, ptr_slot, len_slot, checked } => {
1628            if checked {
1629                buf.push_stencil(
1630                    affine_stencil(op, true),
1631                    &[
1632                        HoleValue::Const(0, a as i64),
1633                        HoleValue::Const(1, b as i64),
1634                        HoleValue::Const(2, const_offset),
1635                        HoleValue::Const(3, ptr_slot as i64),
1636                        HoleValue::Const(4, len_slot as i64),
1637                        HoleValue::Const(5, dst as i64),
1638                        HoleValue::Cont(0, next),
1639                        HoleValue::Cont(1, buf.label(deopt_piece)),
1640                    ],
1641                );
1642            } else {
1643                buf.push_stencil(
1644                    affine_stencil(op, false),
1645                    &[
1646                        HoleValue::Const(0, a as i64),
1647                        HoleValue::Const(1, b as i64),
1648                        HoleValue::Const(2, const_offset),
1649                        HoleValue::Const(3, ptr_slot as i64),
1650                        HoleValue::Const(5, dst as i64),
1651                        HoleValue::Cont(0, next),
1652                    ],
1653                );
1654            }
1655        }
1656        MicroOp::ArrLoad2F { dst, i: ix, j: jx, ptr_slot, len_slot, op } => {
1657            let stencil = match op {
1658                FOp::Add => &ST_ARRLD2_ADDF,
1659                FOp::Sub => &ST_ARRLD2_SUBF,
1660                FOp::Mul => &ST_ARRLD2_MULF,
1661            };
1662            buf.push_stencil(
1663                stencil,
1664                &[
1665                    HoleValue::Const(0, ix as i64),
1666                    HoleValue::Const(1, jx as i64),
1667                    HoleValue::Const(2, ptr_slot as i64),
1668                    HoleValue::Const(3, len_slot as i64),
1669                    HoleValue::Const(4, dst as i64),
1670                    HoleValue::Cont(0, next),
1671                    HoleValue::Cont(1, buf.label(deopt_piece)),
1672                ],
1673            );
1674        }
1675        MicroOp::ArrLoad2 { dst, i: ix, j: jx, ptr_a, len_a, ptr_b, len_b, op, checked } => {
1676            if checked {
1677                buf.push_stencil(
1678                    ld2_int_stencil(op, true),
1679                    &[
1680                        HoleValue::Const(0, ix as i64),
1681                        HoleValue::Const(1, jx as i64),
1682                        HoleValue::Const(2, ptr_a as i64),
1683                        HoleValue::Const(3, len_a as i64),
1684                        HoleValue::Const(4, ptr_b as i64),
1685                        HoleValue::Const(5, len_b as i64),
1686                        HoleValue::Const(6, dst as i64),
1687                        HoleValue::Cont(0, next),
1688                        HoleValue::Cont(1, buf.label(deopt_piece)),
1689                    ],
1690                );
1691            } else {
1692                buf.push_stencil(
1693                    ld2_int_stencil(op, false),
1694                    &[
1695                        HoleValue::Const(0, ix as i64),
1696                        HoleValue::Const(1, jx as i64),
1697                        HoleValue::Const(2, ptr_a as i64),
1698                        HoleValue::Const(3, ptr_b as i64),
1699                        HoleValue::Const(4, dst as i64),
1700                        HoleValue::Cont(0, next),
1701                    ],
1702                );
1703            }
1704        }
1705        MicroOp::ArrStore { src, idx, ptr_slot, len_slot, byte, narrow32, checked } => {
1706            if checked {
1707                buf.push_stencil(
1708                    arrst_stencil(byte, narrow32, true),
1709                    &[
1710                        HoleValue::Const(0, idx as i64),
1711                        HoleValue::Const(1, ptr_slot as i64),
1712                        HoleValue::Const(2, len_slot as i64),
1713                        HoleValue::Const(3, src as i64),
1714                        HoleValue::Cont(0, next),
1715                        HoleValue::Cont(1, buf.label(deopt_piece)),
1716                    ],
1717                );
1718            } else {
1719                buf.push_stencil(
1720                    arrst_stencil(byte, narrow32, false),
1721                    &[
1722                        HoleValue::Const(0, idx as i64),
1723                        HoleValue::Const(1, ptr_slot as i64),
1724                        HoleValue::Const(3, src as i64),
1725                        HoleValue::Cont(0, next),
1726                    ],
1727                );
1728            }
1729        }
1730        MicroOp::ArrRMW { idx, operand, ptr_slot, len_slot, op, checked } => {
1731            if checked {
1732                buf.push_stencil(
1733                    rmw_stencil(op, true),
1734                    &[
1735                        HoleValue::Const(0, idx as i64),
1736                        HoleValue::Const(1, ptr_slot as i64),
1737                        HoleValue::Const(2, len_slot as i64),
1738                        HoleValue::Const(3, operand as i64),
1739                        HoleValue::Cont(0, next),
1740                        HoleValue::Cont(1, buf.label(deopt_piece)),
1741                    ],
1742                );
1743            } else {
1744                buf.push_stencil(
1745                    rmw_stencil(op, false),
1746                    &[
1747                        HoleValue::Const(0, idx as i64),
1748                        HoleValue::Const(1, ptr_slot as i64),
1749                        HoleValue::Const(3, operand as i64),
1750                        HoleValue::Cont(0, next),
1751                    ],
1752                );
1753            }
1754        }
1755        MicroOp::ArrCondSwap { idx1, idx2, ptr_slot, len_slot, cmp, checked } => {
1756            if checked {
1757                buf.push_stencil(
1758                    condswap_stencil(cmp, true),
1759                    &[
1760                        HoleValue::Const(0, idx1 as i64),
1761                        HoleValue::Const(1, idx2 as i64),
1762                        HoleValue::Const(2, ptr_slot as i64),
1763                        HoleValue::Const(3, len_slot as i64),
1764                        HoleValue::Cont(0, next),
1765                        HoleValue::Cont(1, buf.label(deopt_piece)),
1766                    ],
1767                );
1768            } else {
1769                buf.push_stencil(
1770                    condswap_stencil(cmp, false),
1771                    &[
1772                        HoleValue::Const(0, idx1 as i64),
1773                        HoleValue::Const(1, idx2 as i64),
1774                        HoleValue::Const(2, ptr_slot as i64),
1775                        HoleValue::Cont(0, next),
1776                    ],
1777                );
1778            }
1779        }
1780        MicroOp::ArrSwap { idx1, idx2, ptr_slot, len_slot, checked } => {
1781            if checked {
1782                buf.push_stencil(
1783                    &ST_ARRSWAP,
1784                    &[
1785                        HoleValue::Const(0, idx1 as i64),
1786                        HoleValue::Const(1, idx2 as i64),
1787                        HoleValue::Const(2, ptr_slot as i64),
1788                        HoleValue::Const(3, len_slot as i64),
1789                        HoleValue::Cont(0, next),
1790                        HoleValue::Cont(1, buf.label(deopt_piece)),
1791                    ],
1792                );
1793            } else {
1794                buf.push_stencil(
1795                    &ST_ARRSWAP_U,
1796                    &[
1797                        HoleValue::Const(0, idx1 as i64),
1798                        HoleValue::Const(1, idx2 as i64),
1799                        HoleValue::Const(2, ptr_slot as i64),
1800                        HoleValue::Cont(0, next),
1801                    ],
1802                );
1803            }
1804        }
1805        MicroOp::FmaF { dst, a, b, c } => {
1806            buf.push_stencil(
1807                &ST_FMAF,
1808                &[
1809                    HoleValue::Const(0, a as i64),
1810                    HoleValue::Const(1, b as i64),
1811                    HoleValue::Const(2, c as i64),
1812                    HoleValue::Const(3, dst as i64),
1813                    HoleValue::Cont(0, next),
1814                ],
1815            );
1816        }
1817        // Helper-call push; refreshes the pinned ptr/len after a possible
1818        // realloc (the mem-form path reloads them from the frame after).
1819        MicroOp::ArrPush { src, vec_slot, ptr_slot, len_slot, helper_addr, byte: _, narrow32: _ } => {
1820            buf.push_stencil(
1821                &ST_PUSH,
1822                &[
1823                    HoleValue::Const(0, src as i64),
1824                    HoleValue::Const(1, vec_slot as i64),
1825                    HoleValue::Const(2, ptr_slot as i64),
1826                    HoleValue::Const(3, len_slot as i64),
1827                    HoleValue::Const(4, helper_addr),
1828                    HoleValue::Cont(0, next),
1829                ],
1830            );
1831        }
1832        ref other => unreachable!("emit_mem_form: unsupported op {other:?} (pinned chains exclude it)"),
1833    }
1834}
1835
1836/// REGISTER-THREADING compilation (EXODIA 3.1): up to four frame slots are
1837/// PINNED into the threaded registers r0..r3 for the whole chain. Pinned
1838/// operands ride registers through the generated location-variant
1839/// stencils; ops outside the variant families (Div, arrays, calls, …)
1840/// spill the pinned operands they read and reload the pinned slots they
1841/// write, so every memory-form piece still sees a coherent frame.
1842///
1843/// Frame-coherence contract: a pinned slot's FRAME cell is stale between
1844/// its spills — callers must only use pinning where nothing outside the
1845/// chain reads the frame mid-run (mode-A functions: replay re-enters from
1846/// the boundary arguments and the result returns by register).
1847pub fn compile_straightline_pinned(
1848    ops: &[MicroOp],
1849    pins: &[u16],
1850) -> Result<CompiledChain, JitCompileError> {
1851    compile_straightline_pinned_with(ops, pins, None)
1852}
1853
1854/// [`compile_straightline_pinned`] with a SHARED status cell (the function
1855/// tier's deopt/call seam). Integer/bool slots only — no XMM float pins.
1856pub fn compile_straightline_pinned_with(
1857    ops: &[MicroOp],
1858    pins: &[u16],
1859    shared_status: Option<std::sync::Arc<AtomicI64>>,
1860) -> Result<CompiledChain, JitCompileError> {
1861    compile_straightline_pinned_float(ops, pins, &[], shared_status)
1862}
1863
1864/// The full register-threading compiler with BOTH integer pins (`pins`,
1865/// threaded through the GP registers r0..r3) and FLOAT pins (`fpins`,
1866/// threaded through the XMM registers f0..f3). The two budgets are
1867/// independent — the threaded ABI carries `fn(base, sp, r0..r3, f0..f3)`, so
1868/// base/sp consume 2 of the 6 GP arg registers (leaving r0..r3) while all
1869/// four XMM pins sit in the 8 free XMM arg registers and never compete with
1870/// the integer pins.
1871///
1872/// A FLOAT slot pinned to `fN` keeps its f64 live in that XMM register across
1873/// every stencil — the pure float arith variants (V_FBINOP) read and write it
1874/// directly, and the mem-form float stencils (sqrt, divf, the float compares,
1875/// array load/store) THREAD f0..f3 unchanged, so any float pin they do not
1876/// themselves touch survives with no frame traffic. The ones a mem-form op
1877/// does read/write are spilled before / reloaded after, exactly like the GP
1878/// pins.
1879pub fn compile_straightline_pinned_float(
1880    ops: &[MicroOp],
1881    pins: &[u16],
1882    fpins: &[u16],
1883    shared_status: Option<std::sync::Arc<AtomicI64>>,
1884) -> Result<CompiledChain, JitCompileError> {
1885    if pins.is_empty() && fpins.is_empty() {
1886        return compile_straightline_coded(ops, shared_status, None, 0);
1887    }
1888    assert!(pins.len() <= 4, "at most four threaded GP registers");
1889    assert!(fpins.len() <= 6, "at most six threaded XMM registers (f0..f5)");
1890    debug_assert!(
1891        pins.iter().all(|p| !fpins.contains(p)),
1892        "a slot cannot be both GP- and float-pinned"
1893    );
1894    if ops.is_empty() {
1895        return Err(JitCompileError::Empty);
1896    }
1897    if !matches!(ops.last(), Some(MicroOp::Return { .. }) | Some(MicroOp::Jump { .. })) {
1898        return Err(JitCompileError::FallsOffTheEnd);
1899    }
1900    let loc = |slot: u16| -> usize {
1901        pins.iter().position(|&p| p == slot).map(|i| i + 1).unwrap_or(0)
1902    };
1903    // The XMM-pin location of a slot: 0 = frame, 1..4 = f0..f3.
1904    let floc = |slot: u16| -> usize {
1905        fpins.iter().position(|&p| p == slot).map(|i| i + 1).unwrap_or(0)
1906    };
1907    // Which pinned registers an op forces through MEMORY (spill before /
1908    // reload after) because it has no variant family.
1909    let mem_form_touch = |op: &MicroOp| -> (Vec<u16>, Vec<u16>) {
1910        let mut reads: Vec<u16> = Vec::new();
1911        let mut writes: Vec<u16> = Vec::new();
1912        match *op {
1913            MicroOp::Div { dst, lhs, rhs } | MicroOp::Mod { dst, lhs, rhs } => {
1914                reads.extend([lhs, rhs]);
1915                writes.push(dst);
1916            }
1917            MicroOp::DivPow2 { dst, lhs, .. } | MicroOp::MagicDivU { dst, lhs, .. } => {
1918                reads.push(lhs);
1919                writes.push(dst);
1920            }
1921            // Float add/sub/mul are register-threaded (V_FBINOP) — NOT
1922            // mem-form. DivF (div-by-zero side-exit) and the float comparisons
1923            // (no variant yet) stay mem-form.
1924            MicroOp::DivF { dst, lhs, rhs }
1925            | MicroOp::LtF { dst, lhs, rhs }
1926            | MicroOp::LtEqF { dst, lhs, rhs }
1927            | MicroOp::GtF { dst, lhs, rhs }
1928            | MicroOp::GtEqF { dst, lhs, rhs }
1929            | MicroOp::EqF { dst, lhs, rhs }
1930            | MicroOp::NeqF { dst, lhs, rhs } => {
1931                reads.extend([lhs, rhs]);
1932                writes.push(dst);
1933            }
1934            MicroOp::NotInt { dst, src }
1935            | MicroOp::NotBool { dst, src }
1936            | MicroOp::IntToFloat { dst, src }
1937            | MicroOp::SqrtF { dst, src } => {
1938                reads.push(src);
1939                writes.push(dst);
1940            }
1941            MicroOp::BranchF { lhs, rhs, .. } => reads.extend([lhs, rhs]),
1942            // ptr_slot/len_slot are read DIRECTLY from the frame by the array
1943            // stencil (or, when the array's base pointer is GP-pinned, from its
1944            // register via the RPTR variant) — never through the threaded-pin
1945            // spill path, so they are NOT listed as spill-reads here. (They are
1946            // loop-invariant; the array never moves unless pushed-to, and a
1947            // pushed array is not ptr-pinned.)
1948            MicroOp::ArrLoad { dst, idx, .. } => {
1949                reads.push(idx);
1950                writes.push(dst);
1951            }
1952            MicroOp::ArrLoadAffine { dst, a, op, b, .. } => {
1953                reads.push(a);
1954                if op != AffOp::None {
1955                    reads.push(b);
1956                }
1957                writes.push(dst);
1958            }
1959            MicroOp::ArrLoad2F { dst, i: ix, j: jx, .. } => {
1960                reads.extend([ix, jx]);
1961                writes.push(dst);
1962            }
1963            MicroOp::ArrLoad2 { dst, i: ix, j: jx, .. } => {
1964                reads.extend([ix, jx]);
1965                writes.push(dst);
1966            }
1967            MicroOp::ArrStore { src, idx, .. } => {
1968                reads.extend([src, idx]);
1969            }
1970            MicroOp::ArrRMW { idx, operand, .. } => {
1971                reads.extend([idx, operand]);
1972            }
1973            MicroOp::ArrCondSwap { idx1, idx2, .. } => {
1974                reads.extend([idx1, idx2]);
1975            }
1976            MicroOp::ArrSwap { idx1, idx2, .. } => {
1977                reads.extend([idx1, idx2]);
1978            }
1979            MicroOp::FmaF { dst, a, b, c } => {
1980                reads.extend([a, b, c]);
1981                writes.push(dst);
1982            }
1983            MicroOp::ArrPush { src, vec_slot, ptr_slot, len_slot, .. } => {
1984                reads.extend([src, vec_slot, ptr_slot, len_slot]);
1985                writes.extend([ptr_slot, len_slot]);
1986            }
1987            MicroOp::MapGet { dst, key, map_slot, .. } => {
1988                reads.extend([key, map_slot]);
1989                writes.push(dst);
1990            }
1991            MicroOp::MapSet { src, key, map_slot, .. } => {
1992                reads.extend([src, key, map_slot]);
1993            }
1994            MicroOp::MapHas { dst, key, map_slot, .. } => {
1995                reads.extend([key, map_slot]);
1996                writes.push(dst);
1997            }
1998            MicroOp::MemMem {
1999                h_ptr_slot,
2000                h_len_slot,
2001                n_ptr_slot,
2002                n_len_slot,
2003                needle_len_slot,
2004                i_slot,
2005                count_slot,
2006                ..
2007            } => {
2008                // The helper reads every input slot from the frame and writes
2009                // `i_slot`/`count_slot` back — so a pinned operand must spill
2010                // first and a pinned destination reload after. (The recognizer
2011                // runs MemMem regions UNPINNED, so in practice these retain to
2012                // empty; kept for correctness if a pin ever survives.)
2013                reads.extend([
2014                    h_ptr_slot,
2015                    h_len_slot,
2016                    n_ptr_slot,
2017                    n_len_slot,
2018                    needle_len_slot,
2019                    i_slot,
2020                    count_slot,
2021                ]);
2022                writes.extend([i_slot, count_slot]);
2023            }
2024            MicroOp::NewList { vec_slot, ptr_slot, len_slot, .. } => {
2025                writes.extend([vec_slot, ptr_slot, len_slot]);
2026            }
2027            MicroOp::ListTriple { handle_slot, vec_slot, ptr_slot, len_slot, .. } => {
2028                reads.push(handle_slot);
2029                writes.extend([vec_slot, ptr_slot, len_slot]);
2030            }
2031            MicroOp::Call { dst, .. } | MicroOp::CallSelf { dst, .. } => {
2032                // The callee window was staged through ordinary Moves; the
2033                // result lands in the frame.
2034                writes.push(dst);
2035            }
2036            MicroOp::CallSelfCopy { dst, src_start, arg_count, .. } => {
2037                // The fused stencil reads the SOURCE block from the FRAME, so
2038                // any source slot held in a pinned register must spill to its
2039                // frame cell before the copy; the result lands in the frame.
2040                reads.extend((src_start..src_start + arg_count).collect::<Vec<_>>());
2041                writes.push(dst);
2042            }
2043            _ => {}
2044        }
2045        // A touched slot needs spill/reload when it is pinned in EITHER
2046        // budget (GP r0..r3 or XMM f0..f3) — the spill/reload primitives
2047        // dispatch on which.
2048        let pinned = |s: u16| loc(s) != 0 || floc(s) != 0;
2049        reads.retain(|&r| pinned(r));
2050        reads.dedup();
2051        writes.retain(|&w| pinned(w));
2052        writes.dedup();
2053        (reads, writes)
2054    };
2055    let has_variant = |op: &MicroOp| -> bool {
2056        matches!(
2057            op,
2058            MicroOp::Add { .. }
2059                | MicroOp::Sub { .. }
2060                | MicroOp::Mul { .. }
2061                | MicroOp::BitAnd { .. }
2062                | MicroOp::BitOr { .. }
2063                | MicroOp::BitXor { .. }
2064                | MicroOp::Shl { .. }
2065                | MicroOp::Shr { .. }
2066                | MicroOp::Lt { .. }
2067                | MicroOp::LtEq { .. }
2068                | MicroOp::Eq { .. }
2069                | MicroOp::Neq { .. }
2070                | MicroOp::Gt { .. }
2071                | MicroOp::GtEq { .. }
2072                | MicroOp::Move { .. }
2073                | MicroOp::LoadConst { .. }
2074                | MicroOp::Return { .. }
2075                | MicroOp::Branch { .. }
2076                // Float add/sub/mul/div + sqrt — register-threaded through the
2077                // XMM-pin variant tables (V_FBINOP / V_DIVF / V_SQRTF), one
2078                // piece each. DivF still side-exits on a zero divisor; its
2079                // variant carries the deopt continuation.
2080                | MicroOp::AddF { .. }
2081                | MicroOp::SubF { .. }
2082                | MicroOp::MulF { .. }
2083                | MicroOp::DivF { .. }
2084                | MicroOp::SqrtF { .. }
2085                | MicroOp::IntToFloat { .. }
2086                | MicroOp::BranchF { .. }
2087        )
2088    };
2089
2090    let n_pin = pins.len() + fpins.len();
2091    // PASS 1: piece index per op (prologue reloads first).
2092    let mut op_piece: Vec<usize> = Vec::with_capacity(ops.len());
2093    let mut next_piece = n_pin;
2094    for op in ops {
2095        op_piece.push(next_piece);
2096        next_piece += match *op {
2097            // EPILOGUE (Phase 2 write-set pinning): a success exit spills every
2098            // pinned slot (GP and XMM) back to its frame cell so the VM's
2099            // write-back sees the loop-carried values — `n_pin` spill pieces +
2100            // the return.
2101            MicroOp::Return { .. } => n_pin + 1,
2102            _ if has_variant(op) => 1,
2103            MicroOp::Jump { .. } => 1,
2104            MicroOp::JumpIfFalse { cond, .. } | MicroOp::JumpIfTrue { cond, .. } => {
2105                if loc(cond) != 0 { 2 } else { 1 }
2106            }
2107            _ => {
2108                let (r, w) = mem_form_touch(op);
2109                r.len() + 1 + w.len()
2110            }
2111        };
2112    }
2113    let total_op_pieces = next_piece;
2114    let has_checked = ops.iter().any(|op| {
2115        matches!(
2116            op,
2117            // Integer add/sub/mul side-exit (`cont_1`) on signed overflow so the exact
2118            // tier promotes to BigInt — they need the status cell + deopt terminal.
2119            MicroOp::Add { .. }
2120                | MicroOp::Sub { .. }
2121                | MicroOp::Mul { .. }
2122                | MicroOp::Div { .. }
2123                | MicroOp::Mod { .. }
2124                | MicroOp::DivF { .. }
2125                | MicroOp::ArrLoad { .. }
2126                | MicroOp::ArrLoadAffine { checked: true, .. }
2127                | MicroOp::ArrLoad2F { .. }
2128                | MicroOp::ArrLoad2 { checked: true, .. }
2129                | MicroOp::ArrStore { .. }
2130                | MicroOp::ArrRMW { checked: true, .. }
2131                | MicroOp::ArrCondSwap { checked: true, .. }
2132                | MicroOp::ArrSwap { checked: true, .. }
2133                | MicroOp::Call { .. }
2134                | MicroOp::CallSelf { .. }
2135                | MicroOp::CallSelfCopy { .. }
2136                | MicroOp::MapGet { .. }
2137                | MicroOp::MemMem { .. }
2138        )
2139    });
2140    let status: Option<std::sync::Arc<AtomicI64>> = if has_checked {
2141        Some(shared_status.unwrap_or_else(|| std::sync::Arc::new(AtomicI64::new(0))))
2142    } else {
2143        None
2144    };
2145    let deopt_piece = total_op_pieces;
2146
2147    // PASS 2: emit.
2148    let mut buf = JitBuffer::new();
2149    // Prologue: reload each pinned slot from the frame — the GP pins into
2150    // r0..r3 (V_MOV), then the float pins into f0..f3 (V_FMOV). The pieces
2151    // are laid out GP-first, so piece index `i` for `i < pins.len()` reloads
2152    // GP pin `i`, and `pins.len() + j` reloads float pin `j`.
2153    for (i, &slot) in pins.iter().enumerate() {
2154        buf.push_stencil(
2155            V_MOV[i + 1][0],
2156            &[HoleValue::Const(0, slot as i64), HoleValue::Cont(0, buf.label(i + 1))],
2157        );
2158    }
2159    for (j, &slot) in fpins.iter().enumerate() {
2160        let here = pins.len() + j;
2161        buf.push_stencil(
2162            V_FMOV[j + 1][0],
2163            &[HoleValue::Const(0, slot as i64), HoleValue::Cont(0, buf.label(here + 1))],
2164        );
2165    }
2166    // Spill/reload a pinned slot — dispatching on its budget. A GP pin moves
2167    // through V_MOV (r0..r3 ↔ frame i64); a float pin through V_FMOV (f0..f3
2168    // ↔ frame f64 bits). A slot is in exactly one budget.
2169    let spill = |buf: &mut JitBuffer, slot: u16, after: usize| {
2170        if floc(slot) != 0 {
2171            buf.push_stencil(
2172                V_FMOV[0][floc(slot)],
2173                &[HoleValue::Const(1, slot as i64), HoleValue::Cont(0, buf.label(after))],
2174            );
2175        } else {
2176            buf.push_stencil(
2177                V_MOV[0][loc(slot)],
2178                &[HoleValue::Const(1, slot as i64), HoleValue::Cont(0, buf.label(after))],
2179            );
2180        }
2181    };
2182    let reload = |buf: &mut JitBuffer, slot: u16, after: usize| {
2183        if floc(slot) != 0 {
2184            buf.push_stencil(
2185                V_FMOV[floc(slot)][0],
2186                &[HoleValue::Const(0, slot as i64), HoleValue::Cont(0, buf.label(after))],
2187            );
2188        } else {
2189            buf.push_stencil(
2190                V_MOV[loc(slot)][0],
2191                &[HoleValue::Const(0, slot as i64), HoleValue::Cont(0, buf.label(after))],
2192            );
2193        }
2194    };
2195    // Jump threading: a continuation that lands on an unconditional `Jump`
2196    // follows it straight to the target, so the predecessor branches directly
2197    // and the per-iteration back-edge jmp every loop pays (`…; Jump(top)`) is
2198    // skipped. The Jump piece is still EMITTED (left as unreferenced dead code)
2199    // so piece indices — and every `op_piece[..]` label — stay valid.
2200    let no_thread = std::env::var_os("LOGOS_NOTHREAD").is_some();
2201    let thread = |start: usize| -> usize {
2202        if no_thread {
2203            return start;
2204        }
2205        let mut idx = start;
2206        for _ in 0..=ops.len() {
2207            match ops.get(idx) {
2208                Some(MicroOp::Jump { target }) => idx = *target,
2209                _ => break,
2210            }
2211        }
2212        idx
2213    };
2214    for (i, op) in ops.iter().enumerate() {
2215        let here = op_piece[i];
2216        debug_assert_eq!(
2217            buf.pieces_pushed(),
2218            here,
2219            "PASS1/PASS2 piece divergence before op {i} ({op:?}); pins={pins:?} fpins={fpins:?}"
2220        );
2221        let next_op = op_piece.get(thread(i + 1)).copied().unwrap_or(total_op_pieces);
2222        let lbl = |t: usize| -> usize { op_piece[thread(t)] };
2223        match *op {
2224            MicroOp::LoadConst { dst, value } => {
2225                let d = loc(dst);
2226                if d == 0 {
2227                    buf.push_stencil(
2228                        &ST_CONSTST,
2229                        &[
2230                            HoleValue::Const(0, value),
2231                            HoleValue::Const(1, dst as i64),
2232                            HoleValue::Cont(0, buf.label(next_op)),
2233                        ],
2234                    );
2235                } else {
2236                    buf.push_stencil(
2237                        V_CONST[d - 1],
2238                        &[HoleValue::Const(0, value), HoleValue::Cont(0, buf.label(next_op))],
2239                    );
2240                }
2241            }
2242            MicroOp::Move { dst, src } => {
2243                let (fd, fs) = (floc(dst), floc(src));
2244                if fd != 0 || fs != 0 {
2245                    // FLOAT move: at least one operand rides an XMM pin, so this
2246                    // is an f64 copy — thread it through V_FMOV (register f→f, or
2247                    // a frame↔register half when the other end is frame-resident).
2248                    // A float-pinned slot is never GP-pinned, so `loc` plays no
2249                    // part. Register ends read/write the XMM directly (no hole);
2250                    // a frame end uses hole 0 (src) or hole 1 (dst).
2251                    let mut holes: Vec<HoleValue> = Vec::new();
2252                    if fs == 0 {
2253                        holes.push(HoleValue::Const(0, src as i64));
2254                    }
2255                    if fd == 0 {
2256                        holes.push(HoleValue::Const(1, dst as i64));
2257                    }
2258                    holes.push(HoleValue::Cont(0, buf.label(next_op)));
2259                    buf.push_stencil(V_FMOV[fd][fs], &holes);
2260                } else {
2261                    let (d, sl) = (loc(dst), loc(src));
2262                    if d == 0 && sl == 0 {
2263                        buf.push_stencil(
2264                            &ST_MOVSS,
2265                            &[
2266                                HoleValue::Const(0, src as i64),
2267                                HoleValue::Const(1, dst as i64),
2268                                HoleValue::Cont(0, buf.label(next_op)),
2269                            ],
2270                        );
2271                    } else {
2272                        let mut holes: Vec<HoleValue> = Vec::new();
2273                        if sl == 0 {
2274                            holes.push(HoleValue::Const(0, src as i64));
2275                        }
2276                        if d == 0 {
2277                            holes.push(HoleValue::Const(1, dst as i64));
2278                        }
2279                        holes.push(HoleValue::Cont(0, buf.label(next_op)));
2280                        buf.push_stencil(V_MOV[d][sl], &holes);
2281                    }
2282                }
2283            }
2284            MicroOp::Add { dst, lhs, rhs }
2285            | MicroOp::Sub { dst, lhs, rhs }
2286            | MicroOp::Mul { dst, lhs, rhs }
2287            | MicroOp::BitAnd { dst, lhs, rhs }
2288            | MicroOp::BitOr { dst, lhs, rhs }
2289            | MicroOp::BitXor { dst, lhs, rhs }
2290            | MicroOp::Shl { dst, lhs, rhs }
2291            | MicroOp::Shr { dst, lhs, rhs }
2292            | MicroOp::Lt { dst, lhs, rhs }
2293            | MicroOp::LtEq { dst, lhs, rhs }
2294            | MicroOp::Eq { dst, lhs, rhs }
2295            | MicroOp::Neq { dst, lhs, rhs }
2296            | MicroOp::Gt { dst, lhs, rhs }
2297            | MicroOp::GtEq { dst, lhs, rhs } => {
2298                // Gt/GtEq map onto lt/le with swapped operands; Neq is ne.
2299                let (fam, a, b) = match *op {
2300                    MicroOp::Add { .. } => (0usize, lhs, rhs),
2301                    MicroOp::Sub { .. } => (1, lhs, rhs),
2302                    MicroOp::Mul { .. } => (2, lhs, rhs),
2303                    MicroOp::BitAnd { .. } => (3, lhs, rhs),
2304                    MicroOp::BitOr { .. } => (4, lhs, rhs),
2305                    MicroOp::BitXor { .. } => (5, lhs, rhs),
2306                    MicroOp::Shl { .. } => (6, lhs, rhs),
2307                    MicroOp::Shr { .. } => (7, lhs, rhs),
2308                    MicroOp::Lt { .. } => (8, lhs, rhs),
2309                    MicroOp::Gt { .. } => (8, rhs, lhs),
2310                    MicroOp::LtEq { .. } => (9, lhs, rhs),
2311                    MicroOp::GtEq { .. } => (9, rhs, lhs),
2312                    MicroOp::Eq { .. } => (10, lhs, rhs),
2313                    MicroOp::Neq { .. } => (11, lhs, rhs),
2314                    _ => unreachable!(),
2315                };
2316                let (d, l, r) = (loc(dst), loc(a), loc(b));
2317                let mut holes: Vec<HoleValue> = Vec::new();
2318                if l == 0 {
2319                    holes.push(HoleValue::Const(0, a as i64));
2320                }
2321                if r == 0 {
2322                    holes.push(HoleValue::Const(1, b as i64));
2323                }
2324                if d == 0 {
2325                    holes.push(HoleValue::Const(2, dst as i64));
2326                }
2327                holes.push(HoleValue::Cont(0, buf.label(next_op)));
2328                // add/sub/mul (fam 0/1/2) are the CHECKED families: their generated
2329                // stencil takes cont_1 to the shared deopt terminal on signed overflow,
2330                // so the exact tier recomputes and promotes to BigInt.
2331                if fam <= 2 {
2332                    holes.push(HoleValue::Cont(1, buf.label(deopt_piece)));
2333                }
2334                buf.push_stencil(V_BINOP[fam][d][l][r], &holes);
2335            }
2336            // FLOAT 3-address (add/sub/mul): same location encoding, float
2337            // variant table.
2338            MicroOp::AddF { dst, lhs, rhs }
2339            | MicroOp::SubF { dst, lhs, rhs }
2340            | MicroOp::MulF { dst, lhs, rhs } => {
2341                let fam = match *op {
2342                    MicroOp::AddF { .. } => 0usize,
2343                    MicroOp::SubF { .. } => 1,
2344                    MicroOp::MulF { .. } => 2,
2345                    _ => unreachable!(),
2346                };
2347                // Float operands are XMM-pinned → index by the FLOAT pin
2348                // location (floc), NOT the GP location (loc). Using loc would
2349                // resolve an XMM-pinned float to frame (location 0) and read a
2350                // STALE frame cell instead of the live f-pin.
2351                let (d, l, r) = (floc(dst), floc(lhs), floc(rhs));
2352                let mut holes: Vec<HoleValue> = Vec::new();
2353                if l == 0 {
2354                    holes.push(HoleValue::Const(0, lhs as i64));
2355                }
2356                if r == 0 {
2357                    holes.push(HoleValue::Const(1, rhs as i64));
2358                }
2359                if d == 0 {
2360                    holes.push(HoleValue::Const(2, dst as i64));
2361                }
2362                holes.push(HoleValue::Cont(0, buf.label(next_op)));
2363                buf.push_stencil(V_FBINOP[fam][d][l][r], &holes);
2364            }
2365            // FLOAT sqrt threaded through XMM pins (no side-exit).
2366            MicroOp::SqrtF { dst, src } => {
2367                let (d, s) = (floc(dst), floc(src));
2368                let mut holes: Vec<HoleValue> = Vec::new();
2369                if s == 0 {
2370                    holes.push(HoleValue::Const(0, src as i64));
2371                }
2372                if d == 0 {
2373                    holes.push(HoleValue::Const(1, dst as i64));
2374                }
2375                holes.push(HoleValue::Cont(0, buf.label(next_op)));
2376                buf.push_stencil(V_SQRTF[d][s], &holes);
2377            }
2378            // FLOAT divide threaded through XMM pins; a zero divisor side-exits
2379            // to the deopt terminal (cont 1), exactly like the mem-form form.
2380            MicroOp::DivF { dst, lhs, rhs } => {
2381                let (d, l, r) = (floc(dst), floc(lhs), floc(rhs));
2382                let mut holes: Vec<HoleValue> = Vec::new();
2383                if l == 0 {
2384                    holes.push(HoleValue::Const(0, lhs as i64));
2385                }
2386                if r == 0 {
2387                    holes.push(HoleValue::Const(1, rhs as i64));
2388                }
2389                if d == 0 {
2390                    holes.push(HoleValue::Const(2, dst as i64));
2391                }
2392                holes.push(HoleValue::Cont(0, buf.label(next_op)));
2393                holes.push(HoleValue::Cont(1, buf.label(deopt_piece)));
2394                buf.push_stencil(V_DIVF[d][l][r], &holes);
2395            }
2396            // Int→Float threaded through XMM pins: int src at GP loc, f64 dst
2397            // at XMM pin.
2398            MicroOp::IntToFloat { dst, src } => {
2399                let (d, s) = (floc(dst), loc(src));
2400                let mut holes: Vec<HoleValue> = Vec::new();
2401                if s == 0 {
2402                    holes.push(HoleValue::Const(0, src as i64));
2403                }
2404                if d == 0 {
2405                    holes.push(HoleValue::Const(1, dst as i64));
2406                }
2407                holes.push(HoleValue::Cont(0, buf.label(next_op)));
2408                buf.push_stencil(V_I2F[d][s], &holes);
2409            }
2410            // FUSED float compare-and-branch threaded through XMM pins. Same
2411            // NaN-correct cmp→family mapping as the unpinned form (brlef for
2412            // <=/>=, never brltf-swapped — NaN makes `a<=b` ≠ `!(b<a)`).
2413            MicroOp::BranchF { cmp, lhs, rhs, target } => {
2414                let t = buf.label(lbl(target));
2415                let n = buf.label(next_op);
2416                let (fam, a, b) = match cmp {
2417                    Cmp::Lt => (0usize, lhs, rhs),
2418                    Cmp::Gt => (0, rhs, lhs),
2419                    Cmp::LtEq => (1, lhs, rhs),
2420                    Cmp::GtEq => (1, rhs, lhs),
2421                    Cmp::Eq => (2, lhs, rhs),
2422                    Cmp::NotEq => (2, lhs, rhs),
2423                };
2424                let (c0, c1) = if cmp == Cmp::NotEq { (t, n) } else { (n, t) };
2425                let (l, r) = (floc(a), floc(b));
2426                let mut holes: Vec<HoleValue> = Vec::new();
2427                if l == 0 {
2428                    holes.push(HoleValue::Const(0, a as i64));
2429                }
2430                if r == 0 {
2431                    holes.push(HoleValue::Const(1, b as i64));
2432                }
2433                holes.push(HoleValue::Cont(0, c0));
2434                holes.push(HoleValue::Cont(1, c1));
2435                buf.push_stencil(V_BRANCHF[fam][l][r], &holes);
2436            }
2437            MicroOp::Branch { cmp, lhs, rhs, target } => {
2438                // TRUE -> fall through (cont 0 of the variant is TAKEN when
2439                // the condition holds; mirror the unpinned mapping: jump
2440                // when FALSE).
2441                let t = buf.label(lbl(target));
2442                let n = buf.label(next_op);
2443                let (fam, a, b, c_true, c_false) = match cmp {
2444                    Cmp::Lt => (0usize, lhs, rhs, n, t),
2445                    Cmp::GtEq => (0, lhs, rhs, t, n),
2446                    Cmp::Gt => (0, rhs, lhs, n, t),
2447                    Cmp::LtEq => (0, rhs, lhs, t, n),
2448                    Cmp::Eq => (2, lhs, rhs, n, t),
2449                    Cmp::NotEq => (2, lhs, rhs, t, n),
2450                };
2451                let (l, r) = (loc(a), loc(b));
2452                let mut holes: Vec<HoleValue> = Vec::new();
2453                if l == 0 {
2454                    holes.push(HoleValue::Const(0, a as i64));
2455                }
2456                if r == 0 {
2457                    holes.push(HoleValue::Const(1, b as i64));
2458                }
2459                holes.push(HoleValue::Cont(0, c_true));
2460                holes.push(HoleValue::Cont(1, c_false));
2461                buf.push_stencil(V_BRANCH[fam][l][r], &holes);
2462            }
2463            MicroOp::Jump { target } => {
2464                buf.push_stencil(&ST_JUMP, &[HoleValue::Cont(0, buf.label(lbl(target)))]);
2465            }
2466            MicroOp::JumpIfFalse { cond, target } | MicroOp::JumpIfTrue { cond, target } => {
2467                let c = loc(cond);
2468                let mut brz_piece = here;
2469                if c != 0 {
2470                    spill(&mut buf, cond, here + 1);
2471                    brz_piece = here + 1;
2472                }
2473                let _ = brz_piece;
2474                let (c0, c1) = if matches!(op, MicroOp::JumpIfFalse { .. }) {
2475                    (buf.label(next_op), buf.label(lbl(target)))
2476                } else {
2477                    (buf.label(lbl(target)), buf.label(next_op))
2478                };
2479                buf.push_stencil(
2480                    &ST_BRZ,
2481                    &[HoleValue::Const(0, cond as i64), HoleValue::Cont(0, c0), HoleValue::Cont(1, c1)],
2482                );
2483            }
2484            MicroOp::Return { src } => {
2485                // EPILOGUE: spill every pinned slot to its frame cell before
2486                // returning, so the VM's success write-back (which reads the
2487                // frame) observes loop-carried values held in registers. Both
2488                // budgets spill — the GP pins through V_MOV, the float pins
2489                // through V_FMOV (the `spill` closure dispatches on which) —
2490                // matching the `n_pin + 1` pieces reserved for this return.
2491                // The deopt terminal is separate and discards the frame, so it
2492                // needs no spill — only this success path does.
2493                let mut cursor = here;
2494                for &slot in pins.iter().chain(fpins.iter()) {
2495                    spill(&mut buf, slot, cursor + 1);
2496                    cursor += 1;
2497                }
2498                let sl = loc(src);
2499                if sl == 0 {
2500                    buf.push_stencil(&ST_RET2, &[HoleValue::Const(0, src as i64)]);
2501                } else {
2502                    buf.push_stencil(V_RET[sl - 1], &[]);
2503                }
2504            }
2505            ref other => {
2506                // Memory-form op: spill pinned reads, run, reload pinned
2507                // writes — emitted as its own piece run.
2508                let (reads, writes) = mem_form_touch(other);
2509                let mut cursor = here;
2510                for &slot in &reads {
2511                    spill(&mut buf, slot, cursor + 1);
2512                    cursor += 1;
2513                }
2514                let after_op = cursor + 1;
2515                let next_for_op = if writes.is_empty() { next_op } else { after_op };
2516                let next_label = buf.label(next_for_op);
2517                // REGISTER-FORM array access: a word array whose base pointer is
2518                // GP-pinned reads the pointer from its register (no per-access
2519                // frame load). The index/length/value stay in the frame exactly
2520                // as the mem-form path, so the spill/reload bookkeeping above is
2521                // unchanged (mem_form_touch excludes ptr/len). Falls back to the
2522                // frame-pointer mem-form for byte arrays / unpinned pointers.
2523                let rptr = |buf: &mut JitBuffer, table: &[&'static crate::Stencil; 5],
2524                            n: usize, idx: u16, vslot: u16, len_slot: u16, checked: bool| {
2525                    let mut holes = vec![
2526                        HoleValue::Const(0, idx as i64),
2527                        HoleValue::Const(3, vslot as i64),
2528                        HoleValue::Cont(0, next_label),
2529                    ];
2530                    if checked {
2531                        holes.push(HoleValue::Const(2, len_slot as i64));
2532                        holes.push(HoleValue::Cont(1, buf.label(deopt_piece)));
2533                    }
2534                    buf.push_stencil(table[n], &holes);
2535                };
2536                let used_rptr = match *other {
2537                    MicroOp::ArrLoad { dst, idx, ptr_slot, len_slot, byte: false, narrow32: false, checked }
2538                        if loc(ptr_slot) != 0 =>
2539                    {
2540                        let n = loc(ptr_slot);
2541                        let t = if checked { &V_ARRLD_RPTR_C } else { &V_ARRLD_RPTR };
2542                        rptr(&mut buf, t, n, idx, dst, len_slot, checked);
2543                        true
2544                    }
2545                    MicroOp::ArrStore { src, idx, ptr_slot, len_slot, byte: false, narrow32: false, checked }
2546                        if loc(ptr_slot) != 0 =>
2547                    {
2548                        let n = loc(ptr_slot);
2549                        let t = if checked { &V_ARRST_RPTR_C } else { &V_ARRST_RPTR };
2550                        rptr(&mut buf, t, n, idx, src, len_slot, checked);
2551                        true
2552                    }
2553                    _ => false,
2554                };
2555                if !used_rptr {
2556                    emit_mem_form(&mut buf, other, next_label, deopt_piece, &status);
2557                }
2558                cursor += 1;
2559                for (k, &slot) in writes.iter().enumerate() {
2560                    let after = if k + 1 == writes.len() { next_op } else { cursor + 1 };
2561                    reload(&mut buf, slot, after);
2562                    cursor += 1;
2563                }
2564            }
2565        }
2566    }
2567    if let Some(cell) = &status {
2568        let addr = cell.as_ref() as *const AtomicI64 as i64;
2569        buf.push_stencil(&ST_DEOPT, &[HoleValue::Const(0, addr)]);
2570    }
2571    let chain = buf.finish().map_err(|e| JitCompileError::Assembly(e.to_string()))?;
2572    Ok(CompiledChain { chain, status, keepalive: Vec::new() })
2573}
2574
2575/// The returned chain is run with [`CompiledChain::run_with_frame`]; the
2576/// frame's slots are the program's registers (inputs pre-loaded by the
2577/// caller, outputs visible after the run). Programs containing checked ops
2578/// (Div/Mod) get a status cell and a shared side-exit terminal; their runs
2579/// can report [`ChainOutcome::Deopt`].
2580pub fn compile_straightline(ops: &[MicroOp]) -> Result<CompiledChain, JitCompileError> {
2581    compile_straightline_with(ops, None)
2582}
2583
2584/// [`compile_straightline`] with a caller-supplied (per-program) status
2585/// cell, so every chain of one program — and the call stencils between them
2586/// — share a single side-exit channel. The cell's address is what
2587/// `MicroOp::Call::status_addr` must carry.
2588pub fn compile_straightline_with(
2589    ops: &[MicroOp],
2590    shared_status: Option<std::sync::Arc<AtomicI64>>,
2591) -> Result<CompiledChain, JitCompileError> {
2592    compile_straightline_coded(ops, shared_status, None, 0)
2593}
2594
2595/// Like [`compile_straightline_with`], with an optional per-op DEOPT CODE
2596/// table (parallel to `ops`). An op whose code is not the plain marker `1`
2597/// side-exits through its own terminal piece storing that encoded value —
2598/// the function tier encodes `(bytecode_pc << 2) | 3` so the VM can resume
2599/// precisely. `None` (and code `1`) keep the shared replay terminal.
2600pub fn compile_straightline_coded(
2601    ops: &[MicroOp],
2602    shared_status: Option<std::sync::Arc<AtomicI64>>,
2603    deopt_codes: Option<&[i64]>,
2604    depth_addr: i64,
2605) -> Result<CompiledChain, JitCompileError> {
2606    if ops.is_empty() {
2607        return Err(JitCompileError::Empty);
2608    }
2609    if !matches!(ops.last(), Some(MicroOp::Return { .. }) | Some(MicroOp::Jump { .. })) {
2610        return Err(JitCompileError::FallsOffTheEnd);
2611    }
2612    for (i, op) in ops.iter().enumerate() {
2613        match *op {
2614            MicroOp::Jump { target }
2615            | MicroOp::JumpIfFalse { target, .. }
2616            | MicroOp::JumpIfTrue { target, .. }
2617            | MicroOp::Branch { target, .. }
2618            | MicroOp::BranchF { target, .. } => {
2619                if target >= ops.len() {
2620                    return Err(JitCompileError::BadJumpTarget { op_index: i, target });
2621                }
2622            }
2623            _ => {}
2624        }
2625    }
2626
2627    // 3-address lowering: exactly ONE piece per micro-op (piece index ==
2628    // op index), so jump targets need no remapping. Programs with checked
2629    // ops share ONE side-exit terminal placed after every op piece; its
2630    // patched constant is the status cell's address.
2631    let has_checked = ops.iter().any(|op| {
2632        matches!(
2633            op,
2634            // Integer add/sub/mul side-exit (`cont_1`) on signed overflow so the exact
2635            // tier promotes to BigInt — they need the status cell + deopt terminal.
2636            MicroOp::Add { .. }
2637                | MicroOp::Sub { .. }
2638                | MicroOp::Mul { .. }
2639                | MicroOp::Div { .. }
2640                | MicroOp::Mod { .. }
2641                | MicroOp::DivF { .. }
2642                | MicroOp::ArrLoad { .. }
2643                | MicroOp::ArrLoadAffine { checked: true, .. }
2644                | MicroOp::ArrLoad2F { .. }
2645                | MicroOp::ArrLoad2 { checked: true, .. }
2646                | MicroOp::ArrStore { .. }
2647                | MicroOp::ArrRMW { checked: true, .. }
2648                | MicroOp::ArrCondSwap { checked: true, .. }
2649                | MicroOp::ArrSwap { checked: true, .. }
2650                | MicroOp::Call { .. }
2651                | MicroOp::CallSelf { .. }
2652                | MicroOp::CallSelfCopy { .. }
2653                | MicroOp::MapGet { .. }
2654                | MicroOp::MemMem { .. }
2655        )
2656    });
2657    let status: Option<std::sync::Arc<AtomicI64>> = if has_checked {
2658        Some(shared_status.unwrap_or_else(|| std::sync::Arc::new(AtomicI64::new(0))))
2659    } else {
2660        None
2661    };
2662    let deopt_piece = ops.len();
2663    // Distinct non-plain codes get their own terminal pieces after the
2664    // shared one, in first-use order (deterministic).
2665    let mut code_pieces: Vec<i64> = Vec::new();
2666    if let Some(codes) = deopt_codes {
2667        debug_assert_eq!(codes.len(), ops.len());
2668        for (i, op) in ops.iter().enumerate() {
2669            let coded = matches!(
2670                op,
2671                // add/sub/mul are CHECKED now (overflow → exact promotion): inside a
2672                // region their side-exit must carry the op's PRECISE deopt code so the
2673                // VM resumes with the right rollback, exactly like the other checked
2674                // ops — so their codes need terminal pieces too.
2675                MicroOp::Add { .. }
2676                    | MicroOp::Sub { .. }
2677                    | MicroOp::Mul { .. }
2678                    | MicroOp::Div { .. }
2679                    | MicroOp::Mod { .. }
2680                    | MicroOp::DivF { .. }
2681                    | MicroOp::ArrLoad { .. }
2682                    | MicroOp::ArrLoadAffine { checked: true, .. }
2683                    | MicroOp::ArrLoad2F { .. }
2684                    | MicroOp::ArrLoad2 { checked: true, .. }
2685                    | MicroOp::ArrStore { .. }
2686                    | MicroOp::MapGet { .. }
2687            );
2688            if coded && codes[i] != 1 && !code_pieces.contains(&codes[i]) {
2689                code_pieces.push(codes[i]);
2690            }
2691        }
2692    }
2693    let piece_for = |code: i64| -> usize {
2694        if code == 1 {
2695            deopt_piece
2696        } else {
2697            deopt_piece + 1 + code_pieces.iter().position(|&c| c == code).unwrap()
2698        }
2699    };
2700    let code_at = |i: usize| -> i64 { deopt_codes.map(|c| c[i]).unwrap_or(1) };
2701
2702    let mut buf = JitBuffer::new();
2703    for (i, op) in ops.iter().enumerate() {
2704        let next = buf.label(i + 1);
2705        match *op {
2706            MicroOp::LoadConst { dst, value } => {
2707                buf.push_stencil(
2708                    &ST_CONSTST,
2709                    &[
2710                        HoleValue::Const(0, value),
2711                        HoleValue::Const(1, dst as i64),
2712                        HoleValue::Cont(0, next),
2713                    ],
2714                );
2715            }
2716            MicroOp::Move { dst, src } => {
2717                buf.push_stencil(
2718                    &ST_MOVSS,
2719                    &[
2720                        HoleValue::Const(0, src as i64),
2721                        HoleValue::Const(1, dst as i64),
2722                        HoleValue::Cont(0, next),
2723                    ],
2724                );
2725            }
2726            MicroOp::Add { dst, lhs, rhs }
2727            | MicroOp::Sub { dst, lhs, rhs }
2728            | MicroOp::Mul { dst, lhs, rhs }
2729            | MicroOp::Lt { dst, lhs, rhs }
2730            | MicroOp::LtEq { dst, lhs, rhs }
2731            | MicroOp::Eq { dst, lhs, rhs }
2732            | MicroOp::Neq { dst, lhs, rhs } => {
2733                let stencil = match op {
2734                    MicroOp::Add { .. } => &ST_ADD3,
2735                    MicroOp::Sub { .. } => &ST_SUB3,
2736                    MicroOp::Mul { .. } => &ST_MUL3,
2737                    MicroOp::Lt { .. } => &ST_LT3,
2738                    MicroOp::LtEq { .. } => &ST_LE3,
2739                    MicroOp::Eq { .. } => &ST_EQ3,
2740                    MicroOp::Neq { .. } => &ST_NE3,
2741                    _ => unreachable!(),
2742                };
2743                let mut holes = vec![
2744                    HoleValue::Const(0, lhs as i64),
2745                    HoleValue::Const(1, rhs as i64),
2746                    HoleValue::Const(2, dst as i64),
2747                    HoleValue::Cont(0, next),
2748                ];
2749                // add/sub/mul are CHECKED: their stencil takes cont_1 to this op's
2750                // deopt terminal on signed overflow so the exact tier promotes. The
2751                // comparisons (lt/le/eq/ne) never overflow → cont_0 only.
2752                if matches!(op, MicroOp::Add { .. } | MicroOp::Sub { .. } | MicroOp::Mul { .. }) {
2753                    holes.push(HoleValue::Cont(1, buf.label(piece_for(code_at(i)))));
2754                }
2755                buf.push_stencil(stencil, &holes);
2756            }
2757            // a > b ⇔ b < a; a >= b ⇔ b <= a: swap operands, reuse lt3/le3
2758            // (IEEE-exact for the float twins too — NaN is false on both
2759            // sides of the swap).
2760            MicroOp::Gt { dst, lhs, rhs }
2761            | MicroOp::GtEq { dst, lhs, rhs }
2762            | MicroOp::GtF { dst, lhs, rhs }
2763            | MicroOp::GtEqF { dst, lhs, rhs } => {
2764                let stencil = match op {
2765                    MicroOp::Gt { .. } => &ST_LT3,
2766                    MicroOp::GtEq { .. } => &ST_LE3,
2767                    MicroOp::GtF { .. } => &ST_LTF3,
2768                    _ => &ST_LEF3,
2769                };
2770                buf.push_stencil(
2771                    stencil,
2772                    &[
2773                        HoleValue::Const(0, rhs as i64),
2774                        HoleValue::Const(1, lhs as i64),
2775                        HoleValue::Const(2, dst as i64),
2776                        HoleValue::Cont(0, next),
2777                    ],
2778                );
2779            }
2780            MicroOp::BitAnd { dst, lhs, rhs }
2781            | MicroOp::BitOr { dst, lhs, rhs }
2782            | MicroOp::BitXor { dst, lhs, rhs }
2783            | MicroOp::Shl { dst, lhs, rhs }
2784            | MicroOp::Shr { dst, lhs, rhs }
2785            | MicroOp::AddF { dst, lhs, rhs }
2786            | MicroOp::SubF { dst, lhs, rhs }
2787            | MicroOp::MulF { dst, lhs, rhs }
2788            | MicroOp::LtF { dst, lhs, rhs }
2789            | MicroOp::LtEqF { dst, lhs, rhs }
2790            | MicroOp::EqF { dst, lhs, rhs }
2791            | MicroOp::NeqF { dst, lhs, rhs } => {
2792                let stencil = match op {
2793                    MicroOp::BitAnd { .. } => &ST_AND3,
2794                    MicroOp::BitOr { .. } => &ST_OR3,
2795                    MicroOp::BitXor { .. } => &ST_XOR3,
2796                    MicroOp::Shl { .. } => &ST_SHL3,
2797                    MicroOp::Shr { .. } => &ST_SHR3,
2798                    MicroOp::AddF { .. } => &ST_ADDF3,
2799                    MicroOp::SubF { .. } => &ST_SUBF3,
2800                    MicroOp::MulF { .. } => &ST_MULF3,
2801                    MicroOp::LtF { .. } => &ST_LTF3,
2802                    MicroOp::LtEqF { .. } => &ST_LEF3,
2803                    MicroOp::EqF { .. } => &ST_EQF3,
2804                    _ => &ST_NEF3,
2805                };
2806                buf.push_stencil(
2807                    stencil,
2808                    &[
2809                        HoleValue::Const(0, lhs as i64),
2810                        HoleValue::Const(1, rhs as i64),
2811                        HoleValue::Const(2, dst as i64),
2812                        HoleValue::Cont(0, next),
2813                    ],
2814                );
2815            }
2816            MicroOp::DivF { dst, lhs, rhs } => {
2817                buf.push_stencil(
2818                    &ST_DIVF3C,
2819                    &[
2820                        HoleValue::Const(0, lhs as i64),
2821                        HoleValue::Const(1, rhs as i64),
2822                        HoleValue::Const(2, dst as i64),
2823                        HoleValue::Cont(0, next),
2824                        HoleValue::Cont(1, buf.label(piece_for(code_at(i)))),
2825                    ],
2826                );
2827            }
2828            MicroOp::Call {
2829                dst,
2830                args_start,
2831                table_addr,
2832                depth_addr,
2833                status_addr,
2834                limit_slot,
2835                depth_limit,
2836            } => {
2837                let code = code_at(i);
2838                if code != 1 {
2839                    // Precise variant: hole 6 carries the encoded deopt
2840                    // value; the depth limit is BAKED at the kernel's
2841                    // locked MAX_CALL_DEPTH (the adapter verified it).
2842                    debug_assert_eq!(depth_limit, BAKED_CALL_DEPTH);
2843                    buf.push_stencil(
2844                        &ST_CALL_PRECISE,
2845                        &[
2846                            HoleValue::Const(0, table_addr),
2847                            HoleValue::Const(1, args_start as i64),
2848                            HoleValue::Const(2, dst as i64),
2849                            HoleValue::Const(3, depth_addr),
2850                            HoleValue::Const(4, status_addr),
2851                            HoleValue::Const(5, limit_slot as i64),
2852                            HoleValue::Const(6, code),
2853                            HoleValue::Cont(0, next),
2854                        ],
2855                    );
2856                } else {
2857                    buf.push_stencil(
2858                        &ST_CALL,
2859                        &[
2860                            HoleValue::Const(0, table_addr),
2861                            HoleValue::Const(1, args_start as i64),
2862                            HoleValue::Const(2, dst as i64),
2863                            HoleValue::Const(3, depth_addr),
2864                            HoleValue::Const(4, status_addr),
2865                            HoleValue::Const(5, limit_slot as i64),
2866                            HoleValue::Const(6, depth_limit),
2867                            HoleValue::Cont(0, next),
2868                        ],
2869                    );
2870                }
2871            }
2872            MicroOp::CallSelf { dst, args_start, depth_addr, status_addr, limit_slot, frame_size } => {
2873                buf.push_stencil(
2874                    &ST_CALL_SELF,
2875                    &[
2876                        HoleValue::Const(0, 0),
2877                        HoleValue::Const(1, args_start as i64),
2878                        HoleValue::Const(2, dst as i64),
2879                        HoleValue::Const(3, depth_addr),
2880                        HoleValue::Const(4, status_addr),
2881                        HoleValue::Const(5, limit_slot as i64),
2882                        HoleValue::Const(6, frame_size),
2883                        HoleValue::Cont(0, next),
2884                    ],
2885                );
2886                buf.mark_patch_hole(0);
2887            }
2888            MicroOp::CallSelfCopy {
2889                dst,
2890                args_start,
2891                src_start,
2892                arg_count,
2893                depth_addr,
2894                status_addr,
2895                limit_slot,
2896                frame_size,
2897            } => {
2898                buf.push_stencil(
2899                    &ST_CALL_SELF_COPY,
2900                    &[
2901                        HoleValue::Const(0, 0),
2902                        HoleValue::Const(1, args_start as i64),
2903                        HoleValue::Const(2, dst as i64),
2904                        HoleValue::Const(3, depth_addr),
2905                        HoleValue::Const(4, status_addr),
2906                        HoleValue::Const(5, limit_slot as i64),
2907                        HoleValue::Const(6, frame_size),
2908                        HoleValue::Const(7, src_start as i64),
2909                        HoleValue::Const(8, arg_count as i64),
2910                        HoleValue::Cont(0, next),
2911                    ],
2912                );
2913                buf.mark_patch_hole(0);
2914            }
2915            MicroOp::NewList { vec_slot, ptr_slot, len_slot, helper_addr } => {
2916                buf.push_stencil(
2917                    &ST_ALLOCLIST,
2918                    &[
2919                        HoleValue::Const(0, vec_slot as i64),
2920                        HoleValue::Const(1, ptr_slot as i64),
2921                        HoleValue::Const(2, len_slot as i64),
2922                        HoleValue::Const(3, helper_addr),
2923                        HoleValue::Cont(0, next),
2924                    ],
2925                );
2926            }
2927            MicroOp::ListTriple { handle_slot, vec_slot, ptr_slot, len_slot, helper_addr } => {
2928                buf.push_stencil(
2929                    &ST_LISTTRIPLE,
2930                    &[
2931                        HoleValue::Const(0, handle_slot as i64),
2932                        HoleValue::Const(1, vec_slot as i64),
2933                        HoleValue::Const(2, ptr_slot as i64),
2934                        HoleValue::Const(3, len_slot as i64),
2935                        HoleValue::Const(4, helper_addr),
2936                        HoleValue::Cont(0, next),
2937                    ],
2938                );
2939            }
2940            MicroOp::MapGet { dst, key, map_slot, helper_addr } => {
2941                buf.push_stencil(
2942                    &ST_MAPHGET,
2943                    &[
2944                        HoleValue::Const(0, map_slot as i64),
2945                        HoleValue::Const(1, key as i64),
2946                        HoleValue::Const(2, dst as i64),
2947                        HoleValue::Const(3, helper_addr),
2948                        HoleValue::Cont(0, next),
2949                        HoleValue::Cont(1, buf.label(piece_for(code_at(i)))),
2950                    ],
2951                );
2952            }
2953            MicroOp::MapSet { src, key, map_slot, helper_addr } => {
2954                buf.push_stencil(
2955                    &ST_MAPHSET,
2956                    &[
2957                        HoleValue::Const(0, map_slot as i64),
2958                        HoleValue::Const(1, key as i64),
2959                        HoleValue::Const(2, src as i64),
2960                        HoleValue::Const(3, helper_addr),
2961                        HoleValue::Cont(0, next),
2962                    ],
2963                );
2964            }
2965            MicroOp::MemMem {
2966                h_ptr_slot,
2967                h_len_slot,
2968                n_ptr_slot,
2969                n_len_slot,
2970                needle_len_slot,
2971                i_slot,
2972                count_slot,
2973                helper_addr,
2974            } => {
2975                buf.push_stencil(
2976                    &ST_MEMMEM,
2977                    &[
2978                        HoleValue::Const(0, h_ptr_slot as i64),
2979                        HoleValue::Const(1, h_len_slot as i64),
2980                        HoleValue::Const(2, n_ptr_slot as i64),
2981                        HoleValue::Const(3, n_len_slot as i64),
2982                        HoleValue::Const(4, needle_len_slot as i64),
2983                        HoleValue::Const(5, i_slot as i64),
2984                        HoleValue::Const(6, count_slot as i64),
2985                        HoleValue::Const(7, helper_addr),
2986                        HoleValue::Cont(0, next),
2987                        HoleValue::Cont(1, buf.label(piece_for(code_at(i)))),
2988                    ],
2989                );
2990            }
2991            MicroOp::MapHas { dst, key, map_slot, helper_addr } => {
2992                buf.push_stencil(
2993                    &ST_MAPHHAS,
2994                    &[
2995                        HoleValue::Const(0, map_slot as i64),
2996                        HoleValue::Const(1, key as i64),
2997                        HoleValue::Const(2, dst as i64),
2998                        HoleValue::Const(3, helper_addr),
2999                        HoleValue::Cont(0, next),
3000                    ],
3001                );
3002            }
3003            MicroOp::ArrPush { src, vec_slot, ptr_slot, len_slot, helper_addr, byte: _, narrow32: _ } => {
3004                buf.push_stencil(
3005                    &ST_PUSH,
3006                    &[
3007                        HoleValue::Const(0, src as i64),
3008                        HoleValue::Const(1, vec_slot as i64),
3009                        HoleValue::Const(2, ptr_slot as i64),
3010                        HoleValue::Const(3, len_slot as i64),
3011                        HoleValue::Const(4, helper_addr),
3012                        HoleValue::Cont(0, next),
3013                    ],
3014                );
3015            }
3016            MicroOp::ListClear { vec_slot, ptr_slot, len_slot, helper_addr } => {
3017                buf.push_stencil(
3018                    &ST_LIST_CLEAR,
3019                    &[
3020                        HoleValue::Const(0, vec_slot as i64),
3021                        HoleValue::Const(1, ptr_slot as i64),
3022                        HoleValue::Const(2, len_slot as i64),
3023                        HoleValue::Const(3, helper_addr),
3024                        HoleValue::Cont(0, next),
3025                    ],
3026                );
3027            }
3028            MicroOp::ArrLoad { dst, idx, ptr_slot, len_slot, byte, narrow32, checked } => {
3029                if checked {
3030                    buf.push_stencil(
3031                        arrld_stencil(byte, narrow32, true),
3032                        &[
3033                            HoleValue::Const(0, idx as i64),
3034                            HoleValue::Const(1, ptr_slot as i64),
3035                            HoleValue::Const(2, len_slot as i64),
3036                            HoleValue::Const(3, dst as i64),
3037                            HoleValue::Cont(0, next),
3038                            HoleValue::Cont(1, buf.label(piece_for(code_at(i)))),
3039                        ],
3040                    );
3041                } else {
3042                    // Bounds-check eliminated (Oracle-proven): no length hole,
3043                    // no out-of-bounds continuation.
3044                    buf.push_stencil(
3045                        arrld_stencil(byte, narrow32, false),
3046                        &[
3047                            HoleValue::Const(0, idx as i64),
3048                            HoleValue::Const(1, ptr_slot as i64),
3049                            HoleValue::Const(3, dst as i64),
3050                            HoleValue::Cont(0, next),
3051                        ],
3052                    );
3053                }
3054            }
3055            MicroOp::ArrLoadAffine { dst, a, op, b, const_offset, ptr_slot, len_slot, checked } => {
3056                if checked {
3057                    buf.push_stencil(
3058                        affine_stencil(op, true),
3059                        &[
3060                            HoleValue::Const(0, a as i64),
3061                            HoleValue::Const(1, b as i64),
3062                            HoleValue::Const(2, const_offset),
3063                            HoleValue::Const(3, ptr_slot as i64),
3064                            HoleValue::Const(4, len_slot as i64),
3065                            HoleValue::Const(5, dst as i64),
3066                            HoleValue::Cont(0, next),
3067                            HoleValue::Cont(1, buf.label(piece_for(code_at(i)))),
3068                        ],
3069                    );
3070                } else {
3071                    buf.push_stencil(
3072                        affine_stencil(op, false),
3073                        &[
3074                            HoleValue::Const(0, a as i64),
3075                            HoleValue::Const(1, b as i64),
3076                            HoleValue::Const(2, const_offset),
3077                            HoleValue::Const(3, ptr_slot as i64),
3078                            HoleValue::Const(5, dst as i64),
3079                            HoleValue::Cont(0, next),
3080                        ],
3081                    );
3082                }
3083            }
3084            MicroOp::ArrStore { src, idx, ptr_slot, len_slot, byte, narrow32, checked } => {
3085                if checked {
3086                    buf.push_stencil(
3087                        arrst_stencil(byte, narrow32, true),
3088                        &[
3089                            HoleValue::Const(0, idx as i64),
3090                            HoleValue::Const(1, ptr_slot as i64),
3091                            HoleValue::Const(2, len_slot as i64),
3092                            HoleValue::Const(3, src as i64),
3093                            HoleValue::Cont(0, next),
3094                            HoleValue::Cont(1, buf.label(piece_for(code_at(i)))),
3095                        ],
3096                    );
3097                } else {
3098                    buf.push_stencil(
3099                        arrst_stencil(byte, narrow32, false),
3100                        &[
3101                            HoleValue::Const(0, idx as i64),
3102                            HoleValue::Const(1, ptr_slot as i64),
3103                            HoleValue::Const(3, src as i64),
3104                            HoleValue::Cont(0, next),
3105                        ],
3106                    );
3107                }
3108            }
3109            MicroOp::ArrRMW { idx, operand, ptr_slot, len_slot, op, checked } => {
3110                if checked {
3111                    buf.push_stencil(
3112                        rmw_stencil(op, true),
3113                        &[
3114                            HoleValue::Const(0, idx as i64),
3115                            HoleValue::Const(1, ptr_slot as i64),
3116                            HoleValue::Const(2, len_slot as i64),
3117                            HoleValue::Const(3, operand as i64),
3118                            HoleValue::Cont(0, next),
3119                            HoleValue::Cont(1, buf.label(piece_for(code_at(i)))),
3120                        ],
3121                    );
3122                } else {
3123                    buf.push_stencil(
3124                        rmw_stencil(op, false),
3125                        &[
3126                            HoleValue::Const(0, idx as i64),
3127                            HoleValue::Const(1, ptr_slot as i64),
3128                            HoleValue::Const(3, operand as i64),
3129                            HoleValue::Cont(0, next),
3130                        ],
3131                    );
3132                }
3133            }
3134            MicroOp::ArrCondSwap { idx1, idx2, ptr_slot, len_slot, cmp, checked } => {
3135                if checked {
3136                    buf.push_stencil(
3137                        condswap_stencil(cmp, true),
3138                        &[
3139                            HoleValue::Const(0, idx1 as i64),
3140                            HoleValue::Const(1, idx2 as i64),
3141                            HoleValue::Const(2, ptr_slot as i64),
3142                            HoleValue::Const(3, len_slot as i64),
3143                            HoleValue::Cont(0, next),
3144                            HoleValue::Cont(1, buf.label(piece_for(code_at(i)))),
3145                        ],
3146                    );
3147                } else {
3148                    buf.push_stencil(
3149                        condswap_stencil(cmp, false),
3150                        &[
3151                            HoleValue::Const(0, idx1 as i64),
3152                            HoleValue::Const(1, idx2 as i64),
3153                            HoleValue::Const(2, ptr_slot as i64),
3154                            HoleValue::Cont(0, next),
3155                        ],
3156                    );
3157                }
3158            }
3159            MicroOp::ArrSwap { idx1, idx2, ptr_slot, len_slot, checked } => {
3160                if checked {
3161                    buf.push_stencil(
3162                        &ST_ARRSWAP,
3163                        &[
3164                            HoleValue::Const(0, idx1 as i64),
3165                            HoleValue::Const(1, idx2 as i64),
3166                            HoleValue::Const(2, ptr_slot as i64),
3167                            HoleValue::Const(3, len_slot as i64),
3168                            HoleValue::Cont(0, next),
3169                            HoleValue::Cont(1, buf.label(piece_for(code_at(i)))),
3170                        ],
3171                    );
3172                } else {
3173                    buf.push_stencil(
3174                        &ST_ARRSWAP_U,
3175                        &[
3176                            HoleValue::Const(0, idx1 as i64),
3177                            HoleValue::Const(1, idx2 as i64),
3178                            HoleValue::Const(2, ptr_slot as i64),
3179                            HoleValue::Cont(0, next),
3180                        ],
3181                    );
3182                }
3183            }
3184            MicroOp::FmaF { dst, a, b, c } => {
3185                buf.push_stencil(
3186                    &ST_FMAF,
3187                    &[
3188                        HoleValue::Const(0, a as i64),
3189                        HoleValue::Const(1, b as i64),
3190                        HoleValue::Const(2, c as i64),
3191                        HoleValue::Const(3, dst as i64),
3192                        HoleValue::Cont(0, next),
3193                    ],
3194                );
3195            }
3196            MicroOp::ArrLoad2F { dst, i: ix, j: jx, ptr_slot, len_slot, op } => {
3197                let stencil = match op {
3198                    FOp::Add => &ST_ARRLD2_ADDF,
3199                    FOp::Sub => &ST_ARRLD2_SUBF,
3200                    FOp::Mul => &ST_ARRLD2_MULF,
3201                };
3202                buf.push_stencil(
3203                    stencil,
3204                    &[
3205                        HoleValue::Const(0, ix as i64),
3206                        HoleValue::Const(1, jx as i64),
3207                        HoleValue::Const(2, ptr_slot as i64),
3208                        HoleValue::Const(3, len_slot as i64),
3209                        HoleValue::Const(4, dst as i64),
3210                        HoleValue::Cont(0, next),
3211                        HoleValue::Cont(1, buf.label(piece_for(code_at(i)))),
3212                    ],
3213                );
3214            }
3215            MicroOp::ArrLoad2 { dst, i: ix, j: jx, ptr_a, len_a, ptr_b, len_b, op, checked } => {
3216                if checked {
3217                    buf.push_stencil(
3218                        ld2_int_stencil(op, true),
3219                        &[
3220                            HoleValue::Const(0, ix as i64),
3221                            HoleValue::Const(1, jx as i64),
3222                            HoleValue::Const(2, ptr_a as i64),
3223                            HoleValue::Const(3, len_a as i64),
3224                            HoleValue::Const(4, ptr_b as i64),
3225                            HoleValue::Const(5, len_b as i64),
3226                            HoleValue::Const(6, dst as i64),
3227                            HoleValue::Cont(0, next),
3228                            HoleValue::Cont(1, buf.label(piece_for(code_at(i)))),
3229                        ],
3230                    );
3231                } else {
3232                    buf.push_stencil(
3233                        ld2_int_stencil(op, false),
3234                        &[
3235                            HoleValue::Const(0, ix as i64),
3236                            HoleValue::Const(1, jx as i64),
3237                            HoleValue::Const(2, ptr_a as i64),
3238                            HoleValue::Const(3, ptr_b as i64),
3239                            HoleValue::Const(4, dst as i64),
3240                            HoleValue::Cont(0, next),
3241                        ],
3242                    );
3243                }
3244            }
3245            MicroOp::NotInt { dst, src } | MicroOp::NotBool { dst, src }
3246            | MicroOp::IntToFloat { dst, src } | MicroOp::SqrtF { dst, src } => {
3247                let stencil = match op {
3248                    MicroOp::NotInt { .. } => &ST_NOTI2,
3249                    MicroOp::NotBool { .. } => &ST_NOTB2,
3250                    MicroOp::SqrtF { .. } => &ST_SQRTF2,
3251                    _ => &ST_I2F2,
3252                };
3253                buf.push_stencil(
3254                    stencil,
3255                    &[
3256                        HoleValue::Const(0, src as i64),
3257                        HoleValue::Const(1, dst as i64),
3258                        HoleValue::Cont(0, next),
3259                    ],
3260                );
3261            }
3262            MicroOp::BranchF { cmp, lhs, rhs, target } => {
3263                // Jump to target when cmp is FALSE — under IEEE, the false
3264                // path also catches every NaN-unordered pair, so the mapping
3265                // may ONLY swap operands (a>b ⇔ b<a; a>=b ⇔ b<=a), never
3266                // continuations on the truth side.
3267                let t = buf.label(target);
3268                let (stencil, a, b) = match cmp {
3269                    Cmp::Lt => (&ST_BRLTF, lhs, rhs),
3270                    Cmp::Gt => (&ST_BRLTF, rhs, lhs),
3271                    Cmp::LtEq => (&ST_BRLEF, lhs, rhs),
3272                    Cmp::GtEq => (&ST_BRLEF, rhs, lhs),
3273                    Cmp::Eq => (&ST_BREQF, lhs, rhs),
3274                    Cmp::NotEq => (&ST_BREQF, lhs, rhs),
3275                };
3276                let (c0, c1) = if cmp == Cmp::NotEq {
3277                    // NotEq: jump when eps-equal (the TRUE path of breqf).
3278                    (t, next)
3279                } else {
3280                    (next, t)
3281                };
3282                buf.push_stencil(
3283                    stencil,
3284                    &[
3285                        HoleValue::Const(0, a as i64),
3286                        HoleValue::Const(1, b as i64),
3287                        HoleValue::Cont(0, c0),
3288                        HoleValue::Cont(1, c1),
3289                    ],
3290                );
3291            }
3292            MicroOp::Div { dst, lhs, rhs } | MicroOp::Mod { dst, lhs, rhs } => {
3293                let stencil = match op {
3294                    MicroOp::Div { .. } => &ST_DIV3C,
3295                    _ => &ST_MOD3C,
3296                };
3297                buf.push_stencil(
3298                    stencil,
3299                    &[
3300                        HoleValue::Const(0, lhs as i64),
3301                        HoleValue::Const(1, rhs as i64),
3302                        HoleValue::Const(2, dst as i64),
3303                        HoleValue::Cont(0, next),
3304                        HoleValue::Cont(1, buf.label(piece_for(code_at(i)))),
3305                    ],
3306                );
3307            }
3308            MicroOp::DivPow2 { dst, lhs, k } => {
3309                buf.push_stencil(
3310                    &ST_DIVPOW2,
3311                    &[
3312                        HoleValue::Const(0, lhs as i64),
3313                        HoleValue::Const(1, k as i64),
3314                        HoleValue::Const(2, (1i64 << k) - 1),
3315                        HoleValue::Const(3, dst as i64),
3316                        HoleValue::Cont(0, next),
3317                    ],
3318                );
3319            }
3320            MicroOp::Jump { target } => {
3321                buf.push_stencil(&ST_JUMP, &[HoleValue::Cont(0, buf.label(target))]);
3322            }
3323            MicroOp::JumpIfFalse { cond, target } => {
3324                // brz: nonzero → cont 0 (fall through), zero → cont 1 (target).
3325                buf.push_stencil(
3326                    &ST_BRZ,
3327                    &[
3328                        HoleValue::Const(0, cond as i64),
3329                        HoleValue::Cont(0, next),
3330                        HoleValue::Cont(1, buf.label(target)),
3331                    ],
3332                );
3333            }
3334            MicroOp::JumpIfTrue { cond, target } => {
3335                // Same stencil, swapped continuations: nonzero → target.
3336                buf.push_stencil(
3337                    &ST_BRZ,
3338                    &[
3339                        HoleValue::Const(0, cond as i64),
3340                        HoleValue::Cont(0, buf.label(target)),
3341                        HoleValue::Cont(1, next),
3342                    ],
3343                );
3344            }
3345            MicroOp::Branch { cmp, lhs, rhs, target } => {
3346                // Jump to target when cmp is FALSE; fall through when TRUE.
3347                // brlt/breq put the TRUE outcome on cont 0; operand and
3348                // continuation swaps express all six comparison kinds.
3349                let t = buf.label(target);
3350                let (stencil, a, b, c0, c1) = match cmp {
3351                    Cmp::Lt => (&ST_BRLT, lhs, rhs, next, t),
3352                    Cmp::GtEq => (&ST_BRLT, lhs, rhs, t, next),
3353                    Cmp::Gt => (&ST_BRLT, rhs, lhs, next, t),
3354                    Cmp::LtEq => (&ST_BRLT, rhs, lhs, t, next),
3355                    Cmp::Eq => (&ST_BREQ, lhs, rhs, next, t),
3356                    Cmp::NotEq => (&ST_BREQ, lhs, rhs, t, next),
3357                };
3358                buf.push_stencil(
3359                    stencil,
3360                    &[
3361                        HoleValue::Const(0, a as i64),
3362                        HoleValue::Const(1, b as i64),
3363                        HoleValue::Cont(0, c0),
3364                        HoleValue::Cont(1, c1),
3365                    ],
3366                );
3367            }
3368            MicroOp::Return { src } => {
3369                buf.push_stencil(&ST_RET2, &[HoleValue::Const(0, src as i64)]);
3370            }
3371            // The mutable-Text append has NO per-piece stencil — it is emitted
3372            // ONLY for the contiguous regalloc backend (where it lowers to a
3373            // SysV helper call). Decline so the caller falls back to bytecode,
3374            // which is always correct.
3375            MicroOp::StrAppend { .. } => {
3376                return Err(JitCompileError::Unsupported("StrAppend"));
3377            }
3378            // The magic reciprocal: a single `mul`-high + shift piece (the
3379            // contiguous regalloc backend also lowers it, but the stencil tier
3380            // owns the mixed regions regalloc declines — e.g. a `% c` next to a
3381            // map insert). Holes: 0 = lhs, 1 = magic, 2 = more, 3 = mul_back,
3382            // 4 = dst. No deopt continuation (a literal `c > 0` never faults).
3383            MicroOp::MagicDivU { dst, lhs, magic, more, mul_back } => {
3384                buf.push_stencil(
3385                    &ST_MAGICDIV,
3386                    &[
3387                        HoleValue::Const(0, lhs as i64),
3388                        HoleValue::Const(1, magic as i64),
3389                        HoleValue::Const(2, more as i64),
3390                        HoleValue::Const(3, mul_back),
3391                        HoleValue::Const(4, dst as i64),
3392                        HoleValue::Cont(0, next),
3393                    ],
3394                );
3395            }
3396        }
3397    }
3398
3399    if let Some(cell) = &status {
3400        let addr = cell.as_ref() as *const AtomicI64 as i64;
3401        buf.push_stencil(&ST_DEOPT, &[HoleValue::Const(0, addr)]);
3402        for &code in &code_pieces {
3403            buf.push_stencil(
3404                &ST_DEOPT_AT,
3405                &[
3406                    HoleValue::Const(0, addr),
3407                    HoleValue::Const(1, code),
3408                    HoleValue::Const(2, depth_addr),
3409                ],
3410            );
3411        }
3412    }
3413
3414    let chain = buf.finish().map_err(|e| JitCompileError::Assembly(e.to_string()))?;
3415    Ok(CompiledChain { chain, status, keepalive: Vec::new() })
3416}
3417
3418/// Evaluate the unsigned magic reciprocal for [`MicroOp::MagicDivU`]:
3419/// `x / c` when `mul_back == 0`, else `x % c` (`mul_back == c`). The dividend is
3420/// reinterpreted as `u64`, sound because the op is emitted ONLY for a proven
3421/// non-negative `x` (the i64 bit pattern equals the value, and unsigned and
3422/// signed-truncating `/`/`%` agree there). The remainder is `x - q*c` in
3423/// wrapping i64 (its low 64 bits equal the u64 difference). The `more` encoding
3424/// is the `logicaffeine_data::LogosDivU64` one: low 6 bits = shift, `0x40`
3425/// = the 65-bit add-marker path, `0x80` = the pure-shift power-of-two path.
3426#[inline]
3427pub fn magic_eval(x: i64, magic: u64, more: u8, mul_back: i64) -> i64 {
3428    const SHIFT_MASK: u8 = 0x3F;
3429    const ADD_MARKER: u8 = 0x40;
3430    const SHIFT_PATH: u8 = 0x80;
3431    let n = x as u64;
3432    let q = if more & SHIFT_PATH != 0 {
3433        n >> (more & SHIFT_MASK)
3434    } else {
3435        let hi = (((magic as u128) * (n as u128)) >> 64) as u64;
3436        if more & ADD_MARKER != 0 {
3437            let t = (n.wrapping_sub(hi) >> 1).wrapping_add(hi);
3438            t >> (more & SHIFT_MASK)
3439        } else {
3440            hi >> (more & SHIFT_MASK)
3441        }
3442    };
3443    if mul_back == 0 {
3444        q as i64
3445    } else {
3446        x.wrapping_sub((q as i64).wrapping_mul(mul_back))
3447    }
3448}
3449
3450/// The reference evaluator — the independent model every differential runs
3451/// against. Deliberately the dumbest possible implementation: a pc loop.
3452/// `fuel` bounds the step count (None when caught in a loop that long).
3453/// A zero divisor also yields None — the reference's model of the chain's
3454/// side exit (differentials feed nonzero divisors when comparing values, and
3455/// assert Deopt agreement when not).
3456pub fn reference_eval(ops: &[MicroOp], frame: &mut [i64], mut fuel: u64) -> Option<i64> {
3457    let mut pc = 0usize;
3458    while pc < ops.len() {
3459        if fuel == 0 {
3460            return None;
3461        }
3462        fuel -= 1;
3463        match ops[pc] {
3464            MicroOp::LoadConst { dst, value } => frame[dst as usize] = value,
3465            MicroOp::Move { dst, src } => frame[dst as usize] = frame[src as usize],
3466            // EXACT integer arithmetic: signed overflow returns None — the JIT's
3467            // side-exit (deopt) is the matching signal, so the differential stays
3468            // consistent (overflow ⟺ reference None ⟺ JIT deopt).
3469            MicroOp::Add { dst, lhs, rhs } => {
3470                frame[dst as usize] = frame[lhs as usize].checked_add(frame[rhs as usize])?
3471            }
3472            MicroOp::Sub { dst, lhs, rhs } => {
3473                frame[dst as usize] = frame[lhs as usize].checked_sub(frame[rhs as usize])?
3474            }
3475            MicroOp::Mul { dst, lhs, rhs } => {
3476                frame[dst as usize] = frame[lhs as usize].checked_mul(frame[rhs as usize])?
3477            }
3478            MicroOp::Div { dst, lhs, rhs } => {
3479                let b = frame[rhs as usize];
3480                // Zero divisor AND the `i64::MIN / -1` overflow both deopt —
3481                // the exact tier recomputes (and promotes to BigInt).
3482                if b == 0 || (b == -1 && frame[lhs as usize] == i64::MIN) {
3483                    return None;
3484                }
3485                frame[dst as usize] = frame[lhs as usize].wrapping_div(b)
3486            }
3487            MicroOp::DivPow2 { dst, lhs, k } => {
3488                let x = frame[lhs as usize];
3489                let mask = (1i64 << k) - 1;
3490                frame[dst as usize] = x.wrapping_add((x >> 63) & mask) >> k;
3491            }
3492            MicroOp::MagicDivU { dst, lhs, magic, more, mul_back } => {
3493                frame[dst as usize] = magic_eval(frame[lhs as usize], magic, more, mul_back);
3494            }
3495            MicroOp::Mod { dst, lhs, rhs } => {
3496                let b = frame[rhs as usize];
3497                if b == 0 {
3498                    return None;
3499                }
3500                frame[dst as usize] = frame[lhs as usize].wrapping_rem(b)
3501            }
3502            MicroOp::Lt { dst, lhs, rhs } => {
3503                frame[dst as usize] = (frame[lhs as usize] < frame[rhs as usize]) as i64
3504            }
3505            MicroOp::Gt { dst, lhs, rhs } => {
3506                frame[dst as usize] = (frame[lhs as usize] > frame[rhs as usize]) as i64
3507            }
3508            MicroOp::LtEq { dst, lhs, rhs } => {
3509                frame[dst as usize] = (frame[lhs as usize] <= frame[rhs as usize]) as i64
3510            }
3511            MicroOp::GtEq { dst, lhs, rhs } => {
3512                frame[dst as usize] = (frame[lhs as usize] >= frame[rhs as usize]) as i64
3513            }
3514            MicroOp::Eq { dst, lhs, rhs } => {
3515                frame[dst as usize] = (frame[lhs as usize] == frame[rhs as usize]) as i64
3516            }
3517            MicroOp::Neq { dst, lhs, rhs } => {
3518                frame[dst as usize] = (frame[lhs as usize] != frame[rhs as usize]) as i64
3519            }
3520            MicroOp::BitAnd { dst, lhs, rhs } => {
3521                frame[dst as usize] = frame[lhs as usize] & frame[rhs as usize]
3522            }
3523            MicroOp::BitOr { dst, lhs, rhs } => {
3524                frame[dst as usize] = frame[lhs as usize] | frame[rhs as usize]
3525            }
3526            MicroOp::BitXor { dst, lhs, rhs } => {
3527                frame[dst as usize] = frame[lhs as usize] ^ frame[rhs as usize]
3528            }
3529            MicroOp::Shl { dst, lhs, rhs } => {
3530                frame[dst as usize] = frame[lhs as usize].wrapping_shl(frame[rhs as usize] as u32)
3531            }
3532            MicroOp::Shr { dst, lhs, rhs } => {
3533                frame[dst as usize] = frame[lhs as usize].wrapping_shr(frame[rhs as usize] as u32)
3534            }
3535            MicroOp::NotInt { dst, src } => frame[dst as usize] = !frame[src as usize],
3536            MicroOp::NotBool { dst, src } => frame[dst as usize] = frame[src as usize] ^ 1,
3537            MicroOp::AddF { dst, lhs, rhs } => {
3538                let a = f64::from_bits(frame[lhs as usize] as u64);
3539                let b = f64::from_bits(frame[rhs as usize] as u64);
3540                frame[dst as usize] = (a + b).to_bits() as i64
3541            }
3542            MicroOp::SubF { dst, lhs, rhs } => {
3543                let a = f64::from_bits(frame[lhs as usize] as u64);
3544                let b = f64::from_bits(frame[rhs as usize] as u64);
3545                frame[dst as usize] = (a - b).to_bits() as i64
3546            }
3547            MicroOp::MulF { dst, lhs, rhs } => {
3548                let a = f64::from_bits(frame[lhs as usize] as u64);
3549                let b = f64::from_bits(frame[rhs as usize] as u64);
3550                frame[dst as usize] = (a * b).to_bits() as i64
3551            }
3552            MicroOp::DivF { dst, lhs, rhs } => {
3553                let b = f64::from_bits(frame[rhs as usize] as u64);
3554                if b == 0.0 {
3555                    return None;
3556                }
3557                let a = f64::from_bits(frame[lhs as usize] as u64);
3558                frame[dst as usize] = (a / b).to_bits() as i64
3559            }
3560            MicroOp::LtF { dst, lhs, rhs } => {
3561                let a = f64::from_bits(frame[lhs as usize] as u64);
3562                let b = f64::from_bits(frame[rhs as usize] as u64);
3563                frame[dst as usize] = (a < b) as i64
3564            }
3565            MicroOp::GtF { dst, lhs, rhs } => {
3566                let a = f64::from_bits(frame[lhs as usize] as u64);
3567                let b = f64::from_bits(frame[rhs as usize] as u64);
3568                frame[dst as usize] = (a > b) as i64
3569            }
3570            MicroOp::LtEqF { dst, lhs, rhs } => {
3571                let a = f64::from_bits(frame[lhs as usize] as u64);
3572                let b = f64::from_bits(frame[rhs as usize] as u64);
3573                frame[dst as usize] = (a <= b) as i64
3574            }
3575            MicroOp::GtEqF { dst, lhs, rhs } => {
3576                let a = f64::from_bits(frame[lhs as usize] as u64);
3577                let b = f64::from_bits(frame[rhs as usize] as u64);
3578                frame[dst as usize] = (a >= b) as i64
3579            }
3580            // IEEE float equality (`NaN != NaN`) — identical to the
3581            // tree-walker/VM `values_equal` and the compiled backend.
3582            MicroOp::EqF { dst, lhs, rhs } => {
3583                let a = f64::from_bits(frame[lhs as usize] as u64);
3584                let b = f64::from_bits(frame[rhs as usize] as u64);
3585                frame[dst as usize] = (a == b) as i64
3586            }
3587            MicroOp::NeqF { dst, lhs, rhs } => {
3588                let a = f64::from_bits(frame[lhs as usize] as u64);
3589                let b = f64::from_bits(frame[rhs as usize] as u64);
3590                frame[dst as usize] = (a != b) as i64
3591            }
3592            MicroOp::BranchF { cmp, lhs, rhs, target } => {
3593                let a = f64::from_bits(frame[lhs as usize] as u64);
3594                let b = f64::from_bits(frame[rhs as usize] as u64);
3595                let truth = match cmp {
3596                    Cmp::Lt => a < b,
3597                    Cmp::Gt => a > b,
3598                    Cmp::LtEq => a <= b,
3599                    Cmp::GtEq => a >= b,
3600                    Cmp::Eq => a == b,
3601                    Cmp::NotEq => a != b,
3602                };
3603                if !truth {
3604                    pc = target;
3605                    continue;
3606                }
3607            }
3608            MicroOp::IntToFloat { dst, src } => {
3609                frame[dst as usize] = (frame[src as usize] as f64).to_bits() as i64
3610            }
3611            MicroOp::SqrtF { dst, src } => {
3612                frame[dst as usize] =
3613                    f64::from_bits(frame[src as usize] as u64).sqrt().to_bits() as i64
3614            }
3615            // Calls, pushes and map traffic touch live runtime state the
3616            // reference cannot model — VM-level differentials own their
3617            // coverage.
3618            MicroOp::Call { .. }
3619            | MicroOp::CallSelf { .. }
3620            | MicroOp::CallSelfCopy { .. }
3621            | MicroOp::ArrPush { .. }
3622            | MicroOp::ListClear { .. }
3623            | MicroOp::StrAppend { .. }
3624            | MicroOp::MapGet { .. }
3625            | MicroOp::MapSet { .. }
3626            | MicroOp::MapHas { .. }
3627            | MicroOp::NewList { .. }
3628            | MicroOp::ListTriple { .. } => return None,
3629            MicroOp::ArrLoad { dst, idx, ptr_slot, len_slot, byte, narrow32, checked } => {
3630                let i = frame[idx as usize];
3631                let len = frame[len_slot as usize];
3632                let im1 = i.wrapping_sub(1);
3633                // The reference keeps the bounds guard either way (an
3634                // unchecked load that the Oracle proved in-bounds is in
3635                // range by hypothesis; refusing OOB here is just safety).
3636                let _ = checked;
3637                if (im1 as u64) >= (len as u64) {
3638                    return None;
3639                }
3640                // SAFETY: differentials pin a live buffer, like the chain.
3641                frame[dst as usize] = unsafe {
3642                    if byte {
3643                        *(frame[ptr_slot as usize] as *const u8).add(im1 as usize) as i64
3644                    } else if narrow32 {
3645                        *(frame[ptr_slot as usize] as *const i32).add(im1 as usize) as i64
3646                    } else {
3647                        *(frame[ptr_slot as usize] as *const i64).add(im1 as usize)
3648                    }
3649                };
3650            }
3651            MicroOp::ArrLoadAffine { dst, a, op, b, const_offset, ptr_slot, len_slot, checked } => {
3652                let _ = checked;
3653                let idx = op.eval(frame[a as usize], frame[b as usize], const_offset);
3654                let len = frame[len_slot as usize];
3655                let im1 = idx.wrapping_sub(1);
3656                if (im1 as u64) >= (len as u64) {
3657                    return None;
3658                }
3659                // SAFETY: differentials pin a live 8-byte buffer, like the chain.
3660                frame[dst as usize] =
3661                    unsafe { *(frame[ptr_slot as usize] as *const i64).add(im1 as usize) };
3662            }
3663            MicroOp::ArrLoad2F { dst, i: ix, j: jx, ptr_slot, len_slot, op } => {
3664                let len = frame[len_slot as usize];
3665                let im1 = frame[ix as usize].wrapping_sub(1);
3666                let jm1 = frame[jx as usize].wrapping_sub(1);
3667                if (im1 as u64) >= (len as u64) || (jm1 as u64) >= (len as u64) {
3668                    return None;
3669                }
3670                // SAFETY: differentials pin a live 8-byte buffer, like the chain.
3671                let (a, b) = unsafe {
3672                    let ptr = frame[ptr_slot as usize] as *const i64;
3673                    (
3674                        f64::from_bits(*ptr.add(im1 as usize) as u64),
3675                        f64::from_bits(*ptr.add(jm1 as usize) as u64),
3676                    )
3677                };
3678                frame[dst as usize] = op.eval(a, b).to_bits() as i64;
3679            }
3680            MicroOp::ArrLoad2 { dst, i: ix, j: jx, ptr_a, len_a, ptr_b, len_b, op, checked } => {
3681                let _ = checked;
3682                let lena = frame[len_a as usize];
3683                let lenb = frame[len_b as usize];
3684                let im1 = frame[ix as usize].wrapping_sub(1);
3685                let jm1 = frame[jx as usize].wrapping_sub(1);
3686                if (im1 as u64) >= (lena as u64) || (jm1 as u64) >= (lenb as u64) {
3687                    return None;
3688                }
3689                // SAFETY: differentials pin live 8-byte buffers, like the chain.
3690                let (a, b) = unsafe {
3691                    let pa = frame[ptr_a as usize] as *const i64;
3692                    let pb = frame[ptr_b as usize] as *const i64;
3693                    (*pa.add(im1 as usize), *pb.add(jm1 as usize))
3694                };
3695                frame[dst as usize] = op.eval(a, b);
3696            }
3697            MicroOp::ArrStore { src, idx, ptr_slot, len_slot, byte, narrow32, checked } => {
3698                // The reference always validates — a sound proof never trips it.
3699                let _ = checked;
3700                let i = frame[idx as usize];
3701                let len = frame[len_slot as usize];
3702                let im1 = i.wrapping_sub(1);
3703                if (im1 as u64) >= (len as u64) {
3704                    return None;
3705                }
3706                // SAFETY: see ArrLoad.
3707                unsafe {
3708                    if byte {
3709                        *(frame[ptr_slot as usize] as *mut u8).add(im1 as usize) =
3710                            (frame[src as usize] != 0) as u8;
3711                    } else if narrow32 {
3712                        *(frame[ptr_slot as usize] as *mut i32).add(im1 as usize) =
3713                            frame[src as usize] as i32;
3714                    } else {
3715                        *(frame[ptr_slot as usize] as *mut i64).add(im1 as usize) =
3716                            frame[src as usize];
3717                    }
3718                }
3719            }
3720            MicroOp::ArrRMW { idx, operand, ptr_slot, len_slot, op, checked } => {
3721                let _ = checked;
3722                let i = frame[idx as usize];
3723                let len = frame[len_slot as usize];
3724                let im1 = i.wrapping_sub(1);
3725                if (im1 as u64) >= (len as u64) {
3726                    return None;
3727                }
3728                // SAFETY: see ArrLoad — a live 8-byte buffer is pinned.
3729                unsafe {
3730                    let cell = (frame[ptr_slot as usize] as *mut i64).add(im1 as usize);
3731                    *cell = op.eval(*cell, frame[operand as usize]);
3732                }
3733            }
3734            MicroOp::ArrCondSwap { idx1, idx2, ptr_slot, len_slot, cmp, checked } => {
3735                let _ = checked;
3736                let len = frame[len_slot as usize];
3737                let m1 = frame[idx1 as usize].wrapping_sub(1);
3738                let m2 = frame[idx2 as usize].wrapping_sub(1);
3739                if (m1 as u64) >= (len as u64) || (m2 as u64) >= (len as u64) {
3740                    return None;
3741                }
3742                // SAFETY: see ArrLoad — a live 8-byte buffer is pinned.
3743                unsafe {
3744                    let ptr = frame[ptr_slot as usize] as *mut i64;
3745                    let a = *ptr.add(m1 as usize);
3746                    let b = *ptr.add(m2 as usize);
3747                    if cmp.eval(a, b) {
3748                        *ptr.add(m1 as usize) = b;
3749                        *ptr.add(m2 as usize) = a;
3750                    }
3751                }
3752            }
3753            MicroOp::ArrSwap { idx1, idx2, ptr_slot, len_slot, checked } => {
3754                let _ = checked;
3755                let len = frame[len_slot as usize];
3756                let m1 = frame[idx1 as usize].wrapping_sub(1);
3757                let m2 = frame[idx2 as usize].wrapping_sub(1);
3758                if (m1 as u64) >= (len as u64) || (m2 as u64) >= (len as u64) {
3759                    return None;
3760                }
3761                // SAFETY: see ArrLoad — a live 8-byte buffer is pinned.
3762                unsafe {
3763                    let ptr = frame[ptr_slot as usize] as *mut i64;
3764                    let a = *ptr.add(m1 as usize);
3765                    let b = *ptr.add(m2 as usize);
3766                    *ptr.add(m1 as usize) = b;
3767                    *ptr.add(m2 as usize) = a;
3768                }
3769            }
3770            MicroOp::FmaF { dst, a, b, c } => {
3771                let av = f64::from_bits(frame[a as usize] as u64);
3772                let bv = f64::from_bits(frame[b as usize] as u64);
3773                let cv = f64::from_bits(frame[c as usize] as u64);
3774                frame[dst as usize] = ((av * bv) + cv).to_bits() as i64;
3775            }
3776            MicroOp::Jump { target } => {
3777                pc = target;
3778                continue;
3779            }
3780            MicroOp::JumpIfFalse { cond, target } => {
3781                if frame[cond as usize] == 0 {
3782                    pc = target;
3783                    continue;
3784                }
3785            }
3786            MicroOp::JumpIfTrue { cond, target } => {
3787                if frame[cond as usize] != 0 {
3788                    pc = target;
3789                    continue;
3790                }
3791            }
3792            MicroOp::Branch { cmp, lhs, rhs, target } => {
3793                if !cmp.eval(frame[lhs as usize], frame[rhs as usize]) {
3794                    pc = target;
3795                    continue;
3796                }
3797            }
3798            MicroOp::MemMem {
3799                h_ptr_slot,
3800                h_len_slot,
3801                n_ptr_slot,
3802                n_len_slot,
3803                needle_len_slot,
3804                i_slot,
3805                count_slot,
3806                ..
3807            } => {
3808                // Reference model of the naive-search collapse: count overlapping
3809                // matches over `[i, h_len - needle_len + 1]`, add to count, and
3810                // advance `i` to the exit value — the same contract the runtime
3811                // helper implements. A needle index past its buffer would deopt
3812                // (the nest's checked needle `Index`): None, like the divisors.
3813                let h_len = frame[h_len_slot as usize];
3814                let n_buf_len = frame[n_len_slot as usize];
3815                let needle_len = frame[needle_len_slot as usize];
3816                let start = frame[i_slot as usize];
3817                if needle_len > n_buf_len {
3818                    return None;
3819                }
3820                let bound = h_len - needle_len + 1;
3821                let mut count = 0i64;
3822                if needle_len == 0 {
3823                    if start <= bound {
3824                        count = bound - start + 1;
3825                    }
3826                } else {
3827                    // SAFETY: the pinned byte buffers are live for the eval.
3828                    let hay = frame[h_ptr_slot as usize] as *const u8;
3829                    let ndl = frame[n_ptr_slot as usize] as *const u8;
3830                    let mut p = start; // 1-based
3831                    while p <= bound {
3832                        let mut m = true;
3833                        for j in 0..needle_len {
3834                            let hb = unsafe { *hay.add((p + j - 1) as usize) };
3835                            let nb = unsafe { *ndl.add(j as usize) };
3836                            if hb != nb {
3837                                m = false;
3838                                break;
3839                            }
3840                        }
3841                        if m {
3842                            count += 1;
3843                        }
3844                        p += 1;
3845                    }
3846                }
3847                frame[count_slot as usize] += count;
3848                frame[i_slot as usize] = core::cmp::max(start, bound + 1);
3849            }
3850            MicroOp::Return { src } => return Some(frame[src as usize]),
3851        }
3852        pc += 1;
3853    }
3854    unreachable!("validated programs cannot fall off the end")
3855}