# Icicle dependency and browser-portability report - Investigated: 2026-07-24 - Icicle revision: `6f53b6016e63ddcff80a105b8e5ad37caf7a6ed3` - Icicle commit date: 2026-06-29 - Supported Ghidra revision: `50230050fa58bd40d5a96cab9c167fc55bc92a76` - Rust target: `wasm32-unknown-unknown` - Rust toolchain: 1.93.0 This report supports [ADR-0001](../decisions/0001-adopt-selected-icicle-components.md). The reproducible native probe lives in `experiments/icicle`. ## Native result The selected Icicle revision builds natively on macOS for `pcode`, `sleigh-runtime`, `sleigh-compile`, `icicle-mem`, `icicle-cpu`, `icicle-linux`, and `icicle-vm`. Using Icicle's supported AArch64 Ghidra specification, the probe decoded and interpreted `mov x0, #42` followed by `add x0, x0, #1`. It observed `x0 = 43` and `pc = 0x1008`. JIT, JIT memory, shadow stack, and recompilation were disabled, so this result exercises the SLEIGH-to-P-code interpreter path. ## Crate assessment | Component | Native | Upstream wasm32 | wasm32 after two narrow patches | Decision | |---|---:|---:|---:|---| | `pcode` | Pass | Pass | Pass | Adopt as an input format behind a project adapter | | `sleigh-parse` | Pass | Pass | Pass | Adopt transitively | | `sleigh-runtime` | Pass | Blocked by `ahash` RNG | Pass | Adopt | | `sleigh-compile` | Pass | Blocked transitively | Pass | Use during build/installation; avoid browser filesystem paths | | `icicle-mem` | Pass | Blocked by `ahash` RNG | Pass | Use initially behind a project memory adapter | | `icicle-cpu` | Pass | Also has a 48-bit `usize` sentinel | Pass | Adopt lifter and interpreter selectively | | `icicle-linux` | Pass | Blocked transitively | Pass | Mine ABI/architecture behavior; replace host-coupled services | | `icicle-jit` | Pass | Fails in native executable-memory crates | Not pursued | Reject for browser builds | | `icicle-vm` | Pass | Pulls `icicle-jit` unconditionally | Not pursued | Do not expose as the browser runtime boundary | “Pass” means `cargo check` completed for the selected target. It does not prove browser runtime behavior. ## Exact wasm blockers The unmodified selective build was attempted with: ```sh cargo +1.93.0 check --locked \ --target wasm32-unknown-unknown \ -p pcode -p sleigh-runtime -p sleigh-compile \ -p icicle-mem -p icicle-cpu -p icicle-linux ``` It first fails because workspace-default `ahash` enables runtime entropy through `getrandom`, whose bare-wasm backend must be selected explicitly. After configuring `getrandom`'s `wasm_js` feature/backend, `icicle-cpu` fails because `UNKNOWN_BLOCK` uses the 48-bit literal `0xbadbadbadbad` as a `usize`; wasm32 has a 32-bit `usize`. The temporary evidence patch was limited to: 1. Enable `getrandom`'s `wasm_js` feature for the browser target and compile with `--cfg getrandom_backend="wasm_js"`. 2. Replace the diagnostic-only `UNKNOWN_BLOCK` value with `usize::MAX`. The exact diff is retained in `experiments/icicle/patches/0001-wasm-portability.patch`. The successful selective build additionally set: ```sh RUSTFLAGS='--cfg getrandom_backend="wasm_js"' ``` After those changes, all seven selective crates in the command above compile for wasm32. Building `icicle-vm` still fails in `region`, reached through its unconditional `icicle-vm → icicle-jit → cranelift-jit → region` dependency. `region` expects native virtual-memory allocation/protection functions that do not exist for `wasm32-unknown-unknown`. Setting `Config::enable_jit = false` is only a runtime switch and does not remove the dependency or the `JIT` field from `Vm`. ## Browser-runtime assumptions not caught by `cargo check` - `sleigh-compile`'s language builder reads `.ldefs`, `.pspec`, and `.slaspec` files through `std::fs`. The lower-level `from_data` entry point accepts in-memory source text and is the browser-compatible integration seam. - `icicle-linux` contains direct host filesystem reads and `std::thread::sleep`. These compile for wasm but cannot be the browser host boundary. - `icicle-cpu::utils::UdpWriter` directly uses `std::net::UdpSocket`; diagnostic networking must remain unused or be gated. - `icicle-mem` uses a software page map, copy-on-write pages, per-byte permissions, raw-pointer TLB entries, and a separate guest-to-host translation. It is suitable for an interpreter spike but is not direct Memory64 guest addressing. - `icicle-vm` reads executables/specifications from paths in several helpers. Browser loading must pass bytes and project-owned services explicitly. ## Licenses Icicle is `MIT OR Apache-2.0`. The investigated AArch64 Ghidra processor files are marked `GHIDRA` in that module's certification manifest; the Ghidra repository describes those files under Apache-2.0. The selected native dependency graph contains permissive dependencies; no GPL crate is required by the core path. The Ghidra source bundle must retain its Apache license and NOTICE if redistributed. It should be versioned as an explicit source/data input rather than copied without provenance. ## Recommended integration shape 1. Pin Icicle and the AArch64 specification as explicit, reviewable inputs. 2. Carry or upstream the two narrow wasm portability fixes. 3. Build a project-owned adapter from Icicle P-code into a normalized IR; do not make Icicle's `Vm` the public runtime API. 4. Start the interpreter with `icicle-mem` behind a project interface, then measure whether direct Memory64 storage warrants replacement. 5. Implement Linux and browser host services in project crates. Port only clearly useful Icicle ABI tables, AArch64 register/syscall context logic, and tests. 6. Replace the native Cranelift JIT with the planned project IR-to-Wasm backend.