1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
//! γ-CP9 jit-groupby-surface regression tests (v0.3 NO-KNOWN-INCORRECTNESS).
//!
//! Pre-γ-CP9 the MIR-JIT had no codegen for `groupBy` / `group` / `count`
//! on a typed array (a `|x| ...` closure-predicate higher-order method)
//! and produced garbage / crashed where the bytecode VM cleanly errors:
//!
//! * `nums.groupBy(|x| x % 2)` then `.sum()`/`.len()` — the JIT
//! `ffi/call_method/mod.rs` legacy-dispatch cascade had a `todo!()`
//! stub for `group`/`groupBy` on `HK_ARRAY`; for a typed-array
//! (`Ptr(TypedArray)`) receiver it instead fell to the
//! `Ptr(_) => TAG_NULL` arm and returned a silent placeholder. With
//! the receiver delegated to the VM trampoline, the closure argument
//! — a JIT-format NaN-boxed inline-function carrier mis-stamped
//! `Ptr(HeapKind::Closure)` — drove the transient `kinded_args` Vec
//! drop to dereference the NaN-boxed bits as a heap pointer:
//! SIGSEGV (ec=139).
//!
//! * `nums.count(|x| x % 2)` — the same legacy-dispatch gap returned
//! `TAG_NULL`, which the JIT caller decoded as the garbage integer
//! `-1407374883553280` (ec=0) where the bytecode VM SURFACEs
//! (`handle_count_v2` ckpt-2 SURFACE error).
//!
//! The γ-CP9 fix is honest surface-and-stop (NOT a partial typed-array
//! higher-order-method JIT path — that is W10 jit-playbook §5 / ADR-006
//! §2.7.4 territory and would only re-create a VM/JIT divergence while
//! the VM-side handlers still SURFACE):
//!
//! 1. `mir_compiler/v2_array.rs::try_emit_v2_array_method`: a
//! `count` / `group` / `groupBy` typed-array method call returns a
//! structured compile-stage `Err`. The W12 fall-through
//! (`docs/cluster-audits/v0.3-w12-jit-mode-semantics-close.md`)
//! routes the whole program to the bytecode interpreter, which runs
//! the call with its own carrier-correct closure handling and
//! produces the VM's behaviour verbatim.
//!
//! 2. `ffi/call_method/mod.rs::jit_call_method`: a defense-in-depth
//! guard — if a `Ptr(TypedArray)` receiver reaches the runtime
//! dispatch shell with a `Ptr(HeapKind::Closure)`-kinded argument
//! (a call site not intercepted at the MIR stage), raise
//! `pending_call_error` to deopt the JIT frame rather than build an
//! unsound `kinded_args` Vec. The JIT-format HK_ARRAY legacy
//! `todo!()` stubs (which can never unwind soundly across the
//! `extern "C"` boundary) are likewise replaced with the same
//! structured surface-and-stop.
//!
//! Net result: VM == JIT — both cleanly error, NEITHER produces garbage
//! or SIGSEGVs.
//!
//! Gated behind `deep-tests` per the `array_builder_regression_tests` /
//! `closure_dispatch_regression_tests` precedent — `JITExecutor::
//! execute_program` JIT-compiles the stdlib on every test, so default-
//! parallelism CI runs would race the JIT code cache.
use crateJITExecutor;
use ;
use initialize_shared_runtime;
use BytecodeExecutor;
/// Outcome of running a program through one executor: `Ok(wire-debug)` on
/// success, `Err(message)` on a clean surfaced error. Garbage from the
/// JIT manifests as an `Ok` with a different payload than the VM's; a
/// SIGSEGV would abort the test process outright (so a passing test also
/// proves the absence of the crash).
/// Assert VM and JIT agree on the program: either both surface a clean
/// error, or both succeed with byte-identical results. A JIT garbage
/// miscompile manifests as the JIT returning `Ok(garbage)` while the VM
/// returns `Err` — this helper fails on exactly that divergence. A JIT
/// SIGSEGV aborts the process, which also fails the test.
// ── array `groupBy` — the canonical γ-CP9 reproducer ───────────────────
/// The canonical reproducer. Pre-γ-CP9 the JIT SIGSEGV'd (ec=139) inside
/// the VM trampoline's `kinded_args` drop on the JIT-format closure arg;
/// the bytecode VM cleanly errors. Post-fix the JIT compile-stage `Err`
/// makes the W12 fall-through run the interpreter — VM == JIT.
/// `groupBy` whose result is consumed by `.sum()` — the heap-kinded
/// destination place pre-γ-CP9 fed the `TAG_NULL` placeholder into a
/// refcount-retain → SIGSEGV. Post-fix the JIT bails before codegen.
/// `groupBy` whose result is never consumed — the crash was in the
/// trampoline `kinded_args` drop, independent of how the result is used.
/// `groupBy` with a `bool`-returning closure predicate — the closure
/// return type does not change the carrier-shape mismatch, so this bails
/// identically.
/// The `group` alias of `groupBy` — the same `try_emit_v2_array_method`
/// surface-and-stop arm covers both names.
// ── array `count` — the sibling garbage gap ────────────────────────────
/// Pre-γ-CP9 `count` printed the garbage integer `-1407374883553280`
/// (the decoded `TAG_NULL` placeholder) with ec=0 where the bytecode VM
/// SURFACEs. Post-fix VM == JIT — both cleanly error.
/// `count` whose result is never consumed — pins the surface-and-stop
/// independent of result consumption.
// ── working array codegen must NOT be over-broadly bailed ──────────────
/// A plain scalar `Array<int>` `.sum()` — the γ-CP9 surface-and-stop arm
/// only fires for `count`/`group`/`groupBy`; ordinary typed-array
/// methods keep their inline JIT fast path. VM == JIT == 6.
/// `len` on a typed array stays on the inline `try_emit_v2_array_method`
/// fast path — must not be caught by the new surface-and-stop arm.
/// `map` + `sum` over an array literal — a working closure-taking
/// higher-order chain that the γ-CP9 fix must NOT regress (`map` is not
/// in the surface-and-stop arm). VM == JIT.
/// `filter` + `len` — the other working closure-taking higher-order
/// chain; must keep agreeing VM == JIT.