Expand description
Shared per-page memory-materialization (#3) state machine.
Both engines drive this same state machine so they charge
bit-identical category-#3 gas (consensus determinism): the
interpreter does software first-touch accounting (no real unmap),
the x86 recompiler does the same accounting off hardware page-table
faults. See ~/docs/spec-staging/gas-cost.md §3.
The charge rules key strictly on the per-page PageState and the
static PageKind — never on the instruction — so a load-then-store
to one page charges identically regardless of engine. The sole
fault-driven #3 charge is copy-on-write (the first write to a writable
page), charged at most once per page (per frame). Read-only page-in is
not charged at the fault: bringing a read-only region into the working
set is accounted eagerly at the CALL that maps it
(crate::gas_const::call_frame_cost, computed statically from the
callee Image), so a read — first or not — debits nothing here. A read
only records the residency PageState transition. The one re-touch
event is a MGMT_MOVE/MGMT_DROP slot eviction, a block terminator.
§Read-only materialization is a mapping event, not a charge
Read-only (PageKind::PinnedCapRo) regions — the program’s code and
pinned data caps — are still materialized in units, where a unit is
the intersection of one DataCap with one 2 MiB cluster
(CLUSTER_SHIFT, unit_base): the recompiler fault-arounds a whole
unit’s RO pages on first touch so later reads in the unit hit no further
faults, and both engines record the same unit set. That clustering is
now purely a mapping / fault-reduction optimization with zero
gas — the read-only page-in cost is charged once, eagerly, at the CALL
(one crate::gas_const::PAGE_IN_COST per declared 2 MiB unit), not
lazily per touched unit at the fault. Copy-on-write (RW) regions stay
4 KiB-granular: a write copies one page and charges COW_COST. Both
engines key on the same unit_base, so the (gas-free) unit set and the
per-page CoW charge match bit-for-bit.
Structs§
- Hard
Fault - A write to a read-only (pinned) page — a permanent PVM2-level fault.
(Accesses outside any declared mapping are rejected by the caller
before reaching
charge_for.) Charge nothing. - PageSet
- The set of consensus 4 KiB pages a single
width-byte access ataddrtouches: the base page, plus the next page iff the access straddles a page boundary. At mostcrate::gas_const::MAX_PAGES_PER_ACCESS(= 2) pages. Pages are ordered low → high — the fixed order both engines iterate, so the charged page set and total match exactly.
Enums§
- Page
Kind - Static per-page source kind, derived once from the Image’s declared memory mappings (pinned slot vs initial slot vs ephemeral / zero tail).
- Page
State - Dynamic per-page first-touch state.
Constants§
- CLUSTER_
SHIFT - log2 of the read-only materialization cluster size.
21→ 2 MiB, the common large-page size on x86 (PDE), AArch64 (L2 block), and RISC-V (megapage), so the clustered charge model is arch-portable. TODO(gas-calibration): cluster size is subject to change.
Functions§
- access_
pages - Compute the page set for a
width-byte access ataddr. Keyed only onaddrandwidthso the recompiler (which learnswidthfrom a compile-time side table) and the interpreter (which knows it from the opcode) agree byte-for-byte.widthmust be in1..=8. - charge_
for - The category-#3 charge and resulting state for one page touch.
- cluster_
of - Absolute 2 MiB cluster index of
addr(addr >> CLUSTER_SHIFT). - unit_
base - Identity of the read-only materialization unit containing
addr: thecap ∩ clusterregion, named by its base addressmax(cluster_lo, cap_start). The recompiler fault-arounds exactly the unit’s pages on its first touch (mapping them all read-only in one go), so a unit is mapped at most once — a pure fault-reduction key. It carries no gas: read-only page-in is charged eagerly at the CALL (crate::gas_const::call_frame_cost), not per touched unit.