VM Tiering & Code Cache
Source: crates/vm-codecache, crates/vm-runtime/src/execution.rs
Overview
Section titled “Overview”Ashen uses a tiered execution model for RISC-V contracts. New code starts in the interpreter and can be promoted to higher-performance tiers as it becomes “hot” (frequently executed). Gas accounting is tier-independent --- the same gas is charged regardless of which tier executes.
Enable Tiering
Section titled “Enable Tiering”Tiering is gated by both compile-time and runtime switches:
# Build with tiering supportcargo build --features std,tui,vm-tiering
# Enable at runtimeASHEN_VM_TIERING=1 ./target/debug/nodeIf ASHEN_VM_TIERING is unset, the node runs interpreter-only even when
compiled with vm-tiering.
Execution Tiers
Section titled “Execution Tiers”| Tier | Description | When Used |
|---|---|---|
| Interpreter | Decode-per-instruction reference tier; semantic authority | Cold code, short executions |
| JIT | Lazy predecode; caches basic blocks on first execution | Moderately-hot code |
| AOT | Eager predecode; entire program pre-decoded at load time | Hot code, production steady-state |
| Native | Cranelift JIT to native machine code (cranelift-native feature) | Compute-heavy contracts |
Interpreter Modes
Section titled “Interpreter Modes”The interpreter supports two sub-modes:
| Mode | Description | Best For |
|---|---|---|
Step | Execute one instruction at a time | Short executions, constrained memory |
BlockCache | Cache decoded basic blocks for reuse | Tight loops, repeated code paths |
Native Tier (Cranelift)
Section titled “Native Tier (Cranelift)”The cranelift-native feature enables compilation of hot basic blocks to native
machine code via Cranelift. If native compilation fails for a block (e.g.,
unsupported instruction), execution falls back to the interpreter for that block.
Tier Selection
Section titled “Tier Selection”When using the code cache, the runtime queries best_available_tier():
Aot (if cached) > Jit (if cached) > Interpreter (fallback)The fallback table for explicit tier requests:
| Requested | Cache Has | Effective |
|---|---|---|
| Aot | Aot | Aot |
| Aot | Jit | Jit |
| Aot | nothing | Interpreter |
| Jit | Jit+ | Jit |
| Jit | nothing | Interpreter |
| Interpreter | any | Interpreter |
Hot-Code Promotion
Section titled “Hot-Code Promotion”The HotCodeTracker monitors per-contract execution and promotes hot code
automatically. Each contract is tracked by its code_hash.
tracker.record(code_hash, gas_used) -> Option<Tier>On each call, the tracker increments call_count and accumulates total_gas.
Promotion triggers when call count exceeds a threshold:
| Threshold | Promotes To |
|---|---|
jit_threshold_calls | Tier::Jit |
aot_threshold_calls | Tier::Aot |
When promotion fires:
- JIT: Stores the program image in the code cache for lazy basic-block predecoding on next execution.
- AOT: Pre-decodes the entire program into a
PredecodedProgramand stores it in the cache. Future executions skip all decode work.
Promotion happens after execution completes and does not consume additional gas. Promotion failures (e.g., decode errors) are silently ignored --- the contract continues at the lower tier.
By default, JIT/AOT entries remain zero until contracts exceed the promotion thresholds.
Code Cache
Section titled “Code Cache”Cache Key
Section titled “Cache Key”All cache entries are keyed by CacheKey:
| Field | Type | Description |
|---|---|---|
code_hash | [u8; 32] | Blake3 hash of deployed contract ELF bytes |
abi_version | u16 | Contract ABI version (currently 1) |
gas_schedule_id | &'static str | Gas schedule identifier (e.g., "gas-v1") |
translator_version | u16 | Predecoder version (currently 1) |
toolchain_hash | [u8; 32] | Hash of the contract toolchain manifest |
Any change to these fields invalidates all entries for that contract.
Artifact Types
Section titled “Artifact Types”| Artifact | Description | Tier |
|---|---|---|
ProgramImage | Resolved ELF with segments and entry point | All |
PredecodedProgram | Predecoded basic blocks for AOT execution | Aot |
Opaque | Arbitrary bytes (future extension) | Any |
In-Memory Cache
Section titled “In-Memory Cache”The CodeCache is an LRU cache with configurable limits:
| Setting | Default | Description |
|---|---|---|
max_entries | 1024 | Maximum number of cached artifacts |
max_bytes | 256 MiB | Maximum total size of cached artifacts |
When limits are exceeded, the least-recently-used entry is evicted. The cache
tracks per-entry hit_count, last_used_tick, and total_gas_consumed.
Cache Invalidation
Section titled “Cache Invalidation”| Event | Action |
|---|---|
| Gas schedule version bump | All entries invalidated (embedded in cache key) |
| ABI version bump | Affected entries invalidated (embedded in cache key) |
| Translator version bump | All entries invalidated (embedded in cache key) |
| Contract code change | That contract’s entries invalidated (code hash changes) |
| Cache format version bump | All disk entries invalidated (header mismatch) |
| Node restart (unchanged versions) | Reuses persisted entries |
Disk Persistence
Section titled “Disk Persistence”The DiskCodeCache persists compiled artifacts across node restarts.
| Setting | Default | Description |
|---|---|---|
cache_dir | ~/.ashen/code-cache/ | Storage directory |
max_entries | 4096 | Max entries on disk |
max_bytes | 512 MiB | Max total size on disk |
enabled | true | Can be disabled for pure in-memory mode |
File Format
Section titled “File Format”Each entry is stored as {hex(key_hash)}-{tier}.bin:
[4 bytes] Magic: "ASHC"[1 byte] Format version (currently 1)[1 byte] Tier (0=Interpreter, 1=Jit, 2=Aot)[32 bytes] Blake3 checksum of (cache_key || artifact_bytes)[8 bytes] Artifact size (little-endian u64)[N bytes] Artifact data (borsh-serialized)Corruption handling: Blake3 checksum verification on load. Corrupt, truncated, or oversize entries are automatically deleted.
Atomic writes: Entries are written to a .tmp file, then atomically renamed.
Portability: Cache entries are not portable across architectures or translator versions.
Gas Accounting
Section titled “Gas Accounting”Gas charging is tier-independent. All tiers pay the same predecode_per_byte
cost upfront, charged against the gas meter before execution begins:
predecode_gas = predecode_per_byte * program_size_bytesThe predecode_per_byte rate is defined in the gas schedule (gas-v1.json).
AOT-eager mode deducts this cost before execution; cached AOT was charged at
compilation time.
Observability
Section titled “Observability”When std + vm-tiering are enabled, the node exports Prometheus metrics
under /metrics:
| Metric | Description |
|---|---|
code_cache_entries | Total cached entries |
code_cache_entries_by_tier{tier="..."} | Entries by tier (interpreter, jit, aot) |
code_cache_bytes | Total bytes used by cache |
code_cache_hits | Total cache hits since startup |
code_cache_misses | Total cache misses since startup |
CodeCache::stats() returns a CodeCacheStats snapshot with hit_rate()
(computed as hits / total lookups, 0.0 to 1.0).
Verifying Tiering
Section titled “Verifying Tiering”- Build with
--features vm-tiering. - Set
ASHEN_VM_TIERING=1and run the node. - Execute contract workloads.
- Check
code_cache_entries_by_tier{tier="jit"}andtier="aot".
If JIT/AOT remain zero, the node is running tiered selection but no contracts have exceeded the promotion thresholds yet.
Entry Points
Section titled “Entry Points”The runtime provides execution entry points with increasing configurability:
| Function | Use Case |
|---|---|
execute_entrypoint | Basic interpreter, no cache |
execute_entrypoint_tier | Explicit mode selection, no cache |
execute_entrypoint_tiered | Auto-select best tier from cache |
execute_entrypoint_cached | Explicit tier request + cache |
execute_entrypoint_with_tracking | Cache + hot-code promotion (production) |
execute_entrypoint_with_config | Unified entry point via EntrypointConfig |
For production block execution, use execute_entrypoint_with_tracking or
execute_entrypoint_with_config with both cache and tracker configured.
Related
Section titled “Related”- Gas Schedule --- gas costs including
predecode_per_byte - Precompiles & Syscalls --- syscall gas costs
- Feature Flags ---
vm-tieringandcranelift-native - JIT Compilation --- design notes on tiered JIT