Class CellRegistry¶
Defined in File cell_registry.hpp
Nested Relationships¶
Nested Types¶
Class Documentation¶
-
class CellRegistry¶
Runtime side of the cell table: the current result of each cell and its remaining life. This is the owner of those results (the explicit-value-cells design): it tracks the live byte total across held slots, enforces fill-once (a non-persistent cell produced twice without an intervening clear is a duplicate producer — a bug, not a legitimate replay), and seeds/publishes persistent cells through a
PersistentValueStoreso they survive across top-level evaluation calls without depending on the scope caches’ own persistence. Bound cells are cleared at the start of every batch of a loop instance they are bound to (the per-batch scratch reset, expressed on cells).Orientation convention, shared with the
CacheManagerscope caches: a cell holds the value in its node’s canonical orientation, and every reader applies that node’s owncanon_phaseonce. A producer therefore storesapply_phase(node, result)—evaluate_implhands back the oriented result, and the phase is an involution — matchingCacheManager::store_and_access(, readerscache.store_and_access(node,
apply_phase(node, rb))
apply_phaseagain, rootsmult_by_phase). Storing the oriented value here instead would double-apply the phase at every read and flip the sign of every value whose canonicalization needed an odd permutation.Public Functions
-
inline explicit CellRegistry(CellTable const &table, CellRegistryHooks hooks = {})¶
-
inline void seed_persistent()¶
Seeds every persistent cell whose canonical hash is currently held by
hooks_.persistentwith the store’s value, without spending life (the value just arrived from a prior top-level call; nothing has read it yet this call). A no-op for a cell the store does not (yet) hold, and entirely a no-op whenhooks_.persistentorhooks_.hash_ofis unset. Intended to run once, right after construction, before the first read of this evaluation call (seerun_ordered_schedule_pre_results).
-
inline void set(CellId c, ResultPtr v)¶
Production: overwrites the cell’s current result and restores its life from the table (a new batch’s/iteration’s production of a cell whose prior life was drained starts fresh). Fill-once: a non-persistent cell already holding a value it was not read past nor cleared since (
filled_since_clear) is a duplicate producer — seeCellRegistryHooks::strict_fill_once(throws a namedException, defaulting to theSEQUANT_UT_STRICT_FILL_ONCEenv gate; a plainSEQUANT_ASSERTotherwise). Persistent cells are excluded from this check, exactly asCacheManager::entry::storeexcludes its own persistent entries: they legitimately re-store across batch replays and repeated top-level evaluation calls (e.g. successive CC iterations), often with no table-declared clear between productions. A persistent cell is published tohooks_.persistent(when set) on every production, mirroring what it now holds.
-
inline ResultPtr peek(CellId c) const¶
Non-decrementing peek: the cell’s current result, or null if unset.
-
inline ResultPtr read(CellId c)¶
Decrementing read: throws if the cell has no current result, or (for a non-persistent cell) its life is already exhausted. The read that spends a non-persistent cell’s last life also drops the registry’s own reference (the cell has no reader left this evaluation, and holding on would both pin the memory and make the buffer look shared to the reader that just took it — the same thing
CacheManager::entry::access()does at the same point for the scope cache). A later production of the cell restores both value and life viaset.This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
non-null) whether this read spent the cell’s last life.
In-place eligibility is re-derived from this table’s own
life/persistentdirectly (seeCellReadResolver::operand_drained, which queriesdrained()rather than this flag). Theexhaustedoutput tells a reader whether it took ownership of the buffer: the ordered executor’s Assemble step seeds its running sum with the first batch’s partial when this read exhausted the source, and with aResult::cloneof it otherwise (a source with life left, or a persistent one, is still going to be read again from that very buffer, so accumulating into it would corrupt every later read).
-
inline void clear_bound_to(LoopKey const &k)¶
Batch start: drops every cell bound to loop instance
k— the per-batch scratch reset, expressed on cells instead of on a whole scope’s storage. A cell is bound tokeither explicitly (detail::bound_instances:a carriedslicedposition or apartial_overreduction onk) or implicitly, for a non-persistent cell whose own declared home scope’s deepest loop instance isk:the executor re-runs whatever this cell’s tree position computes fresh every batch of that position’s innermost enclosing loop (a step’s own block for a Build; a block-close aggregation for an Assemble), independent of whether the value it holds happens to carrykas a mode or a reduced axis — a “whole” cell unsliced and unsummed on its own home loop is still re-run every batch of it, only its declared life/persistence say how long the result is then read for. A persistent cell is never cleared here (by definition it is bound to no loop instance — seeTableCell::persistent— so neither check ever matches it; the explicitcontinueis a defensive redundant guard). Aproduce_if_absentcell is the one exception to the implicit rule: it is precisely a cell whose home scope sits inside a loop it is not bound to, and the table says it is produced on first visit and reused on every later batch of that loop (the executor’s Build step skips its production while it is resident), so the implicit “its scope’s innermost
loop is \p k” clear would defeat the flag by dropping it at every batch boundary. Such a cell is cleared only on an instance it is genuinely bound to. Also resets the fill-once mark of every cell it clears: the boundary this crosses is exactly the one
set'sfill-once check must not treat as a duplicate producer across.
-
inline void forgo(CellId c, std::size_t count)¶
Spends
countof cellc'sdeclared reads without taking the value: the accounting half of a read the executor decided not to perform. A consumer the runtime cache-halt skipped (nothing left this evaluation reads its result, or aproduce_if_absentcell that is resident and so is not re-produced) never callsread, so without this its source’s life would never reach zero — the source would stay resident to the end of the evaluation (pinning the memory) and keep looking shared to every later reader, which is exactly what disables in-place accumulation.Releases the value when the life reaches zero, exactly as the draining
readdoes (bytes accounted, fill-once mark cleared so a later production of the cell is a fresh one). A no-op for a persistent cell, which never drains, and forcount== 0. Throws whencountexceeds the remaining life: forgoing more reads than the table declared is an accounting bug in the caller, and silently saturating would release a value another consumer is still owed (seeremaining_life, which the ordered executor consults for the one case where a skipped consumer’s visits legitimately outnumber one production’s budget).
-
inline std::size_t remaining_life(CellId c) const¶
- Returns:
cell
c'sremaining declared life (0 for a spent cell; a persistent cell’s life is never spent, so this is informational there).
-
inline std::size_t live_bytes() const¶
- Returns:
the sum of
Result::size_in_bytes()over every slot currently holding a value (persistent and non-persistent alike).
-
template<typename F>
inline void for_each_live(F &&f) const¶ Invokes
f(CellId, ResultPtr const&)for every slot currently holding a value — the registry-side sourceon_peak_livesetwalks once storage moves fully onto the table.
-
inline bool drained(CellId c) const¶
- Returns:
whether cell
cis a spent non-persistent cell: no value held and no life left to spend. A persistent cell is never drained, so it is never reported as drained even when it happens to hold no value yet (e.g. before its first production this evaluation).
-
inline std::optional<CellId> cell_of(std::size_t vid, CellScope const &scope) const¶
The value’s form visible at
scope, by residency (not exact scope equality) — the table’s own visibility contract, decided by the one shared ruledetail::deepest_visible_formstates (deepest resident form wins; at equal depth the earlier candidate does), which the table builder’s read-source selection uses too. Build cells are offered first, which is how a tie prefers a Build. Leaf cells are not candidates here (seeleaf_cell): a leaf has no scope in this sense.
-
inline explicit CellRegistry(CellTable const &table, CellRegistryHooks hooks = {})¶