Sigil 0.22.3

release

A native-codegen correctness release. Two defects found by Crash The Stack in its first week of play, both invisible to the bytecode VM and to every in-process test suite: a release binary that could crash at startup depending on where it was built, and a web build that leaked memory on every frame.

Download 0.22.3Get started

Existing projects build without changes. A declaration such as sigil: "0.22" already admits 0.22.3. Nothing in the language or the build surface changed; a project picks the fixes up by rebuilding with this toolchain.

Quoted literals are safe under the collector

A native binary rebuilds every quoted list or vector at module init, and that reconstruction was not GC-safe: a collection landing in the middle of a large literal could leave later elements dangling, and the crash came much later, in the first major collection. Whether it happened depended on the length of the checkout's path, so the same commit could run in one worktree and segfault in another.

Literals are now rooted and barriered while they are rebuilt, on both the native and the wasm targets. test/integration/test-native-literal-gc.sh forces the timing and is red on 0.22.2, green here.

Tail calls with more than eight arguments no longer leak

A tail call between native-codegen procedures with nine or more arguments carried its arguments in a heap buffer that was never freed. A game's draw path can make thousands of such calls a frame: the published web build grew by about 90 KB per frame and hit FATAL: Out of memory after an hour or two on a board, and the native release binary grew from 283 MB to 907 MB in three and a half minutes.

Both trampoline loops now free the buffer once the callee returns. test/integration/test-native-tramp-args.sh bounces five million nine-argument calls and caps the resident set's growth at 8 MB; on 0.22.2 it grows by 390 MB, here by zero.

Reading a crash in a release binary

Release binaries are stripped. To get a symbolized frame, relink the objects under build/release/native/ without -s and re-append the bundle trailer. To catch a missed barrier directly, run with SIGIL_GC_AUDIT_BARRIERS=1 and force the timing with SIGIL_GC_ADAPTIVE_YOUNG=0 SIGIL_YOUNG_THRESHOLD=<n>; SIGIL_GC_STRESS=1 is blind to this class, since its collections are all major.