[{"data":1,"prerenderedAt":122},["ShallowReactive",2],{"news-list":3},{"articles":4,"failed":17},[5,19,27,36,45,52,60,67,73,80,87,94,101,108,115],{"slug":6,"title":7,"excerpt":8,"content":9,"date":10,"category":11,"readTime":12,"author":13,"tags":14,"featured":17,"visibility":18},"xcx-4-4-the-interpreter-determinism-update","XCX 4.4 — The Interpreter & Determinism Update","XCX 4.4 is a performance and determinism release: the interpreter loop suite runs 17.3% faster, code generation is fully deterministic, fib(30) drops to 6.53 ms in JIT mode (the LuaJIT\u002FNode class), the json.parse leaks are fixed, a memory watchdog guards runaway processes, and the language cleanup begins ahead of 5.0a.","\u003Cp>XCX 4.4 is a performance and determinism release built on top of the HIR foundation introduced in 4.2: the interpreter posts its biggest optimization pass since 4.0 (loop suite -17.3% without JIT), code generation is now fully deterministic, shallow self-recursion inlining drops fib(30) to 6.53 ms in JIT mode (the LuaJIT\u002FNode class), the json.parse leaks are fixed, a memory watchdog guards runaway processes, and a documented-behavior audit closed every gap between the docs and the runtime. On the language side, the \u003Ccode>elf\u003C\u002Fcode>\u002F\u003Ccode>els\u003C\u002Fcode> aliases are gone and block semicolons are now optional.\u003C\u002Fp>\n\n\u003Ch2>VM: interpreter (--no-jit) performance\u003C\u002Fh2>\n\u003Cp>Targeted, VM-internal optimizations of the interpreter hot path. No bytecode-format changes, no architectural redesign.\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>[NEW]\u003C\u002Fstrong> Global variable access no longer takes a lock: reads\u002Fwrites go through a cached raw pointer to the fixed-size globals array (65536 slots, never resized), the same unsynchronized model the JIT path already used. Refcount discipline preserved; \u003Ccode>SetName\u003C\u002Fcode> keeps the locked path since it can register new names.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[NEW]\u003C\u002Fstrong> All six comparison opcodes gained a dedicated int-int fast path comparing raw 64-bit payloads directly, instead of ranking both operands first.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[REF]\u003C\u002Fstrong> Constant-pool names are resolved once per constant index instead of on every execution.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[NEW]\u003C\u002Fstrong> Array index get\u002Fset skip the lock acquire when the executor register uniquely owns the array; the shared\u002Flocked path is unchanged.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[NEW]\u003C\u002Fstrong> String append appends bytes directly from the source allocation when safe, instead of always cloning through an intermediate buffer first.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[REF]\u003C\u002Fstrong> The dispatch loop polls the shutdown flag every 1024 instructions instead of every instruction.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>JIT: deterministic code generation\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> The same binary used to produce two speed classes at random (~50\u002F50 split) across process runs, because global\u002Flocal variable ordering came from a randomly-seeded hash set. Switched to a deterministic ordered set; spill\u002Freload emission now sorts by index. The register-allocation lottery is gone.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>JSON: single-pass deserialization\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[NEW]\u003C\u002Fstrong> JSON values deserialize directly into the runtime's object representation instead of building an intermediate generic tree and re-walking it.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Key ordering and duplicate-handling semantics preserved exactly as before.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[REF]\u003C\u002Fstrong> Parsing a string argument now borrows the payload instead of copying it, and skips a wasted cache-lookup hash for payloads too large to be cached.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>JIT: loop-header alignment\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[NEW]\u003C\u002Fstrong> Hot loop headers are aligned to 64-byte boundaries (via a throwaway probe build + padding), removing a code-layout dependent slowdown where a loop's speed depended on unrelated code emitted before it.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> JIT compile time roughly doubles for chunks containing loops (extra probe build). Runtime semantics unchanged.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>HTTP: request body size limit\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[NEW]\u003C\u002Fstrong> Request bodies over \u003Cstrong>10 MB\u003C\u002Fstrong> are rejected with \u003Ccode>413 Payload Too Large\u003C\u002Fcode> before the handler runs.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[REF]\u003C\u002Fstrong> Requests with a declared \u003Ccode>Content-Length\u003C\u002Fcode> over the limit are rejected without reading the body; undeclared\u002Fchunked requests are bounded to limit+1 bytes.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>Runtime: memory watchdog + json.parse retention fix\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[NEW]\u003C\u002Fstrong> A background watchdog polls process memory every 100 ms and terminates the process with a fatal error if it crosses a configurable ceiling (default 8 GB, overridable via \u003Ccode>XCX_MAX_RAM_MB\u003C\u002Fcode>, 0 disables it).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> A \u003Ccode>json.parse\u003C\u002Fcode> reference-counting bug leaked the entire parse tree on every call (interpreter and JIT-FFI paths), plus leaks on \u003Ccode>.get()\u003C\u002Fcode>\u002F\u003Ccode>.first()\u003C\u002Fcode>\u002Fcustom-get results. Five parses of a 6.4 MB payload (100k users), peak private commit, \u003Ccode>--no-jit\u003C\u002Fcode>: 329.3 → 86.2 MB.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[KNOWN ISSUE, deferred]\u003C\u002Fstrong> A related JIT-mode leak (loop-carried heap values across loop back-edges) is not yet fixed; the watchdog remains the interim protection.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>Compiler: .len() alias\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> \u003Ccode>.len()\u003C\u002Fcode> was documented as an alias of \u003Ccode>.size()\u003C\u002Fcode>\u002F\u003Ccode>.count()\u003C\u002Fcode> on arrays but was never implemented; it now works end-to-end, including json (object + array), set, and map member access.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> \u003Ccode>map.isEmpty()\u003C\u002Fcode> was documented but not implemented; now it works.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>Fixes: shutdown flag, halt semantics, method-argument limit\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> A duplicated shutdown flag meant cooperative (Ctrl-C) shutdown could never actually trigger; consolidated into one flag.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Method calls with more than 16 arguments no longer crash the process; they are rejected at compile time with a clear error.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Network request rejections (SSRF guard) no longer raise raw crashes; they follow proper halt semantics (fatal\u002Ferror) consistently between JIT and interpreter.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Invalid JSON in \u003Ccode>json.parse()\u003C\u002Fcode> no longer raises a raw crash; a proper documented fatal error, consistent across both execution modes.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Halt diagnostics (fatal\u002Ferror\u002Falert) are worded identically between JIT and interpreter modes.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Running the CLI on a non-existent file now exits with a failure code instead of silently succeeding.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>HIR: shallow self-recursion inlining\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[NEW]\u003C\u002Fstrong> Simple leaf self-recursive functions (like \u003Ccode>fib\u003C\u002Fcode>) can now be partially inlined, cutting call overhead significantly.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> The recursion depth limit still applies but is measured in physical (post-inlining) frames, so equivalent programs may recurse deeper before hitting it.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>Documented-behavior audit fixes\u003C\u002Fh2>\n\u003Cp>A round of fixes for behavior that was documented but not implemented (or implemented differently), found via a scenario-based audit:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Date \u003Ccode>.hour\u003C\u002Fcode>\u002F\u003Ccode>.minute\u003C\u002Fcode> consistently use local time; default date formatting and local-midnight storage corrected to match documentation.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> \u003Ccode>string.size()\u003C\u002Fcode> is now a compile-time error, matching the documented string API.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Database \u003Ccode>.remove(table).where(filter)\u003C\u002Fcode> is implemented as documented.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Database \u003Ccode>@optional\u003C\u002Fcode> columns with SQL NULL materialize as the column type's proper default instead of always \u003Ccode>false\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> HTTP response objects include the documented \u003Ccode>text\u003C\u002Fcode> field in all cases (success, error status, connection failure).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Adding floats to strings in JIT mode (\u003Ccode>\"x\" + 0.5\u003C\u002Fcode>) could silently produce garbage instead of concatenation; fixed to match interpreter behavior.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> \u003Ccode>.toStr()\u003C\u002Fcode> on certain scalar values incorrectly returned \u003Ccode>false\u003C\u002Fcode> in JIT mode; fixed.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> JSON mutation through \u003Ccode>.get(i)\u003C\u002Fcode> could silently fail to persist, and \u003Ccode>.bind()\u003C\u002Fcode> used as an expression did nothing; both fixed, mutations now propagate.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Recovering from a \u003Ccode>halt.error\u003C\u002Fcode> inside a typed function now correctly yields the type's default value.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Sequential \u003Ccode>for\u003C\u002Fcode> loops over fibers could corrupt fiber state and crash on a later \u003Ccode>.isDone()\u003C\u002Fcode>; fixed by always allocating a fresh loop variable.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>Note (design decision, not a bug):\u003C\u002Fstrong> iterating a fiber with \u003Ccode>for\u003C\u002Fcode> intentionally yields the fiber's final return value as its last iteration item.\u003C\u002Fp>\n\n\u003Ch2>Crash-style failures + HTTP handler serialization\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> \u003Ccode>.toInt()\u003C\u002Fcode>\u002F\u003Ccode>.toFloat()\u003C\u002Fcode> on a non-numeric string crashed the process with a raw Rust panic in JIT mode; now the same \u003Ccode>halt.error\u003C\u002Fcode> as the interpreter, and failing casts count toward the error tally in both modes (exit 1).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> \u003Ccode>push\u003C\u002Fcode> through a JSON path that does not resolve to an array raised a raw panic; now a proper \u003Ccode>halt.error\u003C\u002Fcode> (\"push target is not an array\") in both modes.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Concurrent HTTP requests raced on shared VM state: a counter handler under 400 parallel requests lost 29 updates. Handler execution is serialized; request I\u002FO stays parallel. Documented trade-off: handlers do not run in parallel with each other, and a handler calling the server's own endpoint from inside would deadlock; full concurrent-state support is a 5.0a design decision.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> The JIT \u003Ccode>json.parse\u003C\u002Fcode> cache matched entries by string pointer alone; after allocator address reuse it could return a different document's tree. Entries now match by content only. Verified: 400\u002F400 unique counter values under full concurrent load.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>Cleanup: duplicate caches and dead VM modules\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> The JIT maintained a second, private \u003Ccode>json.parse\u003C\u002Fcode> cache with pointer-keyed lookup that could return a freed-and-reused document's tree. The JIT now uses the interpreter's parse path directly: one cache, one set of semantics.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> Removed the unused legacy \u003Ccode>vm\u002Fframe\u003C\u002Fcode> and \u003Ccode>vm\u002Fstack\u003C\u002Fcode> modules, remnants of the old stack-based design that nothing instantiates anymore.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> JIT seed type-inference no longer invents operand facts; arithmetic results derive from operands only.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>JIT: globals written inside a function are now visible after the call\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> A function writing a global was invisible after return: \u003Ccode>func bump() { g = g + 1; }\u003C\u002Fcode> called three times left \u003Ccode>g = 0\u003C\u002Fcode> under the JIT. Callers now refresh cached globals after every user-function call, narrowed to the callee's transitive global-slot write set; callees that write no globals add zero reload cost.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> JIT parameter type inference could back-propagate a wrong type onto a parameter (a \u003Ccode>string\u003C\u002Fcode> typed \u003Ccode>Bool\u003C\u002Fcode>), corrupting spill-time tag reconstruction and misrouting dispatch. Parameters are now seeded from their declared signature types.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>Halt parity: JIT fast paths follow interpreter error semantics\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> \u003Ccode>\"str\".replace(\"\", x)\u003C\u002Fcode> silently performed a Rust replace instead of raising R307; out-of-bounds \u003Ccode>slice\u003C\u002Fcode> returned an empty string instead of R303; \u003Ccode>map.get\u003C\u002Fcode> on a missing key returned \u003Ccode>0\u003C\u002Fcode> instead of \u003Ccode>halt.error\u003C\u002Fcode>. All three now match the interpreter (same codes, same messages, error tally incremented).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Fallible JIT fast paths (\u003Ccode>replace\u003C\u002Fcode>, \u003Ccode>slice\u003C\u002Fcode>, \u003Ccode>map.get\u003C\u002Fcode>, out-of-bounds array branches) now check the error tally like the generic dispatch; in-bounds array get\u002Fset stay poll-free, so loop-heavy benchmarks are unaffected.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>Database and tables: @default(v) honored everywhere\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> \u003Ccode>@default(v)\u003C\u002Fcode> was reduced to a boolean flag before code generation, so \u003Ccode>CREATE TABLE\u003C\u002Fcode> carried no DEFAULT clause: inserts stored SQL NULL, \u003Ccode>NULL &lt; x\u003C\u002Fcode> filters never matched, arithmetic on such columns returned NULL, and string defaults read back empty. The declared constant now reaches the SQL schema (\u003Ccode>DEFAULT 0\u003C\u002Fcode>, \u003Ccode>DEFAULT 'user'\u003C\u002Fcode>, …).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> In-memory table rows filled omitted \u003Ccode>@default(v)\u003C\u002Fcode> cells with a bare \u003Ccode>false\u003C\u002Fcode> regardless of column type; omitted cells now take the declared default, matching the SQL side.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>JIT diagnostics, net error typing, and spill-store elision\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> JIT code-finalization failures now report the chunk name and the real Cranelift\u002Fmodule error; the guessed \"likely FreeBSD W^X policy\" wording (misleading on Windows) is gone. Fallback-to-interpreter behavior unchanged.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> \u003Ccode>net.*\u003C\u002Fcode> with a malformed URL is no longer reported as an SSRF attack (plain INVALID error); real SSRF detections keep their exact halt semantics (\u003Ccode>file:\u002F\u002F\u003C\u002Fcode> and link-local → halt.fatal, private ranges → halt.error).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[NEW]\u003C\u002Fstrong> \u003Ccode>spill_all\u003C\u002Fcode> dead-store elision: a dirty register never read anywhere in the chunk and statically holding a scalar skips its memory store at every call\u002Fdispatch boundary.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>Old-defect fixes: global string append ownership + bare math constants\u003C\u002Fh2>\n\u003Cp>Both defects reproduce identically on the 4.2 and 4.3 release binaries.\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Appending to a global string (\u003Ccode>s = s + x\u003C\u002Fcode>, \u003Ccode>--no-jit\u003C\u002Fcode>) could mutate a buffer shared with the chunk's constant pool: a string initialized from a literal after another string had been appended started life containing that string's content. The in-place append now requires sole buffer ownership, and the re-allocation path releases the construction reference.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Bare math constants (\u003Ccode>PI\u003C\u002Fcode>, \u003Ccode>E\u003C\u002Fcode>, \u003Ccode>TAU\u003C\u002Fcode>, \u003Ccode>PHI\u003C\u002Fcode>, \u003Ccode>SQRT2\u003C\u002Fcode>, \u003Ccode>LN2\u003C\u002Fcode>, \u003Ccode>LN10\u003C\u002Fcode>, \u003Ccode>INF\u003C\u002Fcode>, \u003Ccode>INT_MAX\u003C\u002Fcode>, \u003Ccode>INT_MIN\u003C\u002Fcode>) failed to resolve in any program containing at least one aliased \u003Ccode>include\u003C\u002Fcode>. The include expander now rewrites such names only towards an aliased module that actually declares the symbol.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>Language: elf and els keyword aliases removed\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> The conditional keyword aliases \u003Ccode>elf\u003C\u002Fcode> (of \u003Ccode>elseif\u003C\u002Fcode>) and \u003Ccode>els\u003C\u002Fcode> (of \u003Ccode>else\u003C\u002Fcode>) no longer parse; code using them fails with an unknown-identifier error. \u003Ccode>elseif\u003C\u002Fcode>\u002F\u003Ccode>elif\u003C\u002Fcode>\u002F\u003Ccode>else\u003C\u002Fcode> remain — exactly one optional short form per keyword. Update affected sources by replacing \u003Ccode>elf\u003C\u002Fcode> with \u003Ccode>elseif\u003C\u002Fcode> (or \u003Ccode>elif\u003C\u002Fcode>) and \u003Ccode>els\u003C\u002Fcode> with \u003Ccode>else\u003C\u002Fcode>.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>Language: block semicolons are optional, bare style recommended\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> The semicolon after block syntax is now optional everywhere: after \u003Ccode>then\u003C\u002Fcode> (in \u003Ccode>if\u003C\u002Fcode>\u002F\u003Ccode>elseif\u003C\u002Fcode>), after \u003Ccode>do\u003C\u002Fcode> (in \u003Ccode>while\u003C\u002Fcode>\u002F\u003Ccode>for\u003C\u002Fcode>), after \u003Ccode>else\u003C\u002Fcode>, after \u003Ccode>end\u003C\u002Fcode>, and after the closing \u003Ccode>}\u003C\u002Fcode> of \u003Ccode>func\u003C\u002Fcode>\u002F\u003Ccode>fiber\u003C\u002Fcode> definitions and \u003Ccode>{ }\u003C\u002Fcode>-shaped declarations. Previously only \u003Ccode>if\u003C\u002Fcode>'s \u003Ccode>then\u003C\u002Fcode> accepted the bare form.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Recommended style:\u003C\u002Fstrong> write blocks without the decorative semicolon: \u003Ccode>if (ok) then … end\u003C\u002Fcode>, \u003Ccode>func f() { … }\u003C\u002Fcode>. Statement separators inside block bodies are unchanged and still required.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[PLANNED 5.0a]\u003C\u002Fstrong> The semicolon form is removed in 5.0a; the bare form is its only successor and 5.0a ships an automated migration script.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>Verification\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Ccode>cargo test --release\u003C\u002Fcode>: 251 tests \u002F 21 files, 0 failed (incl. RUN-004 R307 under the JIT); scenario suite 104\u002F104 passed; every \u003Ccode>programs\u002F\u003C\u002Fcode> suite passes in JIT and \u003Ccode>--no-jit\u003C\u002Fcode>.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>Performance\u003C\u002Fh2>\n\u003Cp>Official benchmark run, Main Suite, XCX 4.4 vs XCX 4.3 (JIT and \u003Ccode>--no-jit\u003C\u002Fcode>):\u003C\u002Fp>\n\u003Cdiv class=\"docs-table-wrap\">\u003Ctable class=\"docs-table\">\n\u003Cthead>\u003Ctr>\u003Cth>Suite\u003C\u002Fth>\u003Cth>FIB (30)\u003C\u002Fth>\u003Cth>LCG (100M)\u003C\u002Fth>\u003Cth>SIEVE\u003C\u002Fth>\u003Cth>JSON\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\u003Ctd>XCX 4.3, JIT\u003C\u002Ftd>\u003Ctd>11.11 ms\u003C\u002Ftd>\u003Ctd>107.81 ms\u003C\u002Ftd>\u003Ctd>36.542 ms\u003C\u002Ftd>\u003Ctd>0.69 ms\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Cstrong>XCX 4.4, JIT\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>6.53 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>105.87 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>34.287 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>0.55 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>XCX 4.3, --no-jit\u003C\u002Ftd>\u003Ctd>202.93 ms\u003C\u002Ftd>\u003Ctd>4303.23 ms\u003C\u002Ftd>\u003Ctd>2043.74 ms\u003C\u002Ftd>\u003Ctd>0.86 ms\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Cstrong>XCX 4.4, --no-jit\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>179.41 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>4265.23 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>2031.74 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>0.61 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cul>\n\u003Cli>\u003Cstrong>JIT mode improved on all four tests:\u003C\u002Fstrong> fib -41.2% (shallow self-recursion inlining), lcg -1.8%, sieve -6.2%, json -20.3%.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Interpreter mode improved on all four tests:\u003C\u002Fstrong> fib -11.6%, lcg -0.9%, sieve -0.6%, json -29.1%. The headline interpreter result of the cycle is the loop suite: total \u003Cstrong>-17.3%\u003C\u002Fstrong> vs 4.3.\u003C\u002Fli>\n\u003Cli>Full comparison against 26 languages\u002Fruntimes (XCX 4.4 ranks 11th by geometric mean): see the table in the README on GitHub.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>Known issues at the end of the 4.4 cycle\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>JIT code finalization can fail intermittently on Windows\u003C\u002Fstrong> — the process then continues in interpreter mode (results correct, just slower) and logs the real Cranelift error. The fallback is safe and unchanged.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>json.parse leak in JIT mode\u003C\u002Fstrong> when the parse result is carried across a loop back-edge (~48 MB per iteration). Workaround: parse outside the loop; the memory watchdog terminates runaway processes. The fix lands in 5.0a.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>HTTP handler execution is serialized by design\u003C\u002Fstrong> (I\u002FO parallel, handlers one at a time); full concurrent-state support is a deliberate 5.0a design decision.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Map keys containing \".\" \u002F \"[\" or starting with \"\u002F\" are interpreted as nested paths\u003C\u002Fstrong>, not literal keys; key such values by a safe slug (5.0a breaking-change decision with migration tooling).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Residual code-layout sensitivity\u003C\u002Fstrong>: a hot loop can still land in one of two layouts across process runs (~1.7× flip); for strict benchmarking take a minimum across a few launches.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>A while loop over a function-local limit variable silently decrements the limit\u003C\u002Fstrong> (workaround: copy the limit to a fresh local before the loop); scheduled for a fix in 5.0a.\u003C\u002Fli>\n\u003C\u002Ful>","Sep 16, 2026","releases","4 min read","XCX Core Team",[15,16],"news","release",false,"public",{"slug":20,"title":21,"excerpt":22,"content":23,"date":24,"category":11,"readTime":25,"author":13,"tags":26,"featured":17,"visibility":18},"xcx-4-3-the-method-jit-hardening-updatearticle-title","XCX 4.3 — The Method JIT & Hardening Update","Refcount elision in the JIT, hardened SSRF checks (full private-range coverage), unbounded JSON depth, precise parameter type inference, and a big dead-code cleanup (~2900 lines removed).\n","\u003Cp>XCX 4.3 is a correctness, stability and security release built on top of the HIR foundation introduced in 4.2: the JSON nesting limit is gone, raw JSON literals now work in every expression position, the JIT falls back to the interpreter automatically instead of dying, SSRF protection is unified between the JIT and the interpreter, the VM lost a lot of verified dead code, and the sieve benchmark runs 62.7% faster under JIT.\u003C\u002Fp>\n\u003Ch2>JSON: nesting-depth limit lifted (json_recursion_limit)\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> \u003Ccode>json.parse()\u003C\u002Fcode> rejected valid JSON nested deeper than 128 levels with \u003Ccode>halt.fatal: Invalid JSON (R305)\u003C\u002Fcode> - the default recursion limit of \u003Ccode>serde_json::from_str\u003C\u002Fcode>. All user-JSON decoding (strict and relaxed paths in \u003Ccode>handle_json_parse\u003C\u002Fcode>, plus the compile-time \u003Ccode>is_parseable_json\u003C\u002Fcode> predicate) now goes through a depth-unlimited deserializer (\u003Ccode>from_str_unbounded\u003C\u002Fcode> in \u003Ccode>src\u002Fruntime\u002Fbuiltin\u002Fjson\u002Fparse.rs\u003C\u002Fcode>; \u003Ccode>serde_json\u003C\u002Fcode> feature \u003Ccode>unbounded_depth\u003C\u002Fcode>). The practical depth ceiling is the 64MB executor stack. A 500-deep build-serialize-reparse round trip now completes in both JIT and \u003Ccode>--no-jit\u003C\u002Fcode> modes.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> Removed two \u003Ccode>unused mut\u003C\u002Fcode> warnings in the \u003Ccode>nan_ops.rs\u003C\u002Fcode> layout tests; the test build is warning-free again.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>JSON: raw literals \u003Ccode>&lt;&lt;&lt; ... &gt;&gt;&gt;\u003C\u002Fcode> produce a json value in every expression position\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> A \u003Ccode>&lt;&lt;&lt; ... &gt;&gt;&gt;\u003C\u002Fcode> literal was only a json value in declaration position (\u003Ccode>json: x &lt;&lt;&lt; ... &gt;&gt;&gt;;\u003C\u002Fcode> and \u003Ccode>json: x = &lt;&lt;&lt; ... &gt;&gt;&gt;;\u003C\u002Fcode>, which the parser desugars to \u003Ccode>json.parse(...)\u003C\u002Fcode>). In every other expression position - reassignment to an existing variable (\u003Ccode>x = &lt;&lt;&lt; ... &gt;&gt;&gt;;\u003C\u002Fcode>) and call arguments (\u003Ccode>push(&lt;&lt;&lt; ... &gt;&gt;&gt;)\u003C\u002Fcode>) - it silently degraded to a plain String, and subsequent JSON methods (\u003Ccode>.set()\u003C\u002Fcode>, \u003Ccode>.bind()\u003C\u002Fcode>, ...) failed at runtime with the misleading message \u003Ccode>Method Set not found on String\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> The AST compiler (\u003Ccode>src\u002Fcompiler\u002Fcompile_expr\u002Fleaf.rs\u003C\u002Fcode>) and the HIR compiler (\u003Ccode>src\u002Fhir\u002Fcompile_expr.rs\u003C\u002Fcode>) now emit a \u003Ccode>JsonParse\u003C\u002Fcode> opcode after loading the literal string when the literal's content is valid JSON (strict or relaxed - the same acceptance as the runtime parser, exposed as \u003Ccode>is_parseable_json\u003C\u002Fcode> in \u003Ccode>src\u002Fruntime\u002Fbuiltin\u002Fjson\u002Fparse.rs\u003C\u002Fcode>). Literals whose content is not valid JSON (for example containing embedded expressions, which the language does not support) keep the previous raw-string behavior, so programs relying on it are unaffected.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> JIT\u002Finterpreter divergence in \u003Ccode>xcx_jit_json_parse\u003C\u002Fcode> (\u003Ccode>src\u002Fvm\u002Fcore\u002Fjit_helpers.rs\u003C\u002Fcode>): called with an already-parsed json value it returned \u003Ccode>false\u003C\u002Fcode>. It now returns the value itself (identity, refcount-incremented), matching the interpreter path, which reaches the same result via \u003Ccode>to_string\u003C\u002Fcode> + re-parse. The divergence surfaced when desugared \u003Ccode>json.parse(&lt;literal&gt;)\u003C\u002Fcode> calls started receiving json-typed arguments.\u003C\u002Fli>\n\u003Cli>All five reproduction cases now produce identical output in JIT and \u003Ccode>--no-jit\u003C\u002Fcode> modes. Declaration semantics for invalid literal content are unchanged (still an R305 halt at runtime). \u003Ccode>cargo test --release\u003C\u002Fcode>: 200 passed \u002F 0 failed \u002F 0 ignored.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>VM: JIT stability and fallback to the interpreter\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> The \u003Ccode>disable_jit\u003C\u002Fcode> field of the \u003Ccode>VM\u003C\u002Fcode> struct changed from \u003Ccode>bool\u003C\u002Fcode> to \u003Ccode>std::sync::atomic::AtomicBool\u003C\u002Fcode>, allowing safe modification at runtime from JIT context (without taking a mutex).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[NEW]\u003C\u002Fstrong> Dynamic JIT disable on first compilation error. If \u003Ccode>JIT::compile_method\u003C\u002Fcode> or \u003Ccode>JIT::compile\u003C\u002Fcode> returns an error (e.g. \u003Ccode>finalize_definitions\u003C\u002Fcode> reports \u003Ccode>EACCES\u003C\u002Fcode> on systems with a W^X policy), the VM permanently disables the JIT for the whole session (\u003Ccode>disable_jit.store(true)\u003C\u002Fcode>) and continues in interpreter mode. Previous behavior: the VM logged the error and continued in an undefined state.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Removed the \u003Ccode>error_count\u003C\u002Fcode> increment on JIT compilation failure in \u003Ccode>executor.rs\u003C\u002Fcode> and \u003Ccode>jit_helpers.rs\u003C\u002Fcode>. A compilation error (which results in a correct fallback) was previously counted as an execution error, which could incorrectly affect the process exit code.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>Affects:\u003C\u002Fstrong> FreeBSD 14+ (W^X kernel), and any system where the JIT cannot finalize executable-memory metadata.\u003C\u002Fp>\n\u003Ch2>VM: SSRF - unified interpreter and JIT behavior\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> The \u003Ccode>call()\u003C\u002Fcode> function in \u003Ccode>src\u002Fruntime\u002Fbuiltin\u002Fnet\u002Fclient.rs\u003C\u002Fcode> (the interpreter path for \u003Ccode>net.get\u003C\u002Fcode>) now calls \u003Ccode>is_safe_url(&amp;url)\u003C\u002Fcode> instead of a manual \u003Ccode>url.contains(\"169.254.\")\u003C\u002Fcode> check. The previous implementation blocked only link-local addresses (\u003Ccode>169.254.*\u003C\u002Fcode>) instead of the full set of protected address ranges.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> When \u003Ccode>is_safe_url()\u003C\u002Fcode> returns an error prefixed with \u003Ccode>HALT.FATAL\u003C\u002Fcode> or \u003Ccode>HALT.ERROR\u003C\u002Fcode>, the interpreter now raises a \u003Ccode>panic!()\u003C\u002Fcode> with the appropriate message. Previous behavior: returning \u003Ccode>OpResult::Continue\u003C\u002Fcode> with an error map - the VM kept running instead of terminating the process.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> SSRF behavior is now consistent between the JIT path (\u003Ccode>xcx_jit_net_call\u003C\u002Fcode>) and the interpreter path (\u003Ccode>call\u003C\u002Fcode>): both terminate the process when a disallowed address is detected.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>SSRF: private-range check narrowed to RFC 1918\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> \u003Ccode>is_safe_url\u003C\u002Fcode> blocked the entire \u003Ccode>172.*\u003C\u002Fcode> range; only \u003Ccode>172.16.0.0\u002F12\u003C\u002Fcode> is private (RFC 1918). Public \u003Ccode>172.0-15.x\u003C\u002Fcode> and \u003Ccode>172.32-255.x\u003C\u002Fcode> addresses are no longer falsely rejected with \u003Ccode>halt.error\u003C\u002Fcode> - the check now tests the second octet (16-31). Covers interpreter and JIT (shared function).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> Removed the unreferenced duplicate of \u003Ccode>is_safe_url\u003C\u002Fcode> in \u003Ccode>src\u002Fvm\u002Futils\u002Fnetwork.rs\u003C\u002Fcode>; the live interpreter\u002FJIT paths use \u003Ccode>src\u002Fruntime\u002Fbuiltin\u002Fnet\u002Fclient.rs\u003C\u002Fcode>. Three unit tests added there.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>JIT: register-initialization tracking and multi-return-path support\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX] \u003Ccode>defined_locals\u003C\u002Fcode> tracking in \u003Ccode>CodegenCtx\u003C\u002Fcode>:\u003C\u002Fstrong> added a \u003Ccode>defined_locals: [bool; 256]\u003C\u002Fcode> array. Local registers are marked initialized (\u003Ccode>true\u003C\u002Fcode>) only when loaded in \u003Ccode>preload_locals\u003C\u002Fcode> (if they belong to the computed \u003Ccode>needs_init\u003C\u002Fcode> set) and on direct definition in \u003Ccode>def_local\u003C\u002Fcode>, \u003Ccode>def_local_nanboxed\u003C\u002Fcode>, and \u003Ccode>reload_local\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX] \u003Ccode>cleanup_all\u003C\u002Fcode> and \u003Ccode>should_skip_dec_ref\u003C\u002Fcode> filtering:\u003C\u002Fstrong>\n\u003Cul>\n\u003Cli>\u003Ccode>cleanup_all\u003C\u002Fcode> skips registers whose \u003Ccode>defined_locals[r]\u003C\u002Fcode> is \u003Ccode>false\u003C\u002Fcode>. This prevents accidentally loading and defining Cranelift variables in the entry block (Block 0) for uninitialized registers while processing early \u003Ccode>return\u003C\u002Fcode> paths.\u003C\u002Fli>\n\u003Cli>\u003Ccode>should_skip_dec_ref\u003C\u002Fcode> returns \u003Ccode>true\u003C\u002Fcode> for registers with \u003Ccode>defined_locals[r] == false\u003C\u002Fcode>, preventing generation of \u003Ccode>dec_ref\u003C\u002Fcode> instructions for stale\u002Frandom stack values on first assignment to a register.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX] Register synchronization after FFI in \u003Ccode>reload_local\u003C\u002Fcode>:\u003C\u002Fstrong> after calling the \u003Ccode>StrAppendLocal\u003C\u002Fcode> helper, \u003Ccode>reload_local\u003C\u002Fcode> now sets the \u003Ccode>mark_used\u003C\u002Fcode>, \u003Ccode>mark_dirty\u003C\u002Fcode>, \u003Ccode>defined_locals[r] = true\u003C\u002Fcode> flags and \u003Ccode>known_types[r] = TypeTag::String\u003C\u002Fcode>. Previously the missing \u003Ccode>dirty\u003C\u002Fcode> marking meant subsequent \u003Ccode>spill_all\u003C\u002Fcode> operations did not write the updated string pointer and tag back to VM memory (\u003Ccode>locals_ptr\u003C\u002Fcode>).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX] Constant type annotations in \u003Ccode>emit_load_const\u003C\u002Fcode>:\u003C\u002Fstrong> pointers to heap-allocated constant objects (including strings) are now recorded as \u003Ccode>ctx.known_types[dst] = TypeTag::String\u003C\u002Fcode> in \u003Ccode>emit_load_const\u003C\u002Fcode>. Previously \u003Ccode>known_types\u003C\u002Fcode> remained \u003Ccode>TypeTag::Unknown\u003C\u002Fcode>.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>I\u002FO &amp; Terminal: dedicated .xcx file execution (.terminal !run)\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX] Invoking the interpreter for .xcx files:\u003C\u002Fstrong> added the helper \u003Ccode>execute_run(cmd: &amp;str)\u003C\u002Fcode> in \u003Ccode>src\u002Fruntime\u002Fbuiltin\u002Fio\u002Fterminal.rs\u003C\u002Fcode>. When the first parameter of the command points to a \u003Ccode>.xcx\u003C\u002Fcode> file, \u003Ccode>.terminal !run\u003C\u002Fcode> launches the current compiler executable directly (\u003Ccode>std::env::current_exe()\u003C\u002Fcode>) passing the file and arguments, instead of relying on OS file associations (which on Windows triggered the \"Open with\" dialog).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX] Standard output and exit status handling:\u003C\u002Fstrong>\n\u003Cul>\n\u003Cli>\u003Ccode>OpCode::TerminalRun\u003C\u002Fcode> and \u003Ccode>xcx_jit_terminal_run\u003C\u002Fcode> (JIT path) print the captured \u003Ccode>stdout\u003C\u002Fcode> buffer of the child process directly to the terminal (\u003Ccode>write_buffered\u003C\u002Fcode> + \u003Ccode>flush_buffered\u003C\u002Fcode>).\u003C\u002Fli>\n\u003Cli>The functions return the output string \u003Ccode>Value::from_string(stdout)\u003C\u002Fcode> on success (or \u003Ccode>Value::from_bool(true)\u003C\u002Fcode> when the output is empty) and \u003Ccode>Value::from_bool(false)\u003C\u002Fcode> on process failure, enabling correct conditional evaluation (e.g. \u003Ccode>if (NOT .terminal !run target)\u003C\u002Fcode> in the \u003Ccode>PAX\u003C\u002Fcode> package manager).\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>Crypto: random token length representation\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> \u003Ccode>crypto.token(len)\u003C\u002Fcode> now generates \u003Ccode>len * 2\u003C\u002Fcode> hexadecimal characters instead of \u003Ccode>len\u003C\u002Fcode> characters. The \u003Ccode>len\u003C\u002Fcode> parameter represents the token length in bytes, so the hexadecimal string representation is now correctly \u003Ccode>2 * len\u003C\u002Fcode> characters long.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>JIT: parameter type-inference precision\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Introduced bidirectional type-constraint propagation in \u003Ccode>infer_param_types\u003C\u002Fcode>. Previously the inference mechanism assigned a static \u003Ccode>TypeTag::Int\u003C\u002Fcode> to parameters based solely on the occurrence of arithmetic operations (\u003Ccode>Add\u003C\u002Fcode>, \u003Ccode>Sub\u003C\u002Fcode>, \u003Ccode>Mul\u003C\u002Fcode>, etc.) or comparisons (\u003Ccode>Less\u003C\u002Fcode>, \u003Ccode>Greater\u003C\u002Fcode>, etc.). This caused errors (e.g. in \u003Ccode>nested_func_expr_arg\u003C\u002Fcode>) where floating-point values passed to nested functions were incorrectly treated as integers, leading to register corruption \u002F returning \u003Ccode>0\u003C\u002Fcode> in the JIT.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[REF]\u003C\u002Fstrong> The new mechanism propagates types backwards and forwards based on constant types (\u003Ccode>LoadConst\u003C\u002Fcode>), casts (\u003Ccode>Cast*\u003C\u002Fcode>), and type-specific instructions (e.g. loop operations and increments). This safeguards floating-point typing correctness while fully preserving JIT optimizations for integer operations (e.g. in the \u003Ccode>fib(30)\u003C\u002Fcode> benchmark).\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>Store: recursive directory deletion\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Modified \u003Ccode>store.delete()\u003C\u002Fcode> in the interpreter path (\u003Ccode>read_write.rs\u003C\u002Fcode>) and the JIT helper \u003Ccode>xcx_jit_store_delete\u003C\u002Fcode> (\u003Ccode>jit_helpers.rs\u003C\u002Fcode>). Both previously called \u003Ccode>std::fs::remove_file\u003C\u002Fcode> unconditionally, which failed and returned \u003Ccode>false\u003C\u002Fcode> when deleting a directory. The path is now checked - directories are removed recursively with \u003Ccode>std::fs::remove_dir_all\u003C\u002Fcode>, files with \u003Ccode>std::fs::remove_file\u003C\u002Fcode>.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>Terminal: preventing spurious error-count growth (fix terminal_error_count)\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Modified the AST (\u003Ccode>call.rs\u003C\u002Fcode>) and HIR (\u003Ccode>compile_expr_special.rs\u003C\u002Fcode>) compilers to handle the \u003Ccode>terminal\u003C\u002Fcode> namespace. Methods such as \u003Ccode>.write()\u003C\u002Fcode>, \u003Ccode>.clear()\u003C\u002Fcode>, \u003Ccode>.raw()\u003C\u002Fcode>, \u003Ccode>.normal()\u003C\u002Fcode>, \u003Ccode>.cursor()\u003C\u002Fcode>, \u003Ccode>.move()\u003C\u002Fcode>, \u003Ccode>.exit()\u003C\u002Fcode>, \u003Ccode>.run()\u003C\u002Fcode> are now correctly detected and compiled directly to their dedicated VM opcodes, instead of going through the generic \u003Ccode>xcx_jit_method_call_custom\u003C\u002Fcode> JIT\u002FVM helper with a \u003Ccode>TAG_STR\u003C\u002Fcode> (\"terminal\") receiver, which previously bumped the internal \u003Ccode>error_count\u003C\u002Fcode> counter on every call.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>Input: filtering key-release events in raw mode (fix double_input_event)\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Modified input handling in \u003Ccode>input()\u003C\u002Fcode>, \u003Ccode>read_key()\u003C\u002Fcode>, and \u003Ccode>wait_key()\u003C\u002Fcode> in \u003Ccode>input.rs\u003C\u002Fcode> to filter out \u003Ccode>KeyEventKind::Release\u003C\u002Fcode> events (key releases) when reading keyboard events via \u003Ccode>crossterm\u003C\u002Fcode>. Previously, the missing filter caused a single physical keypress on Windows to generate two events with the same key code (press and release), double-triggering event registration in the VM.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>JIT: receiver refcount elision for the GetVar \u002F MethodCall pattern\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Fixed a reference leak on method calls over global collections. The compiler emits every such call as \u003Ccode>GetVar G -&gt; r\u003C\u002Fcode> + \u003Ccode>MethodCall { dst: r, base: r }\u003C\u002Fcode>; \u003Ccode>emit_get_var\u003C\u002Fcode> incremented the pointer's refcount, but the specialized fast paths (\u003Ccode>Update\u003C\u002Fcode>\u002F\u003Ccode>Set\u003C\u002Fcode>\u002F\u003Ccode>Push\u003C\u002Fcode>\u002F\u003Ccode>Get\u003C\u002Fcode>) overwrote the receiver register with the result without a matching \u003Ccode>dec_ref\u003C\u002Fcode> - every \u003Ccode>x.push(...)\u003C\u002Fcode> \u002F \u003Ccode>x.update(...)\u003C\u002Fcode> \u002F \u003Ccode>x.get(...)\u003C\u002Fcode> on a global collection leaked one reference (in long-running loops: unbounded \u003Ccode>strong_count\u003C\u002Fcode> growth).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[NEW]\u003C\u002Fstrong> Static \u003Ccode>getvar_inc_elidable\u003C\u002Fcode> analysis (\u003Ccode>src\u002Fjit\u002Fanalysis.rs\u003C\u002Fcode>) detects a \u003Ccode>GetVar\u003C\u002Fcode> immediately followed by its consuming \u003Ccode>MethodCall\u003C\u002Fcode> (through a window of argument-setup ops that neither spill registers nor run user code). The \u003Ccode>unowned_recv_regs\u003C\u002Fcode> set in \u003Ccode>CodegenCtx\u003C\u002Fcode> marks the register as an un-owned borrow; \u003Ccode>def_local\u003C\u002Fcode> clears the bit on every redefinition, so a borrow never outlives its consuming instruction. The \u003Ccode>[GetVar without inc]\u003C\u002Fcode> + \u003Ccode>[MethodCall without dec]\u003C\u002Fcode> pairs are exactly refcount-neutral.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[REF]\u003C\u002Fstrong> Performance effect (Main Suite, 20 warmup \u002F 100 runs): sieve 99.19 -&gt; 34.63 ms in development measurements (baseline.json: 97.814 ms); decomposition: marking loop 67-85 ms -&gt; ~28 ms, counting loop ~30 ms -&gt; ~17 ms. Other metrics within noise: fib(30) 11.30 -&gt; 11.16 ms, lcg(100M) 109.33 -&gt; 108.52 ms, json 0.126 -&gt; 0.120 ms. Removing the \u003Ccode>inc_ref\u003C\u002Fcode> FFI call from sieve's inner loop (~23M executions in marking, ~10M in counting) was the dominant cost of that benchmark. \u003Ccode>cargo test --release\u003C\u002Fcode>: 199 passed (including two new layout tests in \u003Ccode>nan_ops.rs\u003C\u002Fcode>).\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>JIT\u002FVM: removal of the unreachable trace JIT and fiber-segment JIT subsystems\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> Removed the trace-recording subsystem (\u003Ccode>vm\u002Ftrace\u002F\u003C\u002Fcode>), the \u003Ccode>JIT::compile\u003C\u002Fcode> trace compiler, and the fiber-segment JIT (\u003Ccode>src\u002Fjit\u002Fcompiler_fiber.rs\u003C\u002Fcode>, the \u003Ccode>jit_segments\u003C\u002Fcode> map), along with all \u003Ccode>Executor\u003C\u002Fcode>\u002F\u003Ccode>VM\u003C\u002Fcode> trace plumbing, REPL trace rows, and 16 orphaned emitters. Both subsystems were dead (unreachable from any execution path) - per-function method JIT remains the only JIT mode. No functional behavior change; the engine's dead overhead is gone.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>VM: verified dead-code removal\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> Removed 13 dead files (~1,480 lines) along with the closure-vestige chain: \u003Ccode>ClosureObj\u003C\u002Fcode>, \u003Ccode>UpvalueCell\u003C\u002Fcode>, \u003Ccode>TAG_CLOSURE\u003C\u002Fcode>, the \u003Ccode>MakeClosure\u003C\u002Fcode> opcode, the \u003Ccode>is_closure\u003C\u002Fcode>\u002F\u003Ccode>is_arena\u003C\u002Fcode> flags, and \u003Ccode>collect_backedges\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> Removed 36 scattered dead functions (including the \u003Ccode>xcx_eq\u003C\u002Fcode>..\u003Ccode>xcx_ge\u003C\u002Fcode> family, \u003Ccode>use_*_nanboxed\u003C\u002Fcode>, \u003Ccode>emit_div_int\u003C\u002Fcode>).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> Removed the \u003Ccode>TAG_ARENA\u003C\u002Fcode> value format across 9 files (the nan_ops predicate test was updated).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> Removed the \u003Ccode>Chunk::has_loops\u003C\u002Fcode> field and \u003Ccode>calculate_has_loops\u003C\u002Fcode> - after the trace recorder's removal the field had zero readers (10 construction sites updated).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[REF]\u003C\u002Fstrong> Net ~2,900 lines of verified dead code removed. \u003Ccode>--release\u003C\u002Fcode> build with zero warnings; 200 tests passed \u002F 0 failed \u002F 0 ignored; full benchmark gate at parity or better vs \u003Ccode>baseline.json\u003C\u002Fcode> (sieve -1.7%, FUNC geometric mean -7.9%, TOTAL -1.6% on that interim gate). The only above-baseline entry is the known-open \u003Ccode>for step\u003C\u002Fcode> regression (+35%).\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>Diagnostics and tests: technical-debt cleanup\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> \u003Ccode>DEBUG:\u003C\u002Fcode> prints replaced with span-aware messages: \"Method call on non-object receiver\" and \"Unknown receiver tag\" in \u003Ccode>dispatch.rs\u003C\u002Fcode>, and the JSON fallback path - all now append \u003Ccode>current_span_info(ip)\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> Silenced error messages restored: \u003Ccode>halt.alert\u003C\u002Fcode> again prints a warning to stderr and continues execution (matching \u003Ccode>errors_halt.md\u003C\u002Fcode>); Map\u002FSet method fallbacks print \"Method ... not supported\" with a span instead of halting silently.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[FIX]\u003C\u002Fstrong> \u003Ccode>DatabaseInit\u003C\u002Fcode> now threads the real \u003Ccode>ip\u003C\u002Fcode> into \u003Ccode>handle_database_init\u003C\u002Fcode> - database initialization errors report the actual source position instead of \u003Ccode>0\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[CHG]\u003C\u002Fstrong> Bytecode\u002Fglobal dumps in tests (\u003Ccode>[TEST BYTECODE]\u003C\u002Fcode> etc.) are printed only with \u003Ccode>XCX_TEST_DUMP=1\u003C\u002Fcode>; removed the duplicated \u003Ccode>left_rc\u003C\u002Fcode> declaration line in \u003Ccode>vm\u002Futils\u002Ftable.rs\u003C\u002Fcode> (the last standing compiler warning - the build is now fully clean).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>[NEW]\u003C\u002Fstrong> SSRF link-local coverage restored as a spawned-process test (\u003Ccode>tests\u002Fssrf_link_local.rs\u003C\u002Fcode>), replacing the ignored in-process test (which aborted the release test binary via a panic across a JIT FFI frame).\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>Performance\u003C\u002Fh2>\n\u003Cp>Main Suite (official config: 20 warmup \u002F 100 runs). The 4.2 reference values are the baseline suite introduced with 4.2.\u003C\u002Fp>\n\u003Cdiv class=\"docs-table-wrap\">\u003Ctable class=\"docs-table\">\n\u003Cthead>\u003Ctr>\u003Cth>Variant\u003C\u002Fth>\u003Cth>fib(30)\u003C\u002Fth>\u003Cth>lcg(100M)\u003C\u002Fth>\u003Cth>sieve\u003C\u002Fth>\u003Cth>json\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\u003Ctd>XCX 4.2, JIT\u003C\u002Ftd>\u003Ctd>12.303 ms\u003C\u002Ftd>\u003Ctd>106.480 ms\u003C\u002Ftd>\u003Ctd>97.814 ms\u003C\u002Ftd>\u003Ctd>0.238 ms\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Cstrong>XCX 4.3, JIT\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>10.560 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>104.852 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>36.542 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>0.134 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>XCX 4.2, --no-jit\u003C\u002Ftd>\u003Ctd>186.819 ms\u003C\u002Ftd>\u003Ctd>4328.303 ms\u003C\u002Ftd>\u003Ctd>1876.484 ms\u003C\u002Ftd>\u003Ctd>0.267 ms\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Cstrong>XCX 4.3, --no-jit\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>204.118 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>4528.251 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>2125.645 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>\u003Cstrong>0.154 ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cul>\n\u003Cli>\u003Cstrong>JIT mode improved on all four tests:\u003C\u002Fstrong> fib -14.2%, lcg -1.5%, \u003Cstrong>sieve -62.7%\u003C\u002Fstrong> (the receiver-refcount elision above), json -43.7%.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Interpreter mode (--no-jit) regressed on compute-heavy tests:\u003C\u002Fstrong> fib +9.3%, lcg +4.6%, sieve +13.3% (json improved -42.3%). This continues the interpreter-regression trend present since 4.0; addressing it is the main focus of the 4.4 cycle.\u003C\u002Fli>\n\u003Cli>Full comparison against 26 languages\u002Fruntimes (XCX 4.3 ranks 10th by geometric mean): see the table in the \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fxcxlang-org\u002Fxcx\" target=\"_blank\" rel=\"noopener noreferrer\">README on GitHub\u003C\u002Fa>.\u003C\u002Fli>\n\u003C\u002Ful>","Sep 03, 2026","3 min read",[15,16],{"slug":28,"title":29,"excerpt":30,"content":31,"date":32,"category":33,"readTime":34,"author":13,"tags":35,"featured":17,"visibility":18},"work-on-the-new-xcx-website-has-begun","Work on the new XCX website has begun","We've started work on a new version of the xcxlang.com website, this time built on Nuxt. The planned update includes a visual redesign, a refresh of the brand identity, and a few improvements to modernize the site.","\u003Cp>We've started \u003Cstrong>work \u003C\u002Fstrong>on a new \u003Cstrong>version \u003C\u002Fstrong>of the \u003Cstrong>xcxlang.com\u003C\u002Fstrong> website, this time built on \u003Cstrong>Nuxt\u003C\u002Fstrong>. The planned update includes a \u003Cstrong>visual \u003C\u002Fstrong>redesign, a refresh of the brand identity, and a few \u003Cstrong>improvements \u003C\u002Fstrong>to modernize the site.\u003C\u002Fp>\u003Cp>The \u003Cstrong>project \u003C\u002Fstrong>is in an early stage, more \u003Cstrong>updates \u003C\u002Fstrong>will follow as \u003Cstrong>work\u003C\u002Fstrong> progresses.\u003C\u002Fp>","2026-07-20","announcements","1 min read",[],{"slug":37,"title":38,"excerpt":39,"content":40,"date":41,"category":11,"readTime":42,"author":13,"tags":43,"featured":44,"visibility":18},"xcx-4-2-the-hir-fast-path-update","XCX 4.2 — The HIR & Fast Path Update","XCX 4.2 introduces a new intermediate compilation layer, HIR, along with a substantial JIT optimization pass. Function ASTs are now lowered to HIR before being compiled to bytecode, unlocking HIR-level inlining. The JIT…","\u003Cp>\u003Cstrong>XCX 4.2\u003C\u002Fstrong> introduces a new intermediate compilation layer, \u003Cstrong>HIR\u003C\u002Fstrong>, along with a substantial \u003Cstrong>JIT \u003C\u002Fstrong>optimization pass. Function \u003Cstrong>ASTs \u003C\u002Fstrong>are now lowered to \u003Cstrong>HIR \u003C\u002Fstrong>before being compiled to bytecode, unlocking \u003Cstrong>HIR-level\u003C\u002Fstrong> inlining. The \u003Cstrong>JIT \u003C\u002Fstrong>gains fast paths for arrays and \u003Cstrong>BoolArray\u003C\u002Fstrong>, \u003Cstrong>JSON \u003C\u002Fstrong>gets a parse cache, string concatenation gets new \u003Cstrong>\u003Cem>StrAppend*\u003C\u002Fem>\u003C\u002Fstrong> opcodes, and \u003Cstrong>\u003Cem>table.join\u003C\u002Fem>\u003C\u002Fstrong> moves to \u003Cstrong>Hash-Join O(N+M)\u003C\u002Fstrong>.\u003C\u002Fp>\u003Cp>\u003Cstrong>\u003Cem>~12x faster string append\u003C\u002Fem>\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>\u003Cstrong>\u003Cem> ~21x faster table.join (500x500)\u003C\u002Fem>\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>\u003Cstrong>\u003Cem> 97.8ms sieve under JIT\u003C\u002Fem>\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>\u003Cem>Available on Linux (Ubuntu, Arch\u002FManjaro, and other major distros), Windows, and macOS.\u003C\u002Fem>\u003C\u002Fp>\u003Ch2>\u003Cstrong>What's new\u003C\u002Fstrong>\u003C\u002Fh2>\u003Cp>\u003Cstrong>New Compilation Layer: HIR\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>Function \u003Cstrong>ASTs \u003C\u002Fstrong>are now lowered to \u003Cstrong>HIR \u003C\u002Fstrong>before being compiled to bytecode. \u003Cstrong>HIR-level\u003C\u002Fstrong> function inlining was introduced, excluding fibers, recursion, \u003Cstrong>\u003Cem>return\u003C\u002Fem>\u003C\u002Fstrong> inside a loop, and overly costly functions. Full compilation of \u003Cstrong>\u003Cem>TableLiteral\u003C\u002Fem>\u003C\u002Fstrong>, \u003Cstrong>\u003Cem>DatabaseLiteral\u003C\u002Fem>\u003C\u002Fstrong>, \u003Cstrong>\u003Cem>DateLiteral\u003C\u002Fem>\u003C\u002Fstrong>, and \u003Cstrong>\u003Cem>Tuple\u003C\u002Fem>\u003C\u002Fstrong> literals was added, eliminating the default \u003Cstrong>Int \u003C\u002Fstrong>initialization that used to cause method dispatch errors.\u003C\u002Fp>\u003Cp>\u003Cstrong>JIT: New Optimizations\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>Added a fast path bounds check for arrays (\u003Cstrong>\u003Cem>int[]\u003C\u002Fem>\u003C\u002Fstrong>, \u003Cstrong>\u003Cem>bool[]\u003C\u002Fem>\u003C\u002Fstrong>) — the \u003Cstrong>\u003Cem>index &lt; len\u003C\u002Fem>\u003C\u002Fstrong> comparison now happens directly in \u003Cstrong>JIT \u003C\u002Fstrong>code, with a runtime fallback for out-of-range access. A dedicated \u003Cstrong>BoolArray \u003C\u002Fstrong>fast path now bypasses \u003Cstrong>FFI \u003C\u002Fstrong>and \u003Cstrong>RwLock\u003C\u002Fstrong>. Fixed a type inference bug that failed to recognize \u003Cstrong>\u003Cem>array:b\u003C\u002Fem>\u003C\u002Fstrong> constants as \u003Cstrong>BoolArray\u003C\u002Fstrong>. Added constant tracking in registers (\u003Cstrong>\u003Cem>register_const\u003C\u002Fem>\u003C\u002Fstrong>), division\u002Fmodulo by a power of 2 without guards, and typed \u003Cstrong>\u003Cem>JumpIfFalse\u003C\u002Fem>\u003C\u002Fstrong> reducing the comparison to a simple \u003Cstrong>\u003Cem>icmp\u003C\u002Fem>\u003C\u002Fstrong>.\u003C\u002Fp>\u003Cp>\u003Cstrong>JSON: Caching and Fast Access Paths\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>New thread-local cache for \u003Cstrong>\u003Cem>json.parse()\u003C\u002Fem>\u003C\u002Fstrong> (up to \u003Cstrong>128 \u003C\u002Fstrong>entries). Fast path for simple keys in getters, \u003Cstrong>\u003Cem>has()\u003C\u002Fem>\u003C\u002Fstrong>, and \u003Cstrong>\u003Cem>keys()\u003C\u002Fem>\u003C\u002Fstrong>\u002F\u003Cstrong>\u003Cem>len()\u003C\u002Fem>\u003C\u002Fstrong>. Fixed a thread-unsafe \u003Cstrong>\u003Cem>dirty: AtomicBool\u003C\u002Fem>\u003C\u002Fstrong> flag, replacing it with a \u003Cstrong>\u003Cem>version\u003C\u002Fem>\u003C\u002Fstrong>\u002F\u003Cstrong>\u003Cem>cached_version\u003C\u002Fem>\u003C\u002Fstrong> counter pair, eliminating the risk of reading a corrupted \u003Cstrong>JSON \u003C\u002Fstrong>string under concurrent \u003Cstrong>JIT\u003C\u002Fstrong>\u002F\u003Cstrong>VM \u003C\u002Fstrong>access.\u003C\u002Fp>\u003Cp>\u003Cstrong>String Concatenation: New StrAppend* Opcodes\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>\u003Cstrong>\u003Cem>StrAppendVar\u003C\u002Fem>\u003C\u002Fstrong>\u002F\u003Cstrong>\u003Cem>StrAppendLocal\u003C\u002Fem>\u003C\u002Fstrong>\u002F\u003Cstrong>\u003Cem>StrAppendMember\u003C\u002Fem>\u003C\u002Fstrong>\u002F\u003Cstrong>\u003Cem>StrAppendElement\u003C\u002Fem>\u003C\u002Fstrong> mutate the string buffer in place when \u003Cstrong>Arc \u003C\u002Fstrong>ownership is unique, instead of three allocations per iteration. Results (100k iterations): global variable \u003Cstrong>1300ms → 2.2-3.5ms\u003C\u002Fstrong>; array element \u003Cstrong>86ms → ~3-4ms\u003C\u002Fstrong>; general append under \u003Cstrong>JIT 10790ms → 5.5ms\u003C\u002Fstrong>.\u003C\u002Fp>\u003Cp>\u003Cstrong>Table Queries\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>\u003Cstrong>\u003Cem>table.join\u003C\u002Fem>\u003C\u002Fstrong> with \u003Cstrong>Hash-Join O(N+M)\u003C\u002Fstrong> (500x500 rows: \u003Cstrong>215ms → 10ms\u003C\u002Fstrong>). New \u003Cstrong>\u003Cem>row_cache\u003C\u002Fem>\u003C\u002Fstrong> on \u003Cstrong>RowObj \u003C\u002Fstrong>for \u003Cstrong>\u003Cem>table.where\u003C\u002Fem>\u003C\u002Fstrong>, auto-invalidated on modification. \u003Cstrong>\u003Cem>count()\u003C\u002Fem>\u003C\u002Fstrong>\u002F\u003Cstrong>\u003Cem>len()\u003C\u002Fem>\u003C\u002Fstrong>\u002F\u003Cstrong>\u003Cem>size()\u003C\u002Fem>\u003C\u002Fstrong> with an active \u003Cstrong>\u003Cem>sql_where\u003C\u002Fem>\u003C\u002Fstrong> now computed directly in the database.\u003C\u002Fp>\u003Cp>\u003Cstrong>Networking: HTTP\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>\u003Cstrong>TCP\u003C\u002Fstrong>\u002F\u003Cstrong>TLS \u003C\u002Fstrong>connection pooling via a global agent (\u003Cstrong>\u003Cem>HTTP_AGENT\u003C\u002Fem>\u003C\u002Fstrong>) — \u003Cstrong>100 \u003C\u002Fstrong>sequential \u003Cstrong>HTTPS \u003C\u002Fstrong>requests: \u003Cstrong>195ms\u002Freq → 63ms\u002Freq\u003C\u002Fstrong>.\u003C\u002Fp>\u003Cp>\u003Cstrong>CLI\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>Reorganized \u003Cstrong>\u003Cem>--help\u003C\u002Fem>\u003C\u002Fstrong>, short flags \u003Cstrong>\u003Cem>-h\u003C\u002Fem>\u003C\u002Fstrong>\u002F\u003Cstrong>\u003Cem>-v\u003C\u002Fem>\u003C\u002Fstrong> now working anywhere, combining options with \u003Cstrong>\u003Cem>|\u003C\u002Fem>\u003C\u002Fstrong>.\u003C\u002Fp>\u003Cp>\u003Cstrong>Bug Fixes\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>Fixed hangs on \u003Cstrong>\u003Cem>@step\u003C\u002Fem>\u003C\u002Fstrong> loops with bare backward jumps, a panic on \u003Cstrong>\u003Cem>JsonPush\u003C\u002Fem>\u003C\u002Fstrong> against a \u003Cstrong>JSON \u003C\u002Fstrong>object, missing argument propagation in \u003Cstrong>\u003Cem>table.where(...)\u003C\u002Fem>\u003C\u002Fstrong>, an incorrect bitcast in \u003Cstrong>Float + Int \u003C\u002Fstrong>arithmetic, a missing \u003Cstrong>SSA \u003C\u002Fstrong>block switch in \u003Cstrong>\u003Cem>GetIndex\u003C\u002Fem>\u003C\u002Fstrong> for \u003Cstrong>BoolArray\u003C\u002Fstrong>, and a register allocation bug in closures\u002Fcaptures for \u003Cstrong>\u003Cem>table.where(...)\u003C\u002Fem>\u003C\u002Fstrong>.\u003C\u002Fp>\u003Ch2>Performance results\u003C\u002Fh2>\u003Cp>New benchmark suite (more stable lcg\u002Fsieve measurements, more accurate json sampling):\u003C\u002Fp>\u003Cp>Variant                                fib(30)                           lcg(100m)                        sieve                                json\u003C\u002Fp>\u003Cdiv class=\"docs-table-wrap\">\u003Ctable class=\"docs-table\">\u003Ctbody>\u003Ctr>\u003Ctd data-row=\"2\">XCX 4.1, JIT\u003C\u002Ftd>\u003Ctd data-row=\"2\">13.119ms\u003C\u002Ftd>\u003Ctd data-row=\"2\">107.706ms\u003C\u002Ftd>\u003Ctd data-row=\"2\">201.003ms\u003C\u002Ftd>\u003Ctd data-row=\"2\">0.272ms\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd data-row=\"3\">\u003Cstrong>XCX 4.2, JIT\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd data-row=\"3\">\u003Cstrong>12.303ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd data-row=\"3\">\u003Cstrong>106.480ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd data-row=\"3\">\u003Cstrong>97.814ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd data-row=\"3\">\u003Cstrong>0.238ms\u003C\u002Fstrong>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cblockquote>Test system: AMD Ryzen 7 5800X, 32 GB RAM, Windows 11\u003C\u002Fblockquote>","2026-07-18","2 min read",[],true,{"slug":46,"title":47,"excerpt":48,"content":49,"date":50,"category":33,"readTime":34,"author":13,"tags":51,"featured":44,"visibility":18},"development-update-slower-pace-ahead","Development Update: Slower Pace Ahead","Just a quick heads up. I've got some personal stuff going on right now that's taking","\u003Cp>\u003Cstrong>Just a quick heads \u003C\u002Fstrong>up. I've got some personal \u003Cstrong>stuff \u003C\u002Fstrong>going on right now that's taking \u003C\u002Fp>\u003Cp>up a lot of my time, so development on \u003Cstrong>XCX \u003C\u002Fstrong>(and the tools around it) is going to slow \u003C\u002Fp>\u003Cp>down for a \u003Cstrong>bit\u003C\u002Fstrong>. This isn't the project stalling or being abandoned, just a temporary \u003C\u002Fp>\u003Cp>dip in \u003Cstrong>pace\u003C\u002Fstrong>.\u003C\u002Fp>\u003Cp>Response times on \u003Cstrong>issues \u003C\u002Fstrong>will be slower than usual, and ongoing \u003Cstrong>work \u003C\u002Fstrong>is paused rather \u003C\u002Fp>\u003Cp>than dropped — it'll pick back up.\u003C\u002Fp>\u003Cp>For more details, see the full update on GitHub Discussions .\u003C\u002Fp>\u003Cp>\u003Cstrong>\u003Cu>Thanks for understanding.\u003C\u002Fu>\u003C\u002Fstrong>\u003C\u002Fp>","2026-07-07",[],{"slug":53,"title":54,"excerpt":55,"content":56,"date":57,"category":11,"readTime":58,"author":13,"tags":59,"featured":17,"visibility":18},"xcx-4-1-the-runtime-refinement-update","XCX 4.1 — The Runtime Refinement Update","XCX 4.1 is a refinement update building on the JIT foundation introduced in 4.0. The main change is JIT-to-JIT direct dispatch, allowing compiled functions to call each other without returning to the interpreter,…","\u003Cp>\u003Cstrong>XCX 4.1\u003C\u002Fstrong> is a refinement \u003Cstrong>update \u003C\u002Fstrong>building on the JIT foundation introduced in\u003Cem>\u003C\u002Fem>\u003Cstrong>4.0\u003C\u002Fstrong>. The main change is \u003Cstrong>JIT\u003C\u002Fstrong>-\u003Cstrong>to-JIT \u003C\u002Fstrong>direct dispatch, allowing compiled \u003Cstrong>functions \u003C\u002Fstrong>to call each other without \u003Cstrong>returning \u003C\u002Fstrong>to the interpreter, reducing cross-function call overhead by \u003Cstrong>\u003Cem>~47%\u003C\u002Fem>\u003C\u002Fstrong>. The release also introduces eager callee pre-compilation, \u003Cstrong>inlined \u003C\u002Fstrong>collection size access, configurable hotspot thresholds, \u003Cstrong>REPL \u003C\u002Fstrong>improvements, \u003Cstrong>SQLite FFI \u003C\u002Fstrong>parity fixes, and a \u003Cstrong>PAX \u003C\u002Fstrong>self-\u003Cstrong>upgrade \u003C\u002Fstrong>system.\u003C\u002Fp>\u003Cp>\u003Cstrong>\u003Cem>~47% cross-function call reduction\u003C\u002Fem>\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>\u003Cstrong>\u003Cem> 12.87ms Fib(30) with JIT\u003C\u002Fem>\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>\u003Cstrong>\u003Cem> 25ms 1M cross-function calls\u003C\u002Fem>\u003C\u002Fstrong>\u003C\u002Fp>\u003Ch2>\u003Cstrong>What’s new\u003C\u002Fstrong>\u003C\u002Fh2>\u003Cp>\u003Cstrong>JIT-to-JIT\u003C\u002Fstrong> Direct Call Dispatch\u003C\u002Fp>\u003Cp>\u003Cstrong>Compiled \u003C\u002Fstrong>functions now call other compiled functions directly via \u003Cstrong>\u003Cem>Cranelift call_indirect\u003C\u002Fem>\u003C\u002Fstrong>. The interpreter is no longer re-entered for compiled-to-compiled \u003Cstrong>calls\u003C\u002Fstrong>. A fast path checks callee \u003Cstrong>\u003Cem>jit_ptr\u003C\u002Fem>\u003C\u002Fstrong> atomically; missing entries fall back to the existing slow path. Execution state is fully managed in \u003Cstrong>JIT-emitted\u003C\u002Fstrong> code.\u003C\u002Fp>\u003Cp>Eager Callee Pre-Compilation\u003C\u002Fp>\u003Cp>Before \u003Cstrong>IR \u003C\u002Fstrong>generation, the \u003Cstrong>compiler \u003C\u002Fstrong>scans bytecode for \u003Cstrong>function \u003C\u002Fstrong>calls and \u003Cstrong>pre-compiles\u003C\u002Fstrong> detected callees. This makes \u003Cstrong>direct \u003C\u002Fstrong>dispatch available immediately on first execution. \u003Cstrong>Cycle \u003C\u002Fstrong>detection prevents recursive compilation loops.\u003C\u002Fp>\u003Cp>\u003Cstrong>Inlined Collection Sizes\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cdiv class=\"code-editor-card compact\">\n  \u003Cdiv class=\"editor-header\">\n    \u003Cdiv class=\"editor-header-left\">\n      \u003Cspan class=\"terminal-prompt-icon\">$\u003C\u002Fspan>\n      \u003Cspan class=\"terminal-session-text\">plain\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"editor-header-right\">\n      \u003Cspan class=\"linux-btn linux-min\">_\u003C\u002Fspan>\n      \u003Cspan class=\"linux-btn linux-max\">⛶\u003C\u002Fspan>\n      \u003Cspan class=\"linux-btn linux-close\">×\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"editor-body\">\n    \u003Cpre>\u003Ccode> .size(), .len(), and .count() \u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>for \u003Cstrong>Array\u003C\u002Fstrong>, \u003Cstrong>BoolArray\u003C\u002Fstrong>, and \u003Cstrong>Map \u003C\u002Fstrong>are now handled directly in \u003Cstrong>JIT \u003C\u002Fstrong>code. Size reads compile to a direct\u003Cstrong> 64-bit \u003C\u002Fstrong>memory load, removing \u003Cstrong>FFI \u003C\u002Fstrong>overhead.\u003C\u002Fp>\u003Cp>\u003Cstrong>Pointer Analysis Optimization\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>\u003Cstrong>\u003Cem>analyze_maybe_ptr_regs\u003C\u002Fem>\u003C\u002Fstrong> now uses a \u003Cstrong>256-bit\u003C\u002Fstrong> bitmask implemented as four \u003Cstrong>64-bit\u003C\u002Fstrong> integers. This reduces merge overhead from\u003Cstrong> per-bit\u003C\u002Fstrong> iteration to simple \u003Cstrong>bitwise \u003C\u002Fstrong>operations.\u003C\u002Fp>\u003Cp>\u003Cstrong>Configurable Hotspot Threshold\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>Hotspot compilation \u003Cstrong>threshold \u003C\u002Fstrong>is now runtime configurable via \u003Cstrong>\u003Cem>--threshold\u003C\u002Fem>\u003C\u002Fstrong>. Default remains \u003Cstrong>50 \u003C\u002Fstrong>executions \u003Cstrong>before \u003C\u002Fstrong>compilation. Invalid values terminate execution with \u003Cstrong>error \u003C\u002Fstrong>code \u003Cstrong>1\u003C\u002Fstrong>.\u003C\u002Fp>\u003Cp>\u003Cstrong>Standard Library Updates\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cdiv class=\"code-editor-card compact\">\n  \u003Cdiv class=\"editor-header\">\n    \u003Cdiv class=\"editor-header-left\">\n      \u003Cspan class=\"terminal-prompt-icon\">$\u003C\u002Fspan>\n      \u003Cspan class=\"terminal-session-text\">plain\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"editor-header-right\">\n      \u003Cspan class=\"linux-btn linux-min\">_\u003C\u002Fspan>\n      \u003Cspan class=\"linux-btn linux-max\">⛶\u003C\u002Fspan>\n      \u003Cspan class=\"linux-btn linux-close\">×\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"editor-body\">\n    \u003Cpre>\u003Ccode>Array.slice(start, end)\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>supports \u003Cstrong>half-open\u003C\u002Fstrong> ranges \u003Cstrong>\u003Cem>[start, end\u003C\u002Fem>\u003C\u002Fstrong>) with clamping. \u003Cstrong>json.keys()\u003C\u002Fstrong> returns all top-level keys as \u003Cstrong>string arrays\u003C\u002Fstrong>.\u003C\u002Fp>\u003Cp>\u003Cstrong>REPL Improvements\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>New \u003Cstrong>keybindings \u003C\u002Fstrong>for line navigation and \u003Cstrong>command \u003C\u002Fstrong>execution. Immediate execution for \u003Cstrong>\u003Cem>!-prefixed \u003C\u002Fem>\u003C\u002Fstrong>commands. New diagnostics:\u003Cstrong>\u003Cem> !globals\u003C\u002Fem>\u003C\u002Fstrong>, \u003Cstrong>\u003Cem>!jit\u003C\u002Fem>\u003C\u002Fstrong>,\u003Cstrong>\u003Cem> !reset\u003C\u002Fem>\u003C\u002Fstrong>. Fixed state carryover issues \u003Cstrong>between \u003C\u002Fstrong>sessions.\u003C\u002Fp>\u003Cp>\u003Cstrong>SQLite FFI Parity\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>\u003Cstrong>JIT \u003C\u002Fstrong>and interpreter now handle \u003Cstrong>SQLite\u003C\u002Fstrong> soft errors \u003Cstrong>consistently\u003C\u002Fstrong>. Errors no longer \u003Cstrong>incorrectly \u003C\u002Fstrong>increment error counters under \u003Cstrong>JIT \u003C\u002Fstrong>execution.\u003C\u002Fp>\u003Cp>\u003Cstrong>PAX Self-Upgrade\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>\u003Cstrong>PAX \u003C\u002Fstrong>can now update \u003Cstrong>compiler \u003C\u002Fstrong>and \u003Cstrong>tooling\u003C\u002Fstrong>. Supports version checks, \u003Cstrong>binary \u003C\u002Fstrong>replacement, and \u003Cstrong>safe \u003C\u002Fstrong>Windows \u003Cstrong>file \u003C\u002Fstrong>swapping via \u003Cstrong>\u003Cem>.old\u003C\u002Fem>\u003C\u002Fstrong> fallback.\u003C\u002Fp>\u003Cp>\u003Cstrong>Multiple Variable Declarations\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>Support \u003Cstrong>added \u003C\u002Fstrong>for grouped \u003Cstrong>variable \u003C\u002Fstrong>declarations in \u003Cstrong>both \u003C\u002Fstrong>typed and var forms. \u003Cstrong>Implemented \u003C\u002Fstrong>via compile-time desugaring with no \u003Cstrong>runtime \u003C\u002Fstrong>overhead.\u003C\u002Fp>\u003Cp>\u003Cstrong>Technical Debt Reduction\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>Removed unused \u003Cstrong>parameters\u003C\u002Fstrong>, merged dispatch paths, \u003Cstrong>eliminated \u003C\u002Fstrong>redundant analysis passes, and optimized \u003Cstrong>allocation \u003C\u002Fstrong>patterns in the register system. Build now completes \u003Cstrong>without \u003C\u002Fstrong>warnings.\u003C\u002Fp>\u003Cp>\u003Cstrong>Bug Fixes\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>Block comments \u003Cstrong>now \u003C\u002Fstrong>require explicit closing tokens. Added \u003Cstrong>extended \u003C\u002Fstrong>error test coverage for runtime and compile-time \u003Cstrong>error \u003C\u002Fstrong>handling.\u003C\u002Fp>\u003Ch2>\u003Cstrong>Performance results\u003C\u002Fstrong>\u003C\u002Fh2>\n\u003Cdiv class=\"code-editor-card compact\">\n  \u003Cdiv class=\"editor-header\">\n    \u003Cdiv class=\"editor-header-left\">\n      \u003Cspan class=\"terminal-prompt-icon\">$\u003C\u002Fspan>\n      \u003Cspan class=\"terminal-session-text\">plain\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"editor-header-right\">\n      \u003Cspan class=\"linux-btn linux-min\">_\u003C\u002Fspan>\n      \u003Cspan class=\"linux-btn linux-max\">⛶\u003C\u002Fspan>\n      \u003Cspan class=\"linux-btn linux-close\">×\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"editor-body\">\n    \u003Cpre>\u003Ccode>Loop 100M: 116.27ms (−2.3%)\nFib(30): 12.87ms (−8.1%)\nSieve 100K: 2.29ms (−8.4%)\nJSON 1000×100: 21.46ms (−6.7%)\nCross-function 1M calls: 25ms (−46.8%)\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cblockquote>Test system: AMD Ryzen 7 5800X, 32 GB RAM, Windows 11\u003C\u002Fblockquote>","2026-06-24","5 min read",[],{"slug":61,"title":62,"excerpt":63,"content":64,"date":65,"category":11,"readTime":58,"author":13,"tags":66,"featured":17,"visibility":18},"xcx-4-0-the-performance-architecture-update","XCX 4.0 — The Performance & Architecture Update","The biggest XCX release yet. A ground-up JIT rewrite, new VM value representation, zero-copy JSON, arena allocator, and a fully reworked REPL — all delivering up to 27× speedup in benchmarks.","\u003Cp>The biggest \u003Cstrong>XCX \u003C\u002Fstrong>release yet. A ground-up \u003Cstrong>JIT \u003C\u002Fstrong>rewrite, new \u003Cstrong>VM \u003C\u002Fstrong>value representation, zero-copy \u003Cstrong>JSON\u003C\u002Fstrong>, arena allocator, and a fully reworked \u003Cstrong>REPL \u003C\u002Fstrong>— all delivering up to \u003Cstrong>27×\u003C\u002Fstrong> speedup in \u003Cstrong>benchmarks\u003C\u002Fstrong>.\u003C\u002Fp>\u003Ch2>PERFORMANCE — BENCHMARK RESULTS\u003C\u002Fh2>\u003Cp>Benchmark \u003Cstrong>XCX \u003C\u002Fstrong>3.1 reference XCX 4.0 JIT Improvement\u003C\u002Fp>\u003Cp> ____________________________________\u003C\u002Fp>\u003Cp> |Benchmark| xcx 3.1| xcx 4.0 | Improvment    |\u003C\u002Fp>\u003Cp> |--------------------------------------------------|\u003C\u002Fp>\u003Cp> |Loop 100M| 520ms | 119ms | 4.37×\t\t\t\t   |\u003C\u002Fp>\u003Cp> |--------------------------------------------------|\u003C\u002Fp>\u003Cp> |Fib(30) | 45ms | 14.28ms  |3.15×\t\t\t\t\t   |\u003C\u002Fp>\u003Cp> |--------------------------------------------------|\u003C\u002Fp>\u003Cp> |Sieve 100K |5ms | 2.55ms | 1.96×\t\t\t\t\t   |\u003C\u002Fp>\u003Cp> |--------------------------------------------------|\u003C\u002Fp>\u003Cp> |JSON 1000×100|  112ms |  22.74ms | 4.92×|\u003C\u002Fp>\u003Cp> |____________________________________|\u003C\u002Fp>\u003Ch2>WHAT'S NEW\u003C\u002Fh2>\u003Cp>[JIT] Tracing JIT — fully rewritten (Cranelift)\u003C\u002Fp>\u003Cp> From 1780-line monolith to 20-file architecture with type inference, liveness analysis, NaN-boxing, tiered compilation, and fused loop opcodes. Hot paths detected after 50 visits; 7-argument trace signature.\u003C\u002Fp>\u003Cp>[VM] New value representation — two-word struct\u003C\u002Fp>\u003Cp> Replaced NaN-boxed single 64-bit word with explicit {bits, tag} struct (16 bytes). JIT registers still use NaN-boxing internally via pack\u002Funpack adapters. Added TAG_CLOSURE and TAG_ARENA.\u003C\u002Fp>\u003Cp>[perf] New perf module\u003C\u002Fp>\u003Cp> High-resolution monotonic timer — perf.ms(), perf.us(), perf.ns(). Unlike date.now(), values are guaranteed monotonic and unaffected by NTP\u002FDST.\u003C\u002Fp>\u003Cp>[JSON] Zero-copy JSON engine\u003C\u002Fp>\u003Cp> Internal JsonVal type replaces serde_json::Value. Objects use Vec-based linear search (faster than hashing at small sizes). Direct buffer writes, no intermediate allocations. 112ms -&gt; 22.74ms.\u003C\u002Fp>\u003Cp>[VM] Arena allocator\u003C\u002Fp>\u003Cp> Per-thread bump allocator (TAG_ARENA) — 4KB chunks, no Arc\u002FRwLock overhead, no destructors. Eliminates reference counting cost for temporary values.\u003C\u002Fp>\u003Cp>[REPL] REPL fully reworked (crossterm)\u003C\u002Fp>\u003Cp> Rebuilt on top of crossterm. Features free multiline editing via arrow keys without mode switching, explicit !exec command for triggering script execution, and an updated welcome screen\u002Fhelp menu.\u003C\u002Fp>\u003Cp>[Fix] While loop JIT path fix\u003C\u002Fp>\u003Cp> Shadowing bug in compile_while caused &lt; operator to always fall through to LessEqual path — dedicated JIT optimization was never used.\u003C\u002Fp>\u003Cp>[Fix] Type inference at CFG join points\u003C\u002Fp>\u003Cp> Merge logic incorrectly absorbed types into Unknown for unvisited blocks. Introduced visited: Vec&lt;bool&gt; — JIT now emits more native Cranelift ops instead of polymorphic FFI calls.\u003C\u002Fp>\u003Cp>[Fix] Table Literal Limit Resolution\u003C\u002Fp>\u003Cp> Fixed compiler register overflow when declaring large table literals (exceeding 255 cells). Introduced a selective compilation strategy via new TableBegin and TableInitRow opcodes, recycling VM registers per row incrementally. The mechanism activates automatically for matrices larger than 200 cells, maintaining fast paths for small data.\u003C\u002Fp>\u003Cp>[CLI] New CLI flags\u003C\u002Fp>\u003Cp> --no-jit disables JIT globally.\u003C\u002Fp>\u003Cp>[arch] Codebase modularization\u003C\u002Fp>\u003Cp> 19 -&gt; 269 Rust files. ~18K -&gt; ~35K lines. Largest file is 1172 lines. Compiler split into 25+ modules; VM divided into core\u002F, frame\u002F, value\u002F, object\u002F, trace\u002F.\u003C\u002Fp>\u003Cp>[linux] Linux build parity\u003C\u002Fp>\u003Cp> The Linux version successfully passes all core tests verified on Windows. However, it may still exhibit isolated OS-specific edge-cases and issues not present on Windows. Performance metrics show a slight variance, generally fluctuating by +-5-7% compared to Windows builds.\u003C\u002Fp>\u003Cp>[infra] Closure infrastructure (internal)\u003C\u002Fp>\u003Cp> ClosureObj with upvalue cells, new opcodes MakeClosure\u002FCloseUpvalue\u002FLoadUpvalue\u002FStoreUpvalue. XCX syntax does not yet expose closures — infrastructure ready for 5.0.\u003C\u002Fp>","2026-06-15",[],{"slug":68,"title":69,"excerpt":70,"content":71,"date":65,"category":11,"readTime":34,"author":13,"tags":72,"featured":17,"visibility":18},"xcx-4-0-1","XCX 4.0.1","Released: June 15, 2026","\u003Ch2>XCX 4.0.1 — Hotfix\u003C\u002Fh2>\u003Cp>\u003Cstrong>Released:\u003C\u002Fstrong> June 15, 2026\u003C\u002Fp>\u003Ch2>Bug Fixes\u003C\u002Fh2>\u003Cp>Fixed  .show() method on collections — The .show() method was accidentally left commented out during the XCX 4.0 release, causing collections to silently produce no output when inspected. It is now restored and fully functional.\u003C\u002Fp>\u003Ch2>Installation\u003C\u002Fh2>\u003Cp>Download the latest binary from \u003Ca href=\"https:\u002F\u002Fxcxlang.com\u002F\" rel=\"noopener noreferrer\" target=\"_blank\">xcxlang.com\u003C\u002Fa> or build from source:\u003C\u002Fp>\n\u003Cdiv class=\"code-editor-card compact\">\n  \u003Cdiv class=\"editor-header\">\n    \u003Cdiv class=\"editor-header-left\">\n      \u003Cspan class=\"terminal-prompt-icon\">$\u003C\u002Fspan>\n      \u003Cspan class=\"terminal-session-text\">plain\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"editor-header-right\">\n      \u003Cspan class=\"linux-btn linux-min\">_\u003C\u002Fspan>\n      \u003Cspan class=\"linux-btn linux-max\">⛶\u003C\u002Fspan>\n      \u003Cspan class=\"linux-btn linux-close\">×\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"editor-body\">\n    \u003Cpre>\u003Ccode>git clone https:\u002F\u002Fgithub.com\u002Fyour-org\u002Fxcx\ncd xcx\ncargo build --release\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>",[],{"slug":74,"title":75,"excerpt":76,"content":77,"date":78,"category":33,"readTime":58,"author":13,"tags":79,"featured":17,"visibility":18},"web-playground-released","Web Playground Released","We are excited to announce the release of the XCX Web Playground! You can now try out XCX directly in your browser by visiting playground.xcxlang.com.","\u003Ch2>XCX Web Playground Released\u003C\u002Fh2>\u003Cp>We are excited to announce the release of the \u003Cstrong>XCX \u003C\u002Fstrong>Web \u003Cstrong>Playground\u003C\u002Fstrong>! You can now try out \u003Cstrong>XCX \u003C\u002Fstrong>directly in your browser by visiting \u003Cstrong>playground.xcxlang.com\u003C\u002Fstrong>.\u003C\u002Fp>\u003Ch2>PLAYGROUND LIMITATIONS\u003C\u002Fh2>\u003Cul>\u003Cli>Operates on a virtual file system (no access to real files).\u003C\u002Fli>\u003Cli>No support for the http module.\u003C\u002Fli>\u003Cli>No support for the database module.\u003C\u002Fli>\u003Cli>No support for the crypto module.\u003C\u002Fli>\u003Cli>No support for the store module.\u003C\u002Fli>\u003Cli>Partial support for the terminal module.\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>TECHNICAL SPECIFICATIONS\u003C\u002Fh2>\u003Cp>\u003Cstrong>XCX \u003C\u002Fstrong>Target Version: \u003Cstrong>v4.0\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>\u003Cstrong>Playground \u003C\u002Fstrong>Interpreter: \u003Cstrong>v1.0\u003C\u002Fstrong>\u003C\u002Fp>\u003Ch2>BUILT WITH\u003C\u002Fh2>\u003Cp>TypeScript: interpreter &amp; logic\u003C\u002Fp>\u003Cp> Vue 3: playground UI\u003C\u002Fp>\u003Cp> Tailwind CSS: styling\u003C\u002Fp>\u003Cp> Vite: build tool\u003C\u002Fp>\u003Ch2>SOURCE CODE\u003C\u002Fh2>\u003Cp>The playground is fully open source. You can view the repository on GitHub: xcxlang-org\u002Fxcx-web-playground\u003C\u002Fp>","2026-06-01",[],{"slug":81,"title":82,"excerpt":83,"content":84,"date":85,"category":11,"readTime":58,"author":13,"tags":86,"featured":17,"visibility":18},"xcx-3-1-1-linux-update-pre-release","XCX 3.1.1 — Linux Update Pre-release","This is the first Linux build of XCX.","\u003Cp>This is the first Linux build of XCX.\u003C\u002Fp>\u003Cp>Linux support is in an early experimental stage. The system was primarily developed and tested on Windows, so this build may behave differently on Linux environments.\u003C\u002Fp>\u003Cp>Some features may not work as expected. Stability is not fully verified, and issues such as crashes, performance differences, or unexpected runtime behavior may occur.\u003C\u002Fp>\u003Cp>Feedback is welcome. Bug reports, error logs, and descriptions of unexpected behavior are helpful and will be used to improve future releases and overall Linux compatibility.\u003C\u002Fp>\u003Cp>Use this build for testing and development.\u003C\u002Fp>","2026-05-25",[],{"slug":88,"title":89,"excerpt":90,"content":91,"date":92,"category":11,"readTime":42,"author":13,"tags":93,"featured":17,"visibility":18},"xcx-3-1-changelog-3-0-rarr-3-1","XCX 3.1 — Changelog (3.0 → 3.1)","XCX 3.1 ships a new version of mathlib.","\u003Ch2>XCX 3.1 Changelog\u003C\u002Fh2>\u003Ch2 id=\"compiler-jit\">Compiler \u002F JIT\u003C\u002Fh2>\u003Ch2 id=\"math-library\">Math library\u003C\u002Fh2>\u003Cp>XCX 3.1 ships a new version of mathlib.\u003C\u002Fp>\u003Ch2 id=\"vm\">VM\u003C\u002Fh2>\u003Ch2 id=\"diagnostics\">Diagnostics\u003C\u002Fh2>\u003Ch2 id=\"build\">Build\u003C\u002Fh2>\u003Ch2 id=\"performance\">Performance\u003C\u002Fh2>\u003Cp>Measured on Ryzen 5 5600X \u002F 16 GB RAM \u002F Windows 11.\u003C\u002Fp>\u003Cp>3.1 focuses on correctness and compiler internals. Runtime changes are minor.\u003C\u002Fp>\u003Cdiv class=\"docs-table-wrap\">\u003Ctable class=\"docs-table\">\u003Ctbody>\u003Ctr>\u003Ctd>Loop 100M\u003C\u002Ftd>\u003Ctd>521 ms\u003C\u002Ftd>\u003Ctd>520 ms\u003C\u002Ftd>\u003Ctd>Fibonacci(30)\u003C\u002Ftd>\u003Ctd>60 ms\u003C\u002Ftd>\u003Ctd>45 ms\u003C\u002Ftd>\u003Ctd>Sieve 100K\u003C\u002Ftd>\u003Ctd>5 ms\u003C\u002Ftd>\u003Ctd>5 ms\u003C\u002Ftd>\u003Ctd>JSON parse\u003C\u002Ftd>\u003Ctd>118 ms\u003C\u002Ftd>\u003Ctd>112 ms\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cp>Full comparison table at \u003Ca href=\"https:\u002F\u002Fxcxlang.com\u002Fviews\u002Fperformance.html\" target=\"_blank\" rel=\"noopener noreferrer\">xcxlang.com\u002Fviews\u002Fperformance.html\u003C\u002Fa>.\u003C\u002Fp>","2026-05-24",[],{"slug":95,"title":96,"excerpt":97,"content":98,"date":99,"category":11,"readTime":12,"author":13,"tags":100,"featured":17,"visibility":18},"xcx-3-0-changelog-2-2-3-0","XCX 3.0 — Changelog (2.2 → 3.0)","April 2026 · Type: ANNOUNCEMENT","\u003Cp>\u003Cstrong>April 2026 · Type: ANNOUNCEMENT\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong> XCX 3.0 may contain bugs — the VM architecture has grown complex over time. Please report issues. XCX 4.0 is planned with a fully redesigned architecture.\u003C\u002Fp>\u003Ch2 id=\"database-new\">Database (NEW)\u003C\u002Fh2>\u003Cp>Entirely new subsystem. No equivalent existed in 2.2.\u003C\u002Fp>\u003Ch2 id=\"named-arguments-new\">Named Arguments (NEW)\u003C\u002Fh2>\u003Ch2 id=\"type-system\">Type System\u003C\u002Fh2>\u003Ch2 id=\"error-system-new\">Error System (NEW)\u003C\u002Fh2>\u003Cp>No structured error codes existed in 2.2.\u003C\u002Fp>\u003Cdiv class=\"docs-table-wrap\">\u003Ctable class=\"docs-table\">\u003Ctbody>\u003Ctr>\u003Ctd>S103\u003C\u002Ftd>\u003Ctd>Type mismatch\u003C\u002Ftd>\u003Ctd>S108\u003C\u002Ftd>\u003Ctd>Invalid index\u003C\u002Ftd>\u003Ctd>S109\u003C\u002Ftd>\u003Ctd>Property not found\u003C\u002Ftd>\u003Ctd>S110\u003C\u002Ftd>\u003Ctd>Method not found\u003C\u002Ftd>\u003Ctd>S111\u003C\u002Ftd>\u003Ctd>Invalid arguments\u003C\u002Ftd>\u003Ctd>S211\u003C\u002Ftd>\u003Ctd>Void fiber\u003C\u002Ftd>\u003Ctd>S212\u003C\u002Ftd>\u003Ctd>Invalid fiber run\u003C\u002Ftd>\u003Ctd>S302\u003C\u002Ftd>\u003Ctd>Table row mismatch\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fdiv>\u003Ch2 id=\"collections-arrays\">Collections — Arrays\u003C\u002Fh2>\u003Ch2 id=\"collections-maps\">Collections — Maps\u003C\u002Fh2>\u003Ch2 id=\"collections-tables\">Collections — Tables\u003C\u002Fh2>\u003Ch2 id=\"collections-sets-random.choice\">Collections — Sets \u002F\nrandom.choice\u003C\u002Fh2>\u003Ch2 id=\"standard-library-crypto\">Standard Library — crypto\u003C\u002Fh2>\u003Ch2 id=\"standard-library-store\">Standard Library — store\u003C\u002Fh2>\u003Ch2 id=\"standard-library-random\">Standard Library — random\u003C\u002Fh2>\u003Ch2 id=\"terminal-input\">Terminal &amp; Input\u003C\u002Fh2>\u003Ch2 id=\"io-user-input\">I\u002FO — User Input (\u003Ccode>&gt;?\u003C\u002Fcode>)\u003C\u002Fh2>\u003Ch2 id=\"json\">JSON\u003C\u002Fh2>\u003Ch2 id=\"http-response-object\">HTTP — Response Object\u003C\u002Fh2>\u003Cp>2.2 response had four fields. 3.0 adds two:\u003C\u002Fp>\u003Ch2 id=\"http-cors\">HTTP — CORS\u003C\u002Fh2>\u003Ch2 id=\"string-methods\">String Methods\u003C\u002Fh2>\u003Ch2 id=\"pax-package-manager\">PAX Package Manager\u003C\u002Fh2>\u003Ch3 id=\"project.pax\">project.pax\u003C\u002Fh3>\u003Ch3 id=\"commands\">Commands\u003C\u002Fh3>\u003Ch3 id=\"lockfile\">Lockfile\u003C\u002Fh3>\u003Ch2 id=\"breaking-changes\">Breaking Changes\u003C\u002Fh2>\u003Cdiv class=\"docs-table-wrap\">\u003Ctable class=\"docs-table\">\u003Ctbody>\u003Ctr>\u003Ctd>JSON method\u003C\u002Ftd>\u003Ctd>\u003Ccode>.to_str()\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>\u003Ccode>.toStr()\u003C\u002Fcode> (renamed)\u003C\u002Ftd>\u003Ctd>\u003Ccode>&gt;?\u003C\u002Fcode> input\u003C\u002Ftd>\u003Ctd>Variable type changes at runtime if input mismatches\u003C\u002Ftd>\u003Ctd>Strict parsing only, no type change\u003C\u002Ftd>\u003Ctd>CORS\u003C\u002Ftd>\u003Ctd>Do NOT add \u003Ccode>Access-Control-Allow-Origin\u003C\u002Fcode> manually\u003C\u002Ftd>\u003Ctd>Recommended to declare headers explicitly\u003C\u002Ftd>\u003Ctd>\u003Ccode>random.choice from\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Sets only\u003C\u002Ftd>\u003Ctd>Sets and arrays\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fdiv>\u003Ch2 id=\"internal-changes\">Internal Changes\u003C\u002Fh2>","2026-04-23",[],{"slug":102,"title":103,"excerpt":104,"content":105,"date":106,"category":11,"readTime":34,"author":13,"tags":107,"featured":17,"visibility":18},"xcx-2-2-released","XCX 2.2 Released","JIT Compilation — Cranelift-based JIT; 1B-iteration triple nested loop benchmark: ~478ms vs ~140s in 2.1 (~293x speedup).","\u003Ch2>XCX 2.2: Performance &amp; Efficiency\u003C\u002Fh2>\u003Ch2>CORE PERFORMANCE\u003C\u002Fh2>\u003Cp>JIT Compilation — Cranelift-based JIT; 1B-iteration triple nested loop benchmark: ~478ms vs ~140s in 2.1 (~293x speedup).\u003C\u002Fp>\u003Cp> Trace Recording — Automatic trace capture and native code generation for loops.\u003C\u002Fp>\u003Cp> NaN-Boxed Values — Optimized 64-bit value representation (8 bytes).\u003C\u002Fp>\u003Cp> Lock-Free Atomics — Improved concurrency with AtomicBool shutdown handling.\u003C\u002Fp>\u003Ch2>VM IMPROVEMENTS\u003C\u002Fh2>\u003Cp>Optimized Opcodes — New IncLocalLoopNext, IncVarLoopNext, GuardInt\u002FFloat for faster execution.\u003C\u002Fp>\u003Cp> Better Fiber Support — Enhanced FiberState with yield value caching.\u003C\u002Fp>\u003Ch2>MIGRATION NOTES\u003C\u002Fh2>\u003Cp>Feature 2.1 2.2\u003C\u002Fp>\u003Cp>Execution Interpreted JIT + Interpreted\u003C\u002Fp>\u003Cp>Value Size 24+ bytes 8 bytes\u003C\u002Fp>\u003Cp>Loop Perf (1B) ~140s ~478ms\u003C\u002Fp>\u003Cp>\u003Cstrong>IMPORTANT\u003C\u002Fstrong>: Bytecode is not compatible with 2.1. Requires cranelift, parking_lot, and argon2.\u003C\u002Fp>","2026-03-29",[],{"slug":109,"title":110,"excerpt":111,"content":112,"date":113,"category":11,"readTime":58,"author":13,"tags":114,"featured":17,"visibility":18},"xcx-2-1-released","XCX 2.1 Released","XCX 2.1 is focused entirely on performance, correctness, and production readiness. Existing code runs unchanged and faster.","\u003Ch2>XCX 2.1: Performance &amp; Correctness\u003C\u002Fh2>\u003Cp>XCX 2.1 is focused entirely on performance, correctness, and production readiness. Existing code runs unchanged and faster.\u003C\u002Fp>\u003Ch2>PERFORMANCE HIGHLIGHTS\u003C\u002Fh2>\u003Cp>Constant table deduplication: Smaller bytecode.\u003C\u002Fp>\u003Cp>Method dispatch via enum: Resolved at compile time.\u003C\u002Fp>","2026-03-24",[],{"slug":116,"title":117,"excerpt":118,"content":119,"date":120,"category":11,"readTime":34,"author":13,"tags":121,"featured":17,"visibility":18},"xcx-launch-with-2-0","XCX Launch with 2.0","1has officially launched with version 2.0! This is the first public release of the language, introducing explicit control, native fibers, and a pragmatic technical architecture.","\u003Ch2>XCX 2.0 OFFICIAL LAUNCH\u003C\u002Fh2>\u003Cp>\u003Cstrong>1\u003C\u002Fstrong>has officially launched with version 2.0! This is the first public release of the language, introducing explicit control, native fibers, and a pragmatic technical architecture.\u003C\u002Fp>","2026-03-22",[],1789646459321]