Skip to content

vibee_gen aborts (SIGABRT) on 18 Verilog specs and intermittently on 2 Zig specs #887

Open

Description

@gHashTag

Part of #851, weak point W15. Found by #885/#886. This is a compiler change (src/vibeec/), so the fix is the owner's call. Nothing here has been edited.

What is measured

vibee_gen gen <spec> <out> ends in SIGABRT (signal 6) on 19 of the 1033 specs that report behaviours.

  • Probe run 37242067946 on exp/codegen-flake-ab, 60 passes over the corpus, removing the destination before each generation:

    • 18 specs abort and leave a 0-byte file, in every pass:

      • specs/fpga/blink.tri, counter.tri, fsm_simple.tri;
      • specs/fpga/test_blink.tri, test_counter.tri, test_debouncer.tri, test_multi_led.tri, test_pwm.tri, test_shift_register.tri, test_values.tri;
      • specs/tri/bitnet_mac.vibee, specs/tri/fpga/bitnet_mac.vibee, specs/tri/fpga/trinity_ai_core.vibee, specs/tri/fpga/trit_alu.vibee;
      • specs/tri/trinity_ai_core.vibee, specs/tri/trit_alu.vibee;
      • specs/uart/uart_mode_controller.tri, specs/uart/vsa_uart_operations.tri.
    • Two Zig-target specs abort some of the time and leave no file:

      spec aborts otherwise
      specs/tri/long_context_system.vibee 59 of 60 1 of 60 exits 0 and writes a file
      specs/tri/hdc_stream_classifier.vibee 3 of 60 57 of 60 write the same file
  • fix(codegen): sample the corpus compile check from the baseline #886's own CI run 37243448031 shows the same 19 as NO OUTPUT … signal 6, no file.

  • zig ast-check and zig test both pass a 0-byte file, so before fix(codegen): sample the corpus compile check from the baseline #886 the corpus gate counted the 18 as clean.

The Verilog path: a borrowed slice used after its owner is freed (read from source, not measured)

src/vibeec/vibee_gen.zig on 57de4a1f5:

// 432-436
if (std.mem.eql(u8, spec.language, "varlog") or std.mem.eql(u8, spec.language, "verilog")) {
    var codegen = verilog_codegen.VerilogCodeGen.init(allocator);
    defer codegen.deinit();                       // runs at the end of this block
    generated_code = try codegen.generate(spec);  // a slice into codegen's buffer
}
// ...
defer allocator.free(generated_code);             // 448: frees it again
writeGenerated(output_path, generated_code) ...   // 449: writes freed memory
  • VerilogCodeGen.generate returns self.builder.getOutput() (verilog_codegen.zig:321, and again at :380). getOutput returns self.buffer.items (:271), a borrowed slice.
  • VerilogCodeGen.deinit (:293) frees that buffer, and the defer runs when the if block ends, before line 449.
  • Line 448 then frees the same memory a second time, with items.len rather than the capacity.
  • generateSingleLang repeats the pattern at 464-466.
  • The Zig path does not have this problem: codegen/emitter.zig:1008 returns self.builder.toOwnedSlice().

Why that ends in SIGABRT with a 0-byte file is an inference:

  1. main uses std.heap.page_allocator (vibee_gen.zig:84), so deinit unmaps the buffer's pages.
  2. writeGenerated creates the file (:219), so the file exists at 0 bytes.
  3. The write is then handed an unmapped address. The kernel returns EFAULT, and the standard library treats that as unreachable. In a safety-checked build that is a panic, and the panic aborts.
  4. Because it is a panic rather than an error, the cleanup in writeGenerated that deletes a partial file never runs.

The next CI run of #886 tests this: its gate now prints the panic line instead of the last stack frame. If that line is not an unreachable or EFAULT panic in the write, the inference is wrong, and this issue will be corrected.

Proposed fix (one line, not applied): return an owned slice from VerilogCodeGen.generate, the way the Zig emitter does. That means self.builder.buffer.toOwnedSlice(self.allocator) or an allocator.dupe at the call site. Then the existing defer allocator.free(generated_code) is correct for both paths.

The Zig-path aborts: cause unknown

long_context_system and hdc_stream_classifier abort without a pattern that has been explained. Nothing above accounts for them. Next step: run each one 60 times locally with a Debug build of vibee_gen and read the full trace.

Why it mattered for CI (inference, strong)

  • Main's corpus gate counted a missing file as clean, and it sampled every 32nd entry of the run's own clean list.

  • hdc_stream_classifier is not in the baseline, and it sorts before specs/tri/phi_utils_multi.tri, which sits one place before a sample slot.

  • When it aborts, main's gate counts one extra clean spec. That pushes phi_utils_multi into the sample, and its output does not compile.

  • Main's codegen run was red 6 times on branch main from 09-24 to 10-04. Five of those were exactly DOES NOT COMPILE: specs/tri/phi_utils_multi.tri:

    • 36027587352;
    • 36029076209;
    • 37016445887;
    • 37088537159;
    • 37225448222.

    The sixth (36515512024) was the vsa_simd benchmark.

  • 5 of 66 runs is consistent with an abort rate of 3 in 60, but main's gate printed no names on red, so the runs cannot be checked after the fact. fix(codegen): sample the corpus compile check from the baseline #886 removes the mechanism whatever the trigger was.

Also found while there (probe 37242067946, compile-all)

Of the 769 outputs that are really written, 13 pass ast-check and fail zig test:

  • 9 because they @import("vsa"), which the corpus does not provide: the specs/tri/vsa_* encoders and benchmarks;
  • 3 with a failure the probe recorded as blank: codegen_engine_final_upgrade, sacred_identity, swarm_agents;
  • phi_utils_multi, whose function with a non-void return type implicitly returns.

On 02b9958de none of these falls in #886's 24-slot compile sample: that run compiled 24 of 24. A baseline edit that moves the slots could land on one, and the gate would then name it.

The 18 Verilog specs are all language: varlog, and the two that abort intermittently are language: zig. Both were checked against 57de4a1f5.

Done when

🤖 Generated with Claude Code

Activity

  1. gHashTag commented on Oct 4, 2026

    @gHashTag
    OwnerAuthor

    The prediction held for the 18 Verilog specs

    #886's gate now prints the generator's panic line. Run 37243963781 on d959eb722 printed the same line for all 18 language: varlog specs, for example:

    NO OUTPUT: specs/fpga/blink.tri reported behaviours, then signal 6, no file, thread 2978 panic: programmer bug caused syscall error: FAULT
    

    That text comes from errnoBug in Zig 0.16's std/Io/Threaded.zig. The standard library calls it for an errno it treats as a programming error, such as EFAULT. That matches the chain above: the write is handed an address whose pages were unmapped, the kernel returns EFAULT, and the panic aborts. Step 3 of the inference is now measured.

    What is still read from source rather than observed: that the bad address is the freed Verilog buffer. No other freed slice reaches that write.

    One nuance the issue body did not state: errnoBug panics only when is_debug. In a release build it returns error.Unexpected instead. writeGenerated would then print Error writing output, delete the partial file and return the error. The caller's catch return swallows it, so the generator exits 0 with no file. So a release build of vibee_gen would hide this crash rather than fix it: exit 0, no output, and the same false clean that main's gate gives a missing file.

    Two things this run did not settle:

    • specs/tri/long_context_system.vibee printed no panic: line, only a stack frame (???:?:?: 0x1571064 in ??? (???)). So its abort is not a panic. Zig 0.16's segfault handler prints Segmentation fault at address 0x… and then aborts, which would fit. That is an inference. b937408ce makes the gate print such lines, so the next run will show which one it is.
    • specs/tri/hdc_stream_classifier.vibee did not abort in this run. That is expected at 3 in 60.

    A correction to the gate's own output: it said no file for all 18. The probe found an empty file for those 18. b937408ce reports empty file and no file separately.

    🤖 Generated with Claude Code

  2. gHashTag commented on Oct 4, 2026

    @gHashTag
    OwnerAuthor

    The Zig-path abort is a segfault

    #886's tool at b937408ce now names crash-handler lines as well as panic: lines. Its codegen run 37244594555 shows:

    NO OUTPUT: specs/tri/long_context_system.vibee reported behaviours, then signal 6, no file, Segmentation fault at address 0x7fee7103b2a2
    

    So long_context_system (language zig) does not panic. It takes a segfault, and Zig's handler then aborts, which is why the exit is signal 6. It leaves no file.

    The 18 Verilog specs in the same run read signal 6, empty file, … panic: programmer bug caused syscall error: FAULT, matching the comment above.

    Not known: where the segfault happens. The handler printed no symbolised frame. Finding it needs a debug build with symbols, or a run under a debugger. hdc_stream_classifier did not abort in any of #886's 4 codegen runs, so its 3-in-60 rate from the probe has no new data.

    🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions