Implementation Plan: Zero-Copy Vox Codegen
Overview
Section titled “Overview”This plan outlines the technical steps to transition Vox from a “Clone-Heavy” Rust backend to a “Zero-Copy” systems-native backend. This is Phase 1 of the Native Code Emission strategy.
1. Enrich HIR with Type Information
Section titled “1. Enrich HIR with Type Information”The primary blocker for smart codegen is that HIR nodes do not know their types. We store resolved types in a centralized map within the HirModule.
- Extend
HirModuleincrates/vox-compiler/src/hir/nodes/decl.rs:- Add
pub inferred_types: HashMap<Span, HirType>field.
- Add
- Update
Checkerincrates/vox-compiler/src/typeck/checker/mod.rs:- Add
inferred_typesmap to theCheckerstruct. - Write resolved types to the map during
check_exprinexpr.rs.
- Add
- Refactor
typecheck_hir:- Pass the module’s
inferred_typesto the checker. - Resolve borrow-checker conflicts by temporarily taking ownership of the map during checking.
- Pass the module’s
2. Implement Smart Ownership Tracking in Codegen
Section titled “2. Implement Smart Ownership Tracking in Codegen”Update vox-codegen to use type information to avoid unnecessary .clone() calls.
- Refactor Codegen Layers:
- Propagate
inferred_typesthroughemit_lib,emit_fn,emit_stmt, andemit_expr_with. - Update all call sites in
stmt_expr.rs,stmt_expr_tail.rs,workflow.rs, andhttp.rs.
- Propagate
- Optimize Identifiers:
- Check for
Copytypes: If the inferred type is a primitive (int,bool,float,char,dec), omit.clone().
- Check for
- Advanced String Optimization:
- Escape Analysis: If an identifier is passed to a function that takes a reference (e.g.,
str->&str), emit&norn.as_str()instead ofn.clone(). - Last-Use Detection: If the compiler can prove an identifier is used for the last time in a scope, “move” it instead of cloning.
- Escape Analysis: If an identifier is passed to a function that takes a reference (e.g.,
3. Native UI Prototype (Visus)
Section titled “3. Native UI Prototype (Visus)”Establish a pathway for non-React UI emission.
- Create
crates/vox-native-gui:- Define a
NativeRenderertrait. - Implement a
Slintoreguibackend that mapsHirJsxElementto native widgets.
- Define a
- Update
vox-codegen:- Add
--target=nativeflag to the CLI. - Branch the
Componentlowering path:HIR→WebIR(for React) vsHIR→NativeIR(for systems UI).
- Add
4. WASM Logic Offloading
Section titled “4. WASM Logic Offloading”Enable high-performance logic offloading to WASM for @pure functions.
- Identify
@pureperformance-critical functions:- Use the
@intrinsicdecorator (proposed in research) to flag these functions.
- Use the
- Lower to WASM:
- Use
vox-wasm-engineto compile these functions to.wasmblobs. - Emit Rust glue code in the server that calls into the WASM module instead of executing interpreted logic.
- Use
Verification
Section titled “Verification”- Benchmark: Run the
vox-benchsuite before and after the “Zero-Copy” changes. - Regression: Ensure
cargo test --workspacepasses, specifically checkingvox-codegentests that previously relied on.clone()behavior.
§2.1 Escape Analysis — grounded implementation plan (Workstream B, scoped 2026-06-05)
Section titled “§2.1 Escape Analysis — grounded implementation plan (Workstream B, scoped 2026-06-05)”Companion to vox-memory-model-audit-and-value-optimization-2026-06-05.md (its “Workstream B”). The interpreter CoW work landed; this is the codegen counterpart — eliminating .clone() in emitted Rust by borrowing when the callee only reads. Grounded in a code audit of the current ownership machinery.
Current state (verified)
Section titled “Current state (verified)”- Last-use / move analysis EXISTS —
codegen_rust/emit/usage.rs(UsageTracker::build/is_last_use); at last use,emit_ident_expremits a bare identifier (move). So the remaining clones are for non-last-use arguments (value reused later) — and those cannot be elided without the callee borrowing. OwnershipMode {Owned, Borrowed}(emit/ownership.rs) is threaded through emit.Borrowedis only ever set for ~9 hardcoded builtin string args (is_builtin_arg_borrowed,stmt_expr.rs:~903). User-defined function calls always passOwnershipMode::Owned(stmt_expr.rs:~565) → every reused argument clones.- Type info is available at emit time (
inferred_types: HashMap<Span, HirType>) inemit_ident_expr. - Generated function params are all owned (
workflow.rs:~181):name: String,items: Vec<T>— never&str/&[T]. There is no per-parameter ownership metadata onHirFn/HirParam.
Latent bug to fix first (cheap, safe)
Section titled “Latent bug to fix first (cheap, safe)”emit_ident_expr (stmt_expr.rs:~412) emits .as_str() unconditionally in Borrowed mode, ignoring the type. It’s currently masked because Borrowed only fires for string builtins, but ANY expansion to lists would emit .as_str() on a Vec → uncompilable.
- Make borrow emission type-aware:
String→.as_str()(or&n),Vec<T>→&n/n.as_slice(), else&n. Add a unit test pinning each. Do this BEFORE wideningBorrowed.
The feature (the real work)
Section titled “The feature (the real work)”- 1. Param-borrow metadata. Add per-parameter ownership to
HirFn(e.g.params_borrowable: Vec<bool>), computed by a body analysis: a param is borrowable iff the body only reads it (never moved into a return value, stored, mutated, or passed to an owning callee). MVP heuristic: borrowable forstr/list[T]params whose only uses are reads/borrowing-calls; conservative-owned otherwise. (Soundness: when unsure →Owned. Never borrow something the callee escapes.) - 2. Emit borrowed signatures. In
workflow.rsparam emission, emit&str/&[T]for borrowable params; adjust the body to deref as needed. - 3. Call-site lookup. Build a
HashMap<&str, &HirFn>(or reuse module function table) so the generic-call emitter (stmt_expr.rs:~565) can look up the callee and set per-argumentOwnershipMode::Borrowedfor borrowable params — emitting&xinstead ofx.clone()for non-last-use args. - 4. Replace the hardcoded builtin table with the same metadata mechanism where feasible (or leave the small table and layer user-fn inference on top).
Tests / verification (hook identified 2026-06-06)
Section titled “Tests / verification (hook identified 2026-06-06)”- Verification hook:
vox_codegen::codegen_rust::emit::emit_fn(func, Some(&hir.inferred_types), &[])is public and returns the emitted Rust string for one function (seetests/durability_compiles.rsfor the pattern). So escape-analysis emission is directly assertable at the unit level — no temp crate needed. - New
crates/vox-codegen/tests/escape_analysis_emit.rsusingemit_fn: assert a borrowablestrparam emitss: &strin the signature and the body uses bares(nots.clone()/s.as_str()); assert a negative case — a returned/operator-used param keepss: String(proves the soundness gate). Mixed andlist[T]→&[T]cases. - Compile-resolution safety net already exists: the
*_compiles.rstests (e.g.durability_compiles.rs,ai_fixture_bundle_compiles.rs) andgolden_ts_test.rsfail to compile if emitted paths/types break — run the fullcargo test -p vox-codegenafter the change. For the residual “does the generated body type-check with&strparams” risk, add one emit→cargo buildgolden for a borrowed-param script (heaviest, but the only check that catches a&str-vs-Stringmismatch directly). - Optional: generated-LoC
.clone()count before/after to quantify.
Risk / sequencing
Section titled “Risk / sequencing”Signature changes ripple (body deref, return cloning, nested calls), so land type-aware borrow fix → metadata+inference (conservative) → signatures → call-site borrows, test-gating each. Soundness rule: borrow only when provably read-only; default to owned. This is independent of the interpreter and can proceed on its own branch.
Last Updated: 2026-06-05 Status: In Progress (Workstream B / escape analysis grounded & scoped; not yet implemented)