{"id":50708972,"url":"https://github.com/darmie/beadie","last_synced_at":"2026-06-09T14:00:46.756Z","repository":{"id":348487071,"uuid":"1198305640","full_name":"darmie/beadie","owner":"darmie","description":"Beadie: Hot-function promotion broker for interpreter-to-JIT tiering","archived":false,"fork":false,"pushed_at":"2026-05-11T08:56:15.000Z","size":468,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-11T10:20:28.913Z","etag":null,"topics":["compiler","cranelift-jit","hot-function-replacement","hot-path","hot-reload","interpreter","jit","llvm","llvm-ir","rust","rust-lang"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/darmie.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-01T09:51:02.000Z","updated_at":"2026-05-11T08:56:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/darmie/beadie","commit_stats":null,"previous_names":["darmie/beadie"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/darmie/beadie","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darmie%2Fbeadie","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darmie%2Fbeadie/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darmie%2Fbeadie/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darmie%2Fbeadie/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/darmie","download_url":"https://codeload.github.com/darmie/beadie/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darmie%2Fbeadie/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34110012,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compiler","cranelift-jit","hot-function-replacement","hot-path","hot-reload","interpreter","jit","llvm","llvm-ir","rust","rust-lang"],"created_at":"2026-06-09T14:00:26.283Z","updated_at":"2026-06-09T14:00:46.718Z","avatar_url":"https://github.com/darmie.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# beadie\n\n[![CI](https://github.com/darmie/beadie/actions/workflows/ci.yml/badge.svg)](https://github.com/darmie/beadie/actions/workflows/ci.yml)\n[![Rust](https://img.shields.io/badge/rust-1.70%2B-orange.svg)](https://www.rust-lang.org)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\nHot-function promotion broker for interpreter-to-JIT tiering.\n\nBeadie sits between your interpreter and JIT compiler, automatically detecting hot functions and promoting them to native code via a background compilation thread. It supports single-backend and multi-tier (e.g. Cranelift baseline + LLVM optimizing) compilation strategies.\n\n## Architecture\n\n```\nInterpreter thread              Broker thread\n      │                              │\n  on_invoke()                        │\n      │                              │\n  tick + policy ── threshold ──► compile job\n      │            crossed           │\n      │                         compile(bead, def)\n      │                              │\n  compiled?  ◄── install ───── *mut () code ptr\n      │\n  dispatch_native()\n```\n\n```\nBead state machine:\n\n  Interpreted ──► Queued ──► Compiling ──► Compiled\n       ▲                                      │\n       └───────── invalidate / deopt ◄────────┘\n```\n\n## Usage\n\n```toml\n[dependencies]\nbeadie = \"0.2\"\n\n# Optional JIT backends\nbeadie = { version = \"0.2\", features = [\"cranelift\"] }\nbeadie = { version = \"0.2\", features = [\"llvm\"] }\n```\n\n### Basic promotion\n\n```rust\nuse beadie::{Beadie, Bead};\nuse std::sync::Arc;\n\nstruct Function {\n    bytecode: Vec\u003cu8\u003e,\n    bead: Arc\u003cBead\u003e,\n}\n\nlet beadie = Beadie::new(); // default: promote after 1,000 invocations\n\n// When the runtime creates a function, register it with beadie.\n// The core handle is an opaque pointer beadie stores but never dereferences —\n// use it to map back to your runtime's function object inside the compile closure.\nlet func = Function {\n    bytecode: load_bytecode(),\n    bead: beadie.register(core_ptr, None),\n};\n\n// Interpreter dispatch: on every call, ask beadie whether compiled code exists.\n// beadie tracks invocation count internally — once the threshold is crossed,\n// it submits the compile closure to its background broker thread.\nif let Some(code) = beadie.on_invoke(\u0026func.bead, |bead| {\n    // Called once, on the broker thread, when this function wins promotion.\n    // bead.core_handle() returns the (always-current) core pointer.\n    jit_compile(bead.core_handle())\n}) {\n    // Compiled code is ready — call the native entry point.\n    unsafe { call_native(code, args) };\n} else {\n    // Still warming up or compiling — keep interpreting.\n    interpret(\u0026func.bytecode, args);\n}\n```\n\n### Custom policy\n\n```rust\nuse beadie::{Beadie, ThresholdPolicy, TieredPolicy};\n\n// Fixed threshold\nlet b = Beadie::with_policy(ThresholdPolicy::new(500));\n\n// Two-tier thresholds (tier1 at 500, tier2 at 10,000)\nlet b = Beadie::with_policy(TieredPolicy::default());\n```\n\n### Backend adapter (typed compilation)\n\n```rust\nuse beadie::{BackendAdapter, JitBackend};\n\nlet adapter = BackendAdapter::new(my_backend);\nlet bound = adapter.register(core_ptr, None);\n\nif let Some(code) = adapter.on_invoke(\u0026bound, |bead| build_ir(bead)) {\n    dispatch_native(code, args);\n}\n```\n\n### Tiered compilation\n\n`TieredAdapter` supports any number of tiers. Each tier has its own promotion\npolicy and background worker thread. Backends are dispatched inside a single\ncompile closure indexed by tier:\n\n```rust\nuse beadie::{TieredAdapter, HotnessPolicy, ThresholdPolicy};\nuse std::ptr::null_mut;\n\nlet tiered = TieredAdapter::new(vec![\n    Box::new(ThresholdPolicy::new(1_000))  as Box\u003cdyn HotnessPolicy\u003e,  // tier 0: baseline\n    Box::new(ThresholdPolicy::new(10_000)) as Box\u003cdyn HotnessPolicy\u003e,  // tier 1: optimizing\n    Box::new(ThresholdPolicy::new(50_000)) as Box\u003cdyn HotnessPolicy\u003e,  // tier 2: aggressive\n]);\nlet bound = tiered.register(core_ptr, None);\n\n// The compile closure receives (tier_index, \u0026bead) — dispatch to the\n// appropriate backend based on tier.\nif let Some(code) = tiered.on_invoke(\u0026bound, |tier, bead| match tier {\n    0 =\u003e cranelift.compile(bead, build_baseline(bead)).unwrap_or(null_mut()),\n    1 =\u003e llvm.compile(bead, build_optimized(bead)).unwrap_or(null_mut()),\n    2 =\u003e llvm_aggressive.compile(bead, build_aggressive(bead)).unwrap_or(null_mut()),\n    _ =\u003e null_mut(),\n}) {\n    unsafe { call_native(code, args) };\n}\n```\n\n### On-stack replacement (OSR)\n\nStandard \"promote on next entry\" tiering doesn't help when the work happens\ninside a single long-running invocation — top-level scripts, event-loop\nhandlers, numeric kernels called from FFI. OSR lets the runtime transfer\na *currently-executing* interpreter frame into compiled code at a hot\nloop header.\n\n```rust\nuse beadie::{BackendAdapter, OsrBuild, OsrEntry, ThresholdPolicy};\n\nlet adapter = BackendAdapter::with_policy(backend, ThresholdPolicy::new(100));\nlet bound = adapter.register(core_ptr, None);\n\n// Same dispatch as on_invoke, but the factory also returns OSR entries\n// (one per hot loop header the JIT emits).\nif let Some(code) = adapter.on_invoke_osr(\u0026bound, |bead| {\n    let (def, osr_entries) = my_jit.compile_with_osr(bead);\n    OsrBuild { def, osr: osr_entries }\n}) {\n    unsafe { call_native(code, args) };\n}\n\n// Inside the interpreter's loop back-edge probe:\nif let Some(entry) = bound.bead().osr_entry(loop_header_id) {\n    // Reconstruct live interpreter state, transfer to native at `entry`.\n    runtime.transfer_to_native(entry);\n}\n```\n\n`bead.osr_entry(site)` is lock-free: state check → epoch-pinned acquire\nload → generation check → binary search on a small sorted slice.\nNon-OSR runtimes pay zero cost — the OSR slot stays null.\n\n#### Tier-up swaps with OSR\n\nFor tiered compilation that needs OSR available immediately after a\ntier-up (baseline → optimised), swap code and OSR atomically:\n\n```rust\n// Tier-2 compile finished — replace tier-1 code AND its OSR table.\nlet result = bound.swap_compiled_with_osr(\n    optimised_entry,\n    optimised_osr_entries,\n);\n```\n\nThe plain `swap_compiled` retires any existing OSR table (lookups return\n`None` until a new table is installed) — safe by construction, but no\nOSR until the next install. Use the `_with_osr` variant to keep OSR live\nacross the swap.\n\n### Cranelift configuration\n\n```rust\nuse beadie_cranelift::{CraneliftConfig, CraneliftBackend};\n\nlet backend = CraneliftConfig::new()\n    .opt_level(\"speed\")\n    .set(\"use_colocated_libcalls\", \"false\")\n    .set(\"is_pic\", \"false\")\n    .build()?;\n```\n\n### Runtime lifecycle\n\n```rust\n// GC moved the function object\nbead.update_core(new_ptr);\n\n// Function destroyed\nbead.invalidate();\n\n// Force recompilation of all hot functions (e.g. hot reload)\nb.reload_all();\n\n// Reclaim dead beads periodically\nb.prune();\n```\n\n## API\n\n### Core types\n\n| Type | Description |\n|---|---|\n| `Beadie\u003cP\u003e` | Orchestrator. Owns the bead chain and broker thread |\n| `Bead` | Atomic state machine representing a single function |\n| `Chain` | Thread-safe linked list of beads |\n| `Broker` | Background compilation worker thread |\n\n### Policies\n\n| Type | Description |\n|---|---|\n| `ThresholdPolicy` | Promote after N invocations (default: 1,000) |\n| `TieredPolicy` | Two thresholds for tier1/tier2 promotion |\n| `HotnessPolicy` | Trait for custom promotion strategies |\n\n### Backend layer\n\n| Type | Description |\n|---|---|\n| `JitBackend` | Trait for pluggable JIT compilers |\n| `BackendAdapter\u003cB, P\u003e` | Beadie wired to a single backend |\n| `BoundBead\u003cB\u003e` | Bead pre-wired to a specific backend |\n| `TieredAdapter` | N-tier compilation orchestrator |\n\n### OSR (on-stack replacement)\n\n| Type | Description |\n|---|---|\n| `OsrEntry { site, code }` | One native entry point at an opaque site key |\n| `OsrCompileResult` | Broker-level compile output: entry + OSR entries |\n| `OsrBuild\u003cD\u003e` | Adapter-level factory output: backend def + OSR entries |\n| `Bead::osr_entry(site)` | Lock-free lookup from a back-edge probe |\n| `*::on_invoke_osr` | OSR-aware dispatch (Beadie, BackendAdapter) |\n| `Bead::swap_compiled_with_osr` | Atomic tier-up swap (code + OSR together) |\n\n### Deoptimization\n\n| Type | Description |\n|---|---|\n| `DeoptPolicy` | Trait for bailout recovery strategies |\n| `AlwaysRecompilePolicy` | Always recompile on bailout |\n| `ThresholdDeoptPolicy` | Blacklist after N bailouts |\n| `ExponentialBackoffPolicy` | Increasing delay between recompile attempts |\n| `TieredDeoptPolicy` | Revert to tier1 before blacklisting |\n\n### Hot-path performance\n\n- **Compiled:** one `Acquire` load (branch-predicted)\n- **Cold:** one `Relaxed` fetch-add + one `Relaxed` load\n- Chain lock is never taken on the invoke path\n\n## Example\n\nThe [fib example](crates/beadie-cranelift/examples/fib.rs) demonstrates the\nfull pipeline — source AST, frontend compilation to MIR, interpreter, and\nbeadie-managed JIT promotion via Cranelift:\n\n```\ncargo run -p beadie-cranelift --example fib\n```\n\n`queue_ahead` lets you absorb backend compile latency by submitting the\ncompile job early. Tune it to your backend (Cranelift ~5ms, LLVM ~50ms):\n\n```\nfib(20) x 500 — recursive fibonacci, ~11M dispatches total\n\nconfig                       time        throughput\n------------------------ --------   ---------------\ninterpreter (no beadie)      2.1s      5.1M dispatch/s\nbeadie (qa=0)             522.8ms     20.9M dispatch/s\nbeadie (qa=500)           521.5ms     21.0M dispatch/s\nbeadie (qa=990)           523.8ms     20.9M dispatch/s\nbeadie (pre-compiled)     521.9ms     21.0M dispatch/s\n```\n\nBeadie's dispatch overhead is effectively zero — the pre-compiled baseline\n(every call hits the fast path) matches the tiered runs. The 4x speedup\nover raw interpretation is Cranelift's native codegen.\n\nEnable `RUST_LOG=beadie_core=debug` to trace bead state transitions,\nbroker compile times, and promotion decisions.\n\n## Projects using beadie\n\n| | Project | Description |\n|---|---|---|\n| \u003ca href=\"https://github.com/wrenlift/WrenLift\"\u003e\u003cimg src=\"using/wrenlift_logo.png\" alt=\"WrenLift\" width=\"auto\" height=\"60\"\u003e\u003c/a\u003e | **[WrenLift](https://github.com/wrenlift/WrenLift)** | Tiered JIT for the Wren language. Uses beadie for baseline → optimised promotion, OSR at loop back-edges, and deopt handling. |\n| \u003ca href=\"https://github.com/zyntax-project/zyntax\"\u003e\u003cimg src=\"using/zyn_logo.png\" alt=\"Zyntax\" width=\"auto\" height=\"60\"\u003e\u003c/a\u003e | **[Zyntax](https://github.com/zyntax-project/zyntax)** | Embeds beadie for hot-function JIT promotion. |\n| \u003ca href=\"https://github.com/rayzor-blade/rayzor\"\u003e\u003cimg src=\"using/rayzor_logo.svg\" alt=\"Rayzor\" width=\"auto\" height=\"60\"\u003e\u003c/a\u003e | **[Rayzor](https://github.com/rayzor-blade/rayzor)** | Embeds beadie for hot-function JIT promotion. |\n\nUsing beadie in your project? Open a PR adding it here.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarmie%2Fbeadie","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdarmie%2Fbeadie","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarmie%2Fbeadie/lists"}