kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

JIT (planned work)

This roadmap covers the future of kit's in-process JIT: the append-only incremental linker, function-level hot reload built on top of it, the managed-runtime (Go-style) codegen/JIT contracts, and the remaining cross-host and parity gaps in kit run / kit dbg. The implemented design lives in ../JIT.md; the linker and incremental-link mechanics are in ../LINK.md and LINKER.md. This document records only what is still planned, the open problems, and the order we intend to build them in.

Baseline (the starting point): the JIT mapper (src/link/link_jit.c) reserves one contiguous region, copies segments, applies relocations against the runtime base, and flips to W^X final perms; ELF and Mach-O reloc-apply (cross-TU GOT, weak-undef proximity, far-call stubs, ELF IFUNC pre-resolution) are green, and the inspector surface (kit_jit_view, kit_jit_addr_to_sym, kit_jit_sym_iter_*) is wired. Append-only extension already increments a JIT generation (kit_jit_generation) and stages relocations via link_append_reloc_slot. Reload, managed-runtime hooks, and the regression harness are not yet built.

1. Function-level hot reload

Hot reload adds replacement on top of append-only incremental linking. In v1 only functions can be replaced; data symbols, TLS, type layouts, initializers, destructors, and object lifetime are out of scope. The first usable milestone: in dbg, reload a global function while the worker is stopped, existing function pointers keep working, new calls hit the new body, and old frames return safely.

1.1 Core idea — stable entry, indirected body

Append-only linking can already add a new function body. Reload adds a stable function entry that indirects through a slot to the current body:

foo entry/trampoline  ->  foo.slot  ->  current foo body

Reloading compiles and appends a new body, relocates it, then atomically updates foo.slot. Existing pointers to foo stay valid because they point at the stable entry, not a body generation. The baseline patches one pointer-sized cell, not every call site.

1.2 Indirection is opt-in

The trampoline cost is real and pointless for normal kit run. Gate it behind a JIT mode (KIT_JIT_INDIRECT_EXPORTED_FUNCS vs KIT_JIT_INDIRECT_NONE) so AOT executable links are unaffected. v1 restricts reload to global C-linkage functions visible to kit_jit_lookup; static functions need a stable synthetic identity keyed on the containing TU before they qualify.

1.3 ABI compatibility gate

Reload must not change the ABI a caller already compiled against. The C frontend should emit a compact per-definition ABI signature (target arch/os, call conv, variadic flag, return class+size, arg classes+sizes; fixed KIT_ABI_MAX_ARGS bound, no VLA) so the linker can verify runtime-callability without re-deriving C types. Functions whose signature exceeds the bound are marked non-reloadable until a heap-backed encoding exists. Missing/non-C signatures reject reload unless the user explicitly opts into unchecked replacement.

1.4 Replacement-object restrictions (v1)

A replacement object may contain the new body, private helpers used only by it, read-only literals it needs, debug sections, and undefined refs to already-linked or resolver symbols. It may not contain new writable globals, TLS, ctors/dtors, public definitions other than the target, or colliding strong definitions. This keeps reload function-only in practice, not just in name.

1.5 Old-generation lifetime

After a slot update, old bodies stay mapped. Even with the worker stopped, a live frame may be inside the old function, so continuing must be valid: existing frames finish in the old body, new calls enter the new one. v1 never reclaims old generations until kit_jit_free; later, retire a generation only when the debugger/runtime can prove no stopped or running frame has a PC or return address inside it. Never unmap old code immediately after publishing.

1.6 Debugger integration

dbg must distinguish address breakpoints from symbol/source ones:

Every reload bumps the JIT generation; kit_jit_view rebuilds on mismatch. The rebuilt view keeps old debug info so backtraces from old frames still resolve, while name-to-address lookup prefers the latest generation.

1.7 Transactional publish and failure behavior

Reload is all-or-nothing. ABI mismatch, disallowed data/TLS/init arrays, unresolved symbols, out-of-capacity, or relocation failure all reject and leave the old body active. Pages committed before a failure may remain as dead space, but no public symbol or slot may ever point at them.

1.8 Patch-site index (later, performance only)

The correctness baseline needs no caller patching. A patch-site index built from durable relocation records (target sym -> apply ids, owner input -> apply ids, write section -> apply ids) enables a later fast mode that patches only call sites targeting the reloaded symbol, plus future non-function slot fixups. For v1, build the structures but use them only in assertions/tests.

1.9 Reload work items

2. Go-runtime-style codegen / JIT support

Go is statically typed, so the gap is not dynamic typing in ../CODEGEN.md's KitCg — it is the managed runtime model: precise GC, goroutines, managed stacks, panic/defer/recover, and a long-lived JIT image whose code and metadata evolve safely. Principles: keep CG typed; lower source concepts to storage types plus runtime metadata; make managed behavior explicit rather than inferred late; keep AOT and ordinary C/Toy/Wasm JIT unchanged by default (managed features are opt-in via code options or function attributes); keep all runtime services on context/session structs, never global state. The first useful milestone is far smaller than "compile Go": a tiny managed frontend that allocates traced objects, hits a safepoint, lets the host enumerate roots from a JIT stack map, and calls in through a C-callable trampoline.

2.1 CG interface extensions

2.2 JIT interface extensions

2.3 Managed-runtime sequence

  1. Managed pointer / address-space policy + explicit safepoint records.
  2. Emit and query compact stack maps from the JIT image.
  3. Managed allocation and write-barrier intrinsics.
  4. Managed-stack function attributes + stack-check lowering.
  5. Publish runtime metadata transactionally with JIT appends.
  6. Trap/check tables for panic lowering.
  7. Function replacement/lifetime on top of hot reload (§1).
  8. Broaden debugger/session APIs for multi-threaded coordination.

3. Remaining JIT TODOs

3.1 Driver — kit run

3.2 Inspector / debugger surface

3.3 Memory mapping / executable allocator

3.4 Tests