Skip to content

[tbc] Copy-and-patch JIT execution mode (tbc.mode=jit)#378

Open
PhilippGrulich wants to merge 11 commits into
mainfrom
claude/nautilus-bytecode-interpreter-kggqsw
Open

[tbc] Copy-and-patch JIT execution mode (tbc.mode=jit)#378
PhilippGrulich wants to merge 11 commits into
mainfrom
claude/nautilus-bytecode-interpreter-kggqsw

Conversation

@PhilippGrulich

Copy link
Copy Markdown
Member

Adds a copy-and-patch JIT (Xu & Kjølstad 2021; the technique behind CPython 3.13+'s JIT) as an opt-in execution mode of the tbc backend. The interpreter's remaining per-instruction cost — the dispatch indirect branch, the Instr load, and the operand-field decode — disappears entirely: at build time every opcode handler is compiled offline into a machine-code stencil with holes (relocations against magic _JIT_* symbols), and at compile time a stitcher copies one stencil per instruction into an executable span and patches operands, branch targets, call-site records, and helper addresses directly into the code. Control falls (or directly jumps) from one instruction's code to the next.

Everything else — bytecode, lowering, register allocation, superinstruction fusion, immediate folding, frames, VM stack, trampolines, dyncall externals — is shared 1:1 with the interpreter, which doubles as a differential oracle on identical bytecode.

Measured (Release, x86-64, vs the tail-call interpreter on identical bytecode)

kernel interp jit speedup
fibonacci(15000) 2.79 ms 0.90 ms 3.1×
array sum (2.6M) 784 ms 182 ms 4.3×
internal-call loop (10k) 4.75 ms 2.50 ms 1.9×
external-call loop (10k) 4.63 ms 3.34 ms 1.4×
per-call entry (add) 333 µs 332 µs parity (same pushFrame)

(exec_tbc_*_{interp,jit} in ExecutionBenchmark.cpp; tbc interp is itself ~3–4× over bc.)

How it works

  • Stencils (jit/TBCStencilSource.cpp): one freestanding preserve_none function per opcode, bodies mirroring TBCHandlers.hpp 1:1 and generated from the same X-macro lists in TBCOpcodes.hpp; each ends in [[clang::musttail]] through _JIT_CONTINUE/_JIT_TARGET. Synthetic stencils @RET_VALUE/@RET_VOID/@EPILOGUE/@UNWIND handle returns and program entry/exit.
  • Extractor (tools/stencils/build_stencils.py): compiles the TU per target with a pinned flag set (-mcmodel=large ⇒ every symbol reference is a patchable absolute materialization), harvests bytes + relocations via llvm-readobj JSON (ELF) or a built-in Mach-O parser, and emits the checked-in tables in jit/generated/. Strict: any unexpected relocation kind, symbol, or section is a hard error at generation time. Normal builds (incl. GCC) never need the toolchain; regenerate with cmake --build build --target tbc-regen-stencils (clang ≥ 19).
  • Stitcher (jit/TBCStitcher.cpp): two-pass layout + patch into a single span per program (asmjit JitAllocator/VirtMem own W^X, MAP_JIT, icache flush). Operand register fields are patched pre-scaled as frame byte offsets (zero decode cost), 16-bit immediates as sign-extended 64-bit constants. Peepholes: trailing continue-jumps are elided on fall-through; x86-64 movabs+jmp *%reg pairs relax to direct jmp rel32.
  • Per-target patch kinds: x86-64 ELF needs exactly one (R_X86_64_64); aarch64 ELF uses movz/movk chains + direct b imm26 (+ in-span range thunks for helper bls); arm64 Mach-O GOT-loads every symbol, so the stitcher emulates the GOT with per-instruction value slots inside the span (the CPython trick).
  • Calls: internal CALL pushes the same VM frame via a plain-ABI helper and tail-jumps to the callee's stitched entry; RET pops inline and tail-jumps to the native continuation in the frame header; CALL_EXT/CALL_IND reuse doExtCall (dyncall) via helpers. Reentrancy and the trampoline pool work unchanged (invoke() dispatches per program).
  • Exceptions: stitched code has no unwind info, so C++ exceptions never cross stitched frames — helpers catch into VMContext::pendingException, exit through @UNWIND, and the entry shim rethrows. (Exceptions thrown by invoke() targets already terminate in dyncall's assembly on every mode, interpreter included — pre-existing, unchanged.)
  • Modes: tbc.mode = interp (default — JIT is opt-in) | jit (strict: throws when the build can't execute stitched code, so tests never silently run the wrong engine) | auto (best effort). Availability = generated table for the target + Clang 19+ host (preserve_none entry call) + no ASan. Statistics report tbc.mode, tbc.jit.codeSize.bytes, and skip reasons.
  • Staleness safety: tables resolve stencils by opcode name; a stale table makes stitching bail out to the interpreter (module-level), never mis-execute.

Preparatory refactor (first commit)

TBCInstr.hpp split out of TBCCode.hpp (freestanding, includable by the stencil TU) and the VM core (VMContext, pushFrame, doExtCall, doIndirectCall) hoisted from TBCInterpreter.cpp's anonymous namespace into TBCVm.hpp/.cpp so the JIT helpers share the exact frame logic. No behavior change.

Testing

  • Whole-suite coverage: a tbc-jit pseudo-backend in ExecutionTest.hpp runs the entire execution/val/tracing suite on stitched code (862 dynamic sections in execution-tests alone); also added as a differential-fuzzer peer.
  • TBCJitModeTest.cpp: jit vs de-optimized switch-interpreter reference on identical bytecode × all lowering-option combinations (each combination emits a different stencil population), internal/void/external call kernels, and the mode contract.
  • TBCJitWhiteBoxTest.cpp: hand-authored TBCPrograms covering the exception bridge (VM-stack overflow inside a stitched CALL) and the missing-stencil bail-out.
  • aarch64 validated locally: full execution-tests binary cross-compiled and run under qemu-aarch64 — 11,397 assertions pass, including all tbc-jit sections.
  • Bounded differential fuzzing: no interp-vs-jit mismatches. (The fuzz replay corpus has a pre-existing, backend-independent tracing failure in this environment — reproduced identically on pristine main with the JIT excluded — see notes below.)
  • x86-64: full suite green (363/363, clang-20). GCC 14 build + suite green (degrade path).

Notes for reviewers

  • macOS CI caveat: the macos-15 job uses Apple clang (no preserve_none until LLVM 19), so it validates that the Mach-O stencil table compiles and degrades cleanly but executes the interpreter. The Mach-O GOT-emulation path runs only under a non-Apple clang ≥ 19 on macOS; treat it as best-effort until exercised.
  • The ubuntu-24.04-arm clang-21 job runs the whole suite on stitched aarch64 code (mirrors the local qemu validation).
  • Pre-existing, unrelated: nautilus-fuzz-replay's smoke corpus fails during tracing ("no Return operation was recorded") on pristine main in a clang-20/MLIR-off environment; also NAUTILUS_FUZZ_BACKENDS=bc-only runs hit "Invalid trace … constant loop" on trivial generated programs. Both are backend-independent tracing edges, untouched by this PR.
  • Generated .inc files are large but mechanical; review build_stencils.py + TBCStencilSource.cpp + TBCStitcher.cpp instead. A CI freshness check (regenerate + git diff --exit-code) is a possible follow-up.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NB22eBwKooQMBuwK4jMH2k


Generated by Claude Code

claude added 8 commits July 8, 2026 14:47
Preparation for the copy-and-patch JIT execution mode:

- TBCInstr.hpp: freestanding Instr/kNoReg/packedOffset* definitions
  (no <string>/<vector>), includable by the future stencil TU.
- TBCVm.hpp/.cpp: VMContext, pushFrame, doExtCall, doIndirectCall
  hoisted out of TBCInterpreter.cpp's anonymous namespace so stitched
  code's plain-ABI helpers can share the exact frame logic. VMContext's
  hot members (sp/stackEnd/prog) now come first: their offsets become
  part of the stencil ABI. Adds a pendingException slot for the JIT's
  exception bridging.

No behavior change; all 349 tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NB22eBwKooQMBuwK4jMH2k
Implements a copy-and-patch JIT (Xu & Kjolstad 2021; the CPython 3.13+
technique) layered on the tbc bytecode. Opcode handlers are compiled
offline into machine-code stencils with holes (relocations against
magic _JIT_* symbols); at compile time the stitcher copies one stencil
per instruction into an executable span and patches operands, branch
targets, call-site records, and helper addresses — eliminating the
interpreter dispatch entirely. Control falls directly from one
instruction's code to the next (trailing continue-jumps are elided on
fall-through, and x86-64 movabs+jmp pairs relax to direct jmp rel32).

- jit/TBCStencilSource.cpp: stencil TU (all ~332 opcodes via the shared
  X-macro lists + @RET_VALUE/@RET_VOID/@EPILOGUE/@unwind synthetics),
  freestanding, preserve_none + musttail continuation ABI.
- tools/stencils/build_stencils.py: strict extractor (clang -c ->
  llvm-readobj JSON -> .inc); unknown relocations/symbols/sections fail
  at generation time, never at runtime.
- jit/generated/stencils_{x86_64,aarch64}_elf.inc: checked-in tables,
  regenerated on demand (tbc-regen-stencils target, clang >= 19).
- jit/TBCStitcher.cpp: two-pass layout+patch; asmjit JitAllocator/
  VirtMem own W^X and icache flushing; aarch64 movz/movk + branch26
  patch kinds with in-span range thunks for helper calls.
- jit/TBCStencilHelpers.cpp: plain-ABI helpers (frame push, external/
  indirect calls via dyncall) that catch C++ exceptions into
  VMContext::pendingException — exceptions never unwind through
  stitched frames (no unwind info); the entry shim rethrows.
- Execution: tbc.mode = interp (default) | jit (strict) | auto
  (degrade); stitched programs enter through jit::invokeJit, sharing
  the interpreter's frame layout, VM stack, and trampolines.

Internal calls, external calls, allocas, superinstructions, and
immediates all execute stitched. Stencil tables resolve by opcode NAME,
so a stale generated table degrades to the interpreter instead of
mis-executing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NB22eBwKooQMBuwK4jMH2k
… white-box tests

- ExecutionTest.hpp / fuzz Harness.hpp: add a "tbc-jit" pseudo-backend
  (engine.backend=tbc + tbc.mode=jit), gated on runtime availability, so
  the entire execution/val/tracing suite and the differential fuzzer run
  on stitched copy-and-patch code (862 dynamic sections in the
  execution-tests binary alone).
- TBCJitModeTest.cpp: pins jit against a de-optimized switch-dispatch
  interpreter reference on identical bytecode across all lowering-option
  combinations (each emits a different stencil population), plus
  internal-call, void-call, and external-call kernels, and the
  auto-degrades/jit-is-strict mode contract.
- TBCJitWhiteBoxTest.cpp: hand-authored TBCPrograms exercising machinery
  user kernels cannot reach — the helper exception bridge (VM stack
  overflow during a stitched CALL surfaces as the interpreter's
  RuntimeException via pendingException + @unwind + shim rethrow) and
  the missing-stencil bail-out (stale-table degradation path).
- Harness.hpp: NAUTILUS_FUZZ_BACKENDS=<comma-list> triage filter to
  bisect findings to specific differential peers.

Note: exceptions thrown by invoke() targets terminate in dyncall's
assembly (no unwind info) on every tbc mode including the interpreter —
pre-existing engine behavior, unchanged by the JIT.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NB22eBwKooQMBuwK4jMH2k
exec_tbc_<kernel>_{interp,jit} A/B on identical bytecode, plus two new
call-heavy kernels the existing loop kernels cannot cover: an internal
Nautilus-to-Nautilus CALL loop (frame-push helper cost) and an external
invoke() loop (dyncall bridge cost).

Measured on this branch (Release, clang-20, x86-64):
  fibonacci   2.79 ms -> 0.90 ms   (3.1x)
  sum         784 ms  -> 182 ms    (4.3x)
  internal    4.75 ms -> 2.50 ms   (1.9x)
  external    4.63 ms -> 3.34 ms   (1.4x)
  add (entry-dominated): parity, as expected (same pushFrame work).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NB22eBwKooQMBuwK4jMH2k
…emulated GOT

- Mach-O arm64 support: Apple codegen references every external symbol
  through the GOT (ARM64_RELOC_GOT_LOAD_PAGE21/PAGEOFF12 only), so the
  extractor gains a built-in Mach-O relocatable parser (llvm-readobj has
  no JSON for Mach-O) and the stitcher emulates the GOT with
  per-instruction 8-byte value slots inside the stitched span, pointing
  the adrp/ldr pairs at them. Trailing adrp/ldr/br continue sequences
  are detected for fall-through elision. stencils_arm64_macho.inc is
  generated and checked in; macOS execution is validated by CI.
- aarch64 ELF path validated locally: full execution-tests binary
  cross-compiled (clang-20 --target=aarch64-linux-gnu) and run under
  qemu-aarch64 — 11397 assertions pass, including every tbc-jit
  pseudo-backend section (movz/movk patching, b imm26 branches, helper
  range thunks).
- Guard the x86-64 relaxation peephole per-arch (caught by the cross
  build's -Werror).
- tools/stencils/README.md + tbc README section documenting the JIT.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NB22eBwKooQMBuwK4jMH2k
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NB22eBwKooQMBuwK4jMH2k
GCC-degrade-path run caught the strict-mode throw surfacing at
registerFunction rather than at the first call; wrap the whole sequence
in the assertion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NB22eBwKooQMBuwK4jMH2k
- tbc README: add JIT-specific follow-ups (tiering measurement, perf-map/
  GDB-JIT emission, macOS execution validation, CI stencil freshness
  check, native-ABI entry stencils, Windows posture) and link the typed
  call-thunk follow-up to the external-call benchmark finding.
- docs/options.md: document the tbc options (tbc.mode and the previously
  undocumented interpreter/lowering toggles); refresh the engine.backend
  value list.
- Root README: mention the JIT alongside the bytecode interpreter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NB22eBwKooQMBuwK4jMH2k

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracing Benchmark

Details
Benchmark suite Current: b63ee21 Previous: f40ce48 Ratio
tiered_twotier_addOne 3.8205 ms (± 212.131) 3.44247 ms (± 155.265) 1.11
tiered_singletier_addOne 3.69826 ms (± 146.859) 3.3618 ms (± 130.456) 1.10
single_compile_mlir_addOne 3.68895 ms (± 171.112) 3.43613 ms (± 177.952) 1.07
single_compile_cpp_addOne 21.4132 ms (± 655.556) 26.4631 ms (± 711.56) 0.81
single_compile_bc_addOne 62.0028 us (± 13.602) 98.0348 us (± 27.4974) 0.63
tiered_twotier_sumLoop 5.6845 ms (± 233.364) 5.61799 ms (± 179.52) 1.01
tiered_singletier_sumLoop 6.02305 ms (± 118.079) 5.76543 ms (± 418.806) 1.04
single_compile_mlir_sumLoop 6.05941 ms (± 135.079) 5.767 ms (± 406.338) 1.05
single_compile_cpp_sumLoop 22.9822 ms (± 384.867) 27.258 ms (± 795.55) 0.84
single_compile_bc_sumLoop 123.309 us (± 12.4626) 132.325 us (± 17.4704) 0.93
exec_bc_addOne 34.8698 ns (± 5.20299) 40.833 ns (± 5.46623) 0.85
exec_mlir_addOne 416.843 ns (± 16.7902) 312.181 ns (± 13.3101) 1.34
exec_cpp_addOne 3.66189 ns (± 0.581281) 3.96003 ns (± 0.49718) 0.92
exec_interpreted_addOne 37.0594 ns (± 4.77785) 39.6466 ns (± 2.09221) 0.93
ssa_add 196.362 ns (± 19.2174) 178.726 ns (± 10.343) 1.10
ssa_ifThenElse 445.018 ns (± 31.0425) 451.646 ns (± 33.9467) 0.99
ssa_deeplyNestedIfElse 1.26811 us (± 78.5025) 1.14266 us (± 62.247) 1.11
ssa_loop 478.4 ns (± 41.6017) 473.159 ns (± 27.2745) 1.01
ssa_ifInsideLoop 1008.0199999999999 ns (± 65194.99999999999) 890.244 ns (± 76.3035) 1.13
ssa_loopDirectCall 486.5 ns (± 44.7628) 510.363 ns (± 88.0778) 0.95
ssa_pointerLoop 578.48 ns (± 36.7268) 569.753 ns (± 27.8016) 1.02
ssa_staticLoop 440.096 ns (± 25.0542) 422.875 ns (± 46.3699) 1.04
ssa_fibonacci 510.713 ns (± 71.1305) 503.066 ns (± 51.0695) 1.02
ssa_gcd 448.499 ns (± 39.6278) 454.45 ns (± 52.9235) 0.99
ir_add 856.868 ns (± 65.7648) 743.704 ns (± 43.0261) 1.15
ir_ifThenElse 1.75685 us (± 119.143) 1.56324 us (± 129.394) 1.12
ir_deeplyNestedIfElse 3.86259 us (± 279.741) 3.33141 us (± 266.713) 1.16
ir_loop 1.82134 us (± 129.524) 1.61495 us (± 125.262) 1.13
ir_ifInsideLoop 3.92963 us (± 1.15744) 2.77107 us (± 242.983) 1.42
ir_loopDirectCall 2.07018 us (± 222.915) 1.81245 us (± 144.862) 1.14
ir_pointerLoop 2.2606 us (± 177.704) 2.01834 us (± 171.55) 1.12
ir_staticLoop 1.58698 us (± 121.872) 1.41826 us (± 91.8721) 1.12
ir_fibonacci 1.92535 us (± 119.587) 1.72537 us (± 118.025) 1.12
ir_gcd 1.68506 us (± 167.378) 1.50566 us (± 228.854) 1.12
ir_nestedIf10 8.96743 us (± 261.879) 7.84665 us (± 845.15) 1.14
ir_nestedIf100 100.269 us (± 3.36443) 88.6726 us (± 6.54239) 1.13
ir_chainedIf10 13.9143 us (± 794.806) 11.9919 us (± 1.48366) 1.16
ir_chainedIf100 181.377 us (± 4.4192) 168.051 us (± 8.52439) 1.08
exec_mlir_add 12.6648 ns (± 4.08591) 11.0862 ns (± 1.14504) 1.14
exec_mlir_fibonacci 29.0916 us (± 2.5367) 15.6243 us (± 2.32465) 1.86
exec_mlir_sum 697.937 us (± 29.1324) 606.846 us (± 40.7026) 1.15
exec_cpp_add 5.36992 ns (± 0.95018) 4.77289 ns (± 0.792908) 1.13
exec_cpp_fibonacci 77.3181 us (± 5.30606) 48.5936 us (± 4.5575) 1.59
exec_cpp_sum 16.5244 ms (± 72.6445) 11.4598 ms (± 71.0041) 1.44
exec_bc_add 41.0893 ns (± 5.82849) 53.5135 ns (± 1.24967) 0.77
exec_bc_fibonacci 327.726 us (± 6.52208) 432.118 us (± 13.2637) 0.76
exec_bc_sum 71.3435 ms (± 761.015) 95.4766 ms (± 189.461) 0.75
exec_tbc_add 28.8603 ns (± 2.36363) 27.8693 ns (± 3.76267) 1.04
exec_tbc_fibonacci 141.376 us (± 4.37266) 144.856 us (± 9.65742) 0.98
exec_tbc_sum 43.0128 ms (± 94.977) 36.7237 ms (± 71.1662) 1.17
exec_asmjit_add 2.94321 ns (± 0.204662) 3.55782 ns (± 0.729931) 0.83
exec_asmjit_fibonacci 13.7842 us (± 1.56906) 14.3868 us (± 1.69882) 0.96
exec_asmjit_sum 3.75612 ms (± 13.3783) 2.80271 ms (± 21.1561) 1.34
exec_bc_add_passesOff 40.6364 ns (± 3.70372) 55.7061 ns (± 11.6292) 0.73
exec_bc_add_passesOn 40.106 ns (± 0.844161) 55.1404 ns (± 7.17841) 0.73
exec_bc_fibonacci_passesOff 327.737 us (± 5.64689) 430.671 us (± 10.1648) 0.76
exec_bc_fibonacci_passesOn 309.919 us (± 8.39899) 390.329 us (± 13.4303) 0.79
exec_bc_sum_passesOff 71.3267 ms (± 692.546) 95.4237 ms (± 432.425) 0.75
exec_bc_sum_passesOn 63.1463 ms (± 488.347) 79.7055 ms (± 287.679) 0.79
exec_tbc_add_passesOff 28.2451 ns (± 1.0143) 27.3588 ns (± 4.46378) 1.03
exec_tbc_add_passesOn 28.9539 ns (± 2.55106) 26.6915 ns (± 3.04041) 1.08
exec_tbc_fibonacci_passesOff 141.297 us (± 2.83359) 145.24 us (± 11.0975) 0.97
exec_tbc_fibonacci_passesOn 141.621 us (± 4.07885) 143.12 us (± 7.02762) 0.99
exec_tbc_sum_passesOff 43.0259 ms (± 95.9382) 36.7523 ms (± 125.509) 1.17
exec_tbc_sum_passesOn 39.408 ms (± 228.933) 33.9784 ms (± 68.6602) 1.16
exec_asmjit_add_passesOff 2.93482 ns (± 0.170829) 3.53975 ns (± 0.500097) 0.83
exec_asmjit_add_passesOn 2.94935 ns (± 0.229106) 3.6568 ns (± 0.895016) 0.81
exec_asmjit_fibonacci_passesOff 14.1095 us (± 2.65127) 14.5441 us (± 2.24492) 0.97
exec_asmjit_fibonacci_passesOn 14.2122 us (± 1.9883) 14.3295 us (± 1.43398) 0.99
exec_asmjit_sum_passesOff 3.75622 ms (± 38.3388) 2.79484 ms (± 24.0295) 1.34
exec_asmjit_sum_passesOn 3.74913 ms (± 23.432) 2.80573 ms (± 35.3542) 1.34
exec_bc_add_noRegAlloc 40.2715 ns (± 1.70514) 54.7084 ns (± 6.0781) 0.74
exec_bc_add_regAlloc 40.0686 ns (± 1.57065) 54.7144 ns (± 6.20036) 0.73
exec_bc_fibonacci_noRegAlloc 330.236 us (± 11.2736) 433.338 us (± 14.1354) 0.76
exec_bc_fibonacci_regAlloc 329.948 us (± 10.0893) 433.108 us (± 13.4909) 0.76
exec_bc_sum_noRegAlloc 72.9027 ms (± 136.592) 95.5085 ms (± 227.227) 0.76
exec_bc_sum_regAlloc 71.9919 ms (± 1.57105) 95.4802 ms (± 174.487) 0.75
exec_bc_add_call 40.7607 ns (± 3.87051) 55.0564 ns (± 6.92496) 0.74
exec_bc_add_switch 39.2882 ns (± 2.90946) 41.3202 ns (± 4.98038) 0.95
exec_bc_add_threaded 36.7715 ns (± 2.77091) 38.9284 ns (± 4.11859) 0.94
exec_bc_fibonacci_call 328.172 us (± 7.25286) 432.62 us (± 13.9514) 0.76
exec_bc_fibonacci_switch 316.139 us (± 9.04894) 332.087 us (± 10.4208) 0.95
exec_bc_fibonacci_threaded 392.622 us (± 10.3678) 436.857 us (± 12.4801) 0.90
exec_bc_sum_call 71.7598 ms (± 1.3946) 95.4494 ms (± 235.29) 0.75
exec_bc_sum_switch 69.8101 ms (± 1.80618) 69.4439 ms (± 2.71317) 1.01
exec_bc_sum_threaded 83.8376 ms (± 1.103) 86.6076 ms (± 717.139) 0.97
exec_bc_add_threaded_noReuse 37.0194 ns (± 2.81709) 39.3206 ns (± 4.75011) 0.94
exec_bc_add_threaded_reuse 30.5146 ns (± 3.02793) 35.2867 ns (± 5.26744) 0.86
exec_bc_fibonacci_threaded_noReuse 391.923 us (± 8.23418) 435.479 us (± 10.6901) 0.90
exec_bc_fibonacci_threaded_reuse 392.174 us (± 8.10203) 435.818 us (± 10.6278) 0.90
exec_bc_sum_threaded_noReuse 83.7314 ms (± 867.906) 86.535 ms (± 741.907) 0.97
exec_bc_sum_threaded_reuse 83.4497 ms (± 49.3247) 86.4911 ms (± 724.446) 0.96
exec_bc_add_threaded_noSuperinstr 36.9831 ns (± 3.22363) 39.9908 ns (± 5.56132) 0.92
exec_bc_add_threaded_superinstr 36.7344 ns (± 2.94987) 39.5102 ns (± 4.32363) 0.93
e2e_tiered_bc_to_mlir 3774.04 us (± 107020) 3424.57 us (± 104297) 1.10
e2e_single_mlir 6.09479 ms (± 136.575) 6.10746 ms (± 277.545) 1.00
trace_add 2.55756 us (± 254.897) 2.32997 us (± 284.458) 1.10
completing_trace_add 2.58275 us (± 259.784) 2.32121 us (± 238.704) 1.11
trace_ifThenElse 9.3109 us (± 1.30405) 8.84805 us (± 1.46534) 1.05
completing_trace_ifThenElse 5.13112 us (± 408.034) 4.51046 us (± 495.027) 1.14
trace_deeplyNestedIfElse 26.3231 us (± 4.21665) 26.1408 us (± 3.22932) 1.01
completing_trace_deeplyNestedIfElse 13.8746 us (± 919.879) 12.6294 us (± 1.79333) 1.10
trace_loop 9.04553 us (± 1.09741) 8.56061 us (± 1.2196) 1.06
completing_trace_loop 5.22235 us (± 446.877) 4.66355 us (± 478.542) 1.12
trace_ifInsideLoop 17.2518 us (± 2.07131) 16.8386 us (± 3.29449) 1.02
completing_trace_ifInsideLoop 9.50418 us (± 844.796) 8.37888 us (± 1.24115) 1.13
trace_loopDirectCall 9.02837 us (± 678.95) 8.83234 us (± 1.32496) 1.02
completing_trace_loopDirectCall 5.26347 us (± 650.794) 4.73988 us (± 531.606) 1.11
trace_pointerLoop 14.7084 us (± 1.13774) 14.0468 us (± 2.23189) 1.05
completing_trace_pointerLoop 11.4385 us (± 1.56331) 10.034 us (± 1.62767) 1.14
trace_staticLoop 7.65236 us (± 373.723) 7.26519 us (± 739.036) 1.05
completing_trace_staticLoop 7.63234 us (± 406.122) 7.34913 us (± 1.20138) 1.04
trace_fibonacci 10.4937 us (± 1.17907) 10.0727 us (± 1.82544) 1.04
completing_trace_fibonacci 6.81912 us (± 957.25) 6.11759 us (± 1.03048) 1.11
trace_gcd 8.09877 us (± 591.071) 7.90518 us (± 1.21079) 1.02
completing_trace_gcd 4.32146 us (± 460.485) 3.92041 us (± 451.136) 1.10
trace_nestedIf10 40.8317 us (± 5.75133) 38.5832 us (± 7.57042) 1.06
completing_trace_nestedIf10 40.8805 us (± 5.2254) 38.1096 us (± 6.35015) 1.07
trace_nestedIf100 1.33405 ms (± 26.5775) 1.36503 ms (± 44.0102) 0.98
completing_trace_nestedIf100 1.41404 ms (± 26.7053) 1.37896 ms (± 53.9371) 1.03
trace_chainedIf10 96.9094 us (± 8.93641) 99.3859 us (± 11.5858) 0.98
completing_trace_chainedIf10 53.0828 us (± 7.07023) 48.8038 us (± 8.12306) 1.09
trace_chainedIf100 4.09924 ms (± 43.1165) 4.45164 ms (± 51.5101) 0.92
completing_trace_chainedIf100 2.45686 ms (± 27.711) 2.27347 ms (± 48.137) 1.08
comp_mlir_add 6.12595 ms (± 176.348) 6.00104 ms (± 325.341) 1.02
comp_mlir_ifThenElse 6.84481 ms (± 186.631) 7.00804 ms (± 905.718) 0.98
comp_mlir_deeplyNestedIfElse 5.73258 ms (± 161.777) 5.52347 ms (± 227.831) 1.04
comp_mlir_loop 7.7892 ms (± 176.341) 8.29743 ms (± 330.108) 0.94
comp_mlir_ifInsideLoop 27.7481 ms (± 325.727) 30.5552 ms (± 1.07569) 0.91
comp_mlir_loopDirectCall 12.0394 ms (± 259.069) 12.8359 ms (± 474.872) 0.94
comp_mlir_pointerLoop 26.9279 ms (± 274.733) 29.4418 ms (± 351.512) 0.91
comp_mlir_staticLoop 5.70189 ms (± 133.519) 5.19965 ms (± 204.884) 1.10
comp_mlir_fibonacci 10.7648 ms (± 228.445) 11.6453 ms (± 355.292) 0.92
comp_mlir_gcd 9.7842 ms (± 206.827) 10.4805 ms (± 390.024) 0.93
comp_mlir_nestedIf10 10.8599 ms (± 225.366) 11.1312 ms (± 381.003) 0.98
comp_mlir_nestedIf100 25.3402 ms (± 814.661) 26.4105 ms (± 355.242) 0.96
comp_mlir_chainedIf10 11.2132 ms (± 207.488) 10.6172 ms (± 692.589) 1.06
comp_mlir_chainedIf100 59.6641 ms (± 454.582) 22.0431 ms (± 401.566) 2.71
comp_cpp_add 21.6056 ms (± 480.638)
comp_cpp_ifThenElse 21.6895 ms (± 480.162)
comp_cpp_deeplyNestedIfElse 22.7124 ms (± 484.4)
comp_cpp_loop 21.9059 ms (± 475.639)
comp_cpp_ifInsideLoop 22.698 ms (± 465.421)
comp_cpp_loopDirectCall 22.1783 ms (± 485.687)
comp_cpp_pointerLoop 22.3976 ms (± 550.389)
comp_cpp_staticLoop 21.7478 ms (± 455.558)
comp_cpp_fibonacci 21.9083 ms (± 441.769)
comp_cpp_gcd 22.0478 ms (± 475.297)
comp_cpp_nestedIf10 24.8946 ms (± 541.327)
comp_cpp_nestedIf100 57.4819 ms (± 502.818)
comp_cpp_chainedIf10 27.1457 ms (± 495.484)
comp_cpp_chainedIf100 87.0887 ms (± 704.97)
comp_bc_add 11.3999 us (± 2.29364)
comp_bc_ifThenElse 14.8491 us (± 3.1196)
comp_bc_deeplyNestedIfElse 20.7133 us (± 2.58874)
comp_bc_loop 14.886 us (± 2.71112)
comp_bc_ifInsideLoop 18.6447 us (± 3.64045)
comp_bc_loopDirectCall 15.1323 us (± 2.62169)
comp_bc_pointerLoop 16.3093 us (± 2.76895)
comp_bc_staticLoop 14.1993 us (± 3.20244)
comp_bc_fibonacci 15.2635 us (± 2.66626)
comp_bc_gcd 13.8447 us (± 2.35424)
comp_bc_nestedIf10 39.7305 us (± 6.56122)
comp_bc_nestedIf100 283.474 us (± 7.43136)
comp_bc_chainedIf10 53.737 us (± 7.41373)
comp_bc_chainedIf100 452.304 us (± 9.88686)
comp_tbc_add 2.01524 us (± 146.936)
comp_tbc_ifThenElse 4.4709 us (± 432.877)
comp_tbc_deeplyNestedIfElse 9.98292 us (± 721.449)
comp_tbc_loop 4.43595 us (± 354.649)
comp_tbc_ifInsideLoop 7.70818 us (± 567.481)
comp_tbc_loopDirectCall 4.70621 us (± 467.749)
comp_tbc_pointerLoop 5.96421 us (± 355.977)
comp_tbc_staticLoop 5.34118 us (± 365.234)
comp_tbc_fibonacci 5.04983 us (± 650.575)
comp_tbc_gcd 3.81973 us (± 269.709)
comp_tbc_nestedIf10 30.1303 us (± 2.19224)
comp_tbc_nestedIf100 291.911 us (± 6.48745)
comp_tbc_chainedIf10 38.0531 us (± 4.02679)
comp_tbc_chainedIf100 407.358 us (± 7.43445)
comp_asmjit_add 17.2893 us (± 3.90377)
comp_asmjit_ifThenElse 26.3066 us (± 6.59685)
comp_asmjit_deeplyNestedIfElse 35.9225 us (± 5.71542)
comp_asmjit_loop 26.4154 us (± 6.82451)
comp_asmjit_ifInsideLoop 34.0817 us (± 6.87674)
comp_asmjit_loopDirectCall 29.1259 us (± 6.929)
comp_asmjit_pointerLoop 31.6221 us (± 6.68343)
comp_asmjit_staticLoop 24.4511 us (± 7.10012)
comp_asmjit_fibonacci 29.6823 us (± 8.96493)
comp_asmjit_gcd 26.9043 us (± 6.30915)
comp_asmjit_nestedIf10 61.0246 us (± 7.21237)
comp_asmjit_nestedIf100 494.929 us (± 18.2761)
comp_asmjit_chainedIf10 78.1102 us (± 9.87836)
comp_asmjit_chainedIf100 632.547 us (± 16.7297)

This comment was automatically generated by workflow using github-action-benchmark.

claude added 3 commits July 8, 2026 16:51
The benchmark runner used Ubuntu 24.04's default clang (18), which
cannot execute copy-and-patch stitched code (preserve_none needs
Clang 19+), so the new exec_tbc_*_{interp,jit} A/B benchmarks were
silently skipped and never published to the dashboard. Install
clang-21 from the LLVM apt repo, mirroring pr.yml's install step.

Note: the compiler bump shifts all published benchmark baselines; the
first run after merge establishes new reference numbers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NB22eBwKooQMBuwK4jMH2k
main added BAND_b/BOR_b/BXOR_b (02eb413, val<bool> fuzz domains) after
the tables were generated; the merged enum therefore contained opcodes
with no stencil. The name-keyed resolution degraded exactly as designed
(module-level interpreter bail-out with a precise reason — the opcodes
were inserted mid-enum, so an order-indexed table would have silently
mis-executed every subsequent opcode), and the strict tbc-jit fuzz peer
turned it into a loud CI failure. Regenerating adds the three stencils
on every target (339 entries).

The CI fuzz replay gate now passes locally: 2000/2000 generated
programs agree across all backends including tbc-jit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NB22eBwKooQMBuwK4jMH2k
@PhilippGrulich PhilippGrulich marked this pull request as ready for review July 11, 2026 06:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants