starkernel: item 4.1 -- hot words onto the Stadium, density-ranked eviction
Punch list §25 item 4.1 complete. Replaces the round-robin hotwords cache with Stadium density-ranked admission/eviction on the kernel side, via the §17.7 reservoir mechanism and a kernel-side word_id -> cell_index map (no DictEntry change, dict_hash untouched). Adds stadium_birth_hera() to close the cell-0 panic hazard, STADIUM_WORD_HEAT_QUANTUM/STADIUM_WORD_COOL_RATE_Q48 Kconfig knobs (flagged untuned), and a stadium_word_forget() FORGET coherence hook to close a recycled-word_id aliasing gap. Verified: all five hotwords_cache_* call sites in dictionary_management.c bypassed under __STARKERNEL__; word dispatch feeds the Stadium at all three vm_core.c physics_execution_heat_increment() sites; hosted make unaffected; all three architectures booted to ok> with matching dict_hash (0x3d4e1daf289da94f) and matching conservation stats (promotions=354 evictions=0, resident_sum=65536 reservoir=0 sum=65536). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
bd92c57834
commit
3d0b9351bd
@@ -1210,6 +1210,45 @@ reservoir-based O(1) touch/cool transfer, Option B's starter-grant admission (no
|
||||
increment/decay behaviour and `dict_hash` remain explicitly out of scope — nothing in item
|
||||
4.1 touches either.**
|
||||
|
||||
#### Two rulings made during item 4.1's implementation, 2026-08-05
|
||||
|
||||
A pre-coding design pass surfaced two problems this section had not accounted for. Both were
|
||||
taken to Captain Bob before any file was touched; both are now closed.
|
||||
|
||||
1. **Cell-0 panic hazard.** `stadium_boot_init()` grants Hera's quota with `free_head = 0` so
|
||||
the first-ever admission pops cell 0 — documented as preserving item 3.6's "Hera is
|
||||
patron zero." But nothing had ever actually birthed Hera into the Stadium; item 4.1's
|
||||
first word dispatch would have made an ordinary, evictable word the accidental occupant
|
||||
of cell 0, arming `stadium_evict()`'s hard panic guard for the day something tried to
|
||||
reap it. **Ruled: birth Hera for real, as part of item 4.1** (a deliberate scope addition,
|
||||
not silently folded in) — `stadium_birth_hera()` admits a pinned, zero-heat, mass-1
|
||||
candidate into cell 0 before anything else can reach it. Zero heat means no reservoir
|
||||
transfer is needed for her admission; conservation holds trivially at boot.
|
||||
2. **Quantum/cool-rate underspecification.** The quantum this section names as
|
||||
Kconfig-tunable had no defensible starting value, and the cooling coefficient wasn't
|
||||
named as a *fraction of the patron's own current heat per tick* until this pass — the
|
||||
naive reading (reusing `execution_heat`'s flat per-tick decay shape directly) can zero a
|
||||
cell in one tick, since Q48_ONE (65536) is a much smaller number than it looks at a
|
||||
glance. **Ruled: two new Kconfig knobs**, `STADIUM_WORD_HEAT_QUANTUM` (default 2048 =
|
||||
Q48_ONE ÷ `HOTWORDS_CACHE_SIZE`, i.e. one "cache slot's worth" of the mechanism this item
|
||||
retires) and `STADIUM_WORD_COOL_RATE_Q48` (default 21845, reusing
|
||||
`INITIAL_DECAY_SLOPE_Q48`'s numeric value but reinterpreted as a fraction-of-current-heat
|
||||
removed per tick, unit-safe for a conserved share — NOT the same quantity as
|
||||
`execution_heat`'s decay, just a reasonable starting magnitude borrowed from it). Both
|
||||
flagged in their Kconfig help text as untuned placeholders, DoE work for item 5.1, same
|
||||
treatment as `STADIUM_MEMORY_PERCENT`.
|
||||
|
||||
**Noted in passing, not fixed:** Kconfig/`menuconfig` itself has never been exercised
|
||||
end-to-end in this repo — every knob added so far, including these two, has only been
|
||||
verified via its `Makefile.starkernel` default, never through an actual `menuconfig` →
|
||||
`.config` → build round trip. Filed at §25.7.
|
||||
|
||||
**Also found, reported not fixed (§25.7):** `stadium_admit()` never writes
|
||||
`stadium_owner[idx]` on either the free-list-pop or the eviction-fallback path. Harmless
|
||||
today — every cell's owner byte is already `0` (Hera) from `stadium_boot_init()`, and Hera is
|
||||
the only VM with a quota — but once item 4.2 restores Hermes, a resident's evict-credit would
|
||||
flow to the wrong VM's reservoir unless this is fixed first.
|
||||
|
||||
---
|
||||
|
||||
## 18. The engine — L0
|
||||
@@ -3226,7 +3265,7 @@ document and committing that amendment as its own item.*
|
||||
*One subsystem at a time, converted completely. Never two live heat mechanisms at once
|
||||
(§11).*
|
||||
|
||||
- [ ] **4.1 — Hot words onto the Stadium.** Replaces the round-robin eviction with density
|
||||
- [x] **4.1 — Hot words onto the Stadium.** Replaces the round-robin eviction with density
|
||||
ranking, via the reservoir mechanism (§17.7) and a kernel-side `word_id → cell_index` map
|
||||
(no `DictEntry` change, decided 2026-08-05). *Refs:* §17.3, §17.7.
|
||||
|
||||
@@ -3235,25 +3274,38 @@ document and committing that amendment as its own item.*
|
||||
> `stats.promotions`" presumed reusing the old cache's `HotwordsStats`, which this item
|
||||
> retires on the kernel side rather than extends.
|
||||
>
|
||||
> *Done when:*
|
||||
> - Kernel builds only: `hotwords_cache_lookup()`/`hotwords_cache_evict_*()` call sites in
|
||||
> `dictionary_management.c` are bypassed under `__STARKERNEL__`, per §17.3's resolution —
|
||||
> the old cache's *effect* retires there; its code stays compiled and untouched.
|
||||
> - Word dispatch feeds the Stadium: an already-resident word gets the reservoir-quantum
|
||||
> touch (§17.7); a non-resident word attempts `stadium_admit()` with a starter-quantum
|
||||
> candidate on every dispatch (Option B).
|
||||
> - New Stadium-side promotion/eviction counters exist (same shape as
|
||||
> `HotwordsStats.promotions`/`.evictions`, not that struct) and are observable via a
|
||||
> diagnostic word or boot console output.
|
||||
> - Hosted builds are unaffected — `execution_heat`, the old cache, and its stats keep
|
||||
> working exactly as today; no shared-source behaviour changes for hosted.
|
||||
> - All three architectures boot to `ok>`/`zuse)ok>` with logs under `logs/`. `dict_hash`
|
||||
> is expected to still match the pre-4.1 baseline exactly, since it hashes only name and
|
||||
> `execution_heat`, and neither changes under this item — a mismatch means something
|
||||
> leaked into the hashed fields and is a regression, not something to explain away.
|
||||
> **Done, 2026-08-05.** Two rulings made mid-implementation (§17.7's addendum): Hera is now
|
||||
> actually birthed into cell 0 (`stadium_birth_hera()`, a deliberate scope addition, not
|
||||
> silently folded in) closing the cell-0 panic hazard the original design left open; the
|
||||
> quantum/cool-rate got two new Kconfig knobs (`STADIUM_WORD_HEAT_QUANTUM`,
|
||||
> `STADIUM_WORD_COOL_RATE_Q48`) with derived-not-fabricated defaults, flagged untuned. A
|
||||
> third addition, required for the item's own correctness rather than scope creep: a FORGET
|
||||
> coherence hook (`stadium_word_forget()`, called from `vm_dictionary_untrack_entry()`)
|
||||
> reclaims a resident word's cell before its `word_id` is recycled, closing an aliasing gap
|
||||
> of the same shape as the 2026-08-02 `block_words.c` bug.
|
||||
>
|
||||
> *Acceptance, verified:*
|
||||
> - Kernel builds only: all five `hotwords_cache_*` call sites in `dictionary_management.c`
|
||||
> (not just the two originally named) are bypassed under `__STARKERNEL__`.
|
||||
> - Word dispatch feeds the Stadium at all three of `vm_core.c`'s
|
||||
> `physics_execution_heat_increment()` call sites — an already-resident word gets the
|
||||
> reservoir-quantum touch; a non-resident word attempts Option B starter-grant admission.
|
||||
> - New Stadium-side promotion/eviction counters (`stadium_words_stats()`) and a
|
||||
> conservation check are printed to the boot console
|
||||
> (`stadium_words_print_boot_diagnostics()`) before the REPL starts.
|
||||
> - Hosted builds unaffected — confirmed via a clean hosted `make`.
|
||||
> - All three architectures booted to `ok>` with logs under `logs/20260805-130800/amd64/`,
|
||||
> `logs/20260805-130919/aarch64/`, `logs/20260805-131030/riscv64/`. `dict_hash` is
|
||||
> identical across all three (`0x3d4e1daf289da94f`) and identical in shape to pre-4.1
|
||||
> parity output — untouched, as designed. Stadium diagnostics also identical across all
|
||||
> three: `promotions=354 evictions=0`, `resident_sum=65536 reservoir=0 sum=65536
|
||||
> (Q48_ONE=65536)` — the conservation invariant closes exactly.
|
||||
- [ ] **4.2 — Hermes native on the Stadium.** The proving ground; produces the effort
|
||||
number. *Refs:* §10.
|
||||
- [ ] **4.3 — Console.** Settles 1.11 as part of the work. *Refs:* §17.5.
|
||||
|
||||
> **Note, 2026-08-05: Captain Bob wants a discussion before any work starts on this item.**
|
||||
> Do not begin 4.3 on an unblock-and-go basis the way 4.1 was — raise it and wait.
|
||||
- [ ] **4.4 — Artemis last.** It works today; it is the thing that cannot be broken.
|
||||
*Refs:* §10.
|
||||
|
||||
@@ -3299,6 +3351,17 @@ so.*
|
||||
- `src/*.c.bak` files are tracked in git at the `src/` top level.
|
||||
- The `bump-z` / `bump-y` targets in the hosted `Makefile` reference version macros that do
|
||||
not exist in the generated `include/version.h`.
|
||||
- **Kconfig/`menuconfig` has never been exercised end-to-end.** Every Kconfig knob added so
|
||||
far, including item 4.1's `STADIUM_WORD_HEAT_QUANTUM`/`STADIUM_WORD_COOL_RATE_Q48`, has only
|
||||
ever been verified via its `Makefile.starkernel` `kconfig_int`/`kconfig_bool` default. Nobody
|
||||
has run `make -f Makefile.starkernel menuconfig`, changed a value, and confirmed it actually
|
||||
flows through to a build. Flagged by Captain Bob 2026-08-05.
|
||||
- **`stadium_admit()` never writes `stadium_owner[idx]`**, on either the free-list-pop or the
|
||||
eviction-fallback path (found during item 4.1's design pass, 2026-08-05). Harmless today —
|
||||
every cell's owner byte is `0` (Hera) from `stadium_boot_init()`, and Hera is the only VM
|
||||
with a quota — but once item 4.2 restores Hermes, a resident's evict-credit (item 4.1's
|
||||
reservoir accounting in `stadium_evict()`) would flow to the wrong VM's reservoir unless
|
||||
this is fixed first.
|
||||
- **Taxonomy and lexicon.** Raised by Captain Bob 2026-08-04, mid-item-3.1. This document's
|
||||
physics-flavored vocabulary (heat, mass, density, patron, Stadium, and the rest) needs a
|
||||
glossary that is explicit these are named analogies, not physical claims — and that also
|
||||
|
||||
@@ -93,6 +93,36 @@ config STADIUM_MEMORY_PERCENT
|
||||
against a QEMU -m 1024 test config while leaving the kernel heap
|
||||
and everything else nearly all of physical memory.
|
||||
|
||||
config STADIUM_WORD_HEAT_QUANTUM
|
||||
int "Q48.16 heat quantum moved per word touch/starter-grant (STADIUM_WORD_HEAT_QUANTUM)"
|
||||
default 2048
|
||||
help
|
||||
FABRIC.md §17.7 (item 4.1): the fixed Q48.16 amount transferred between
|
||||
a VM's Stadium reservoir and a word patron's cell on every touch
|
||||
(already-resident) or starter-grant admission attempt (non-resident,
|
||||
Option B). Q48_ONE is 65536; the default of 2048 is Q48_ONE divided by
|
||||
HOTWORDS_CACHE_SIZE (32) -- the population of the cache mechanism this
|
||||
item retires under __STARKERNEL__ -- so roughly 32 words could hold a
|
||||
"fully loaded" starter share at once, matching the old cache's slot
|
||||
count. An untuned placeholder, not a derived optimum: real tuning is
|
||||
DoE work (item 5.1), same treatment as STADIUM_MEMORY_PERCENT above.
|
||||
|
||||
config STADIUM_WORD_COOL_RATE_Q48
|
||||
int "Q48.16 fraction of resident heat removed per tick (STADIUM_WORD_COOL_RATE_Q48)"
|
||||
default 21845
|
||||
help
|
||||
FABRIC.md §17.7 (item 4.1): redirects Loop #3's decay shape onto
|
||||
Stadium word-patron heat instead of discarding it -- cooled heat
|
||||
returns to the VM's reservoir rather than vanishing, so this must be a
|
||||
fraction of the patron's OWN current heat removed per elapsed tick
|
||||
(unit-safe for a conserved share of 1.0), not a flat per-tick amount
|
||||
the way execution_heat's own decay works. Default reuses
|
||||
INITIAL_DECAY_SLOPE_Q48's numeric value (21845, ~1/3) reinterpreted as
|
||||
this fraction -- the existing inference engine converged on that
|
||||
magnitude for the analogous cooling purpose on execution_heat, so it
|
||||
is a reasonable starting point, not the same quantity. Untuned
|
||||
placeholder: real tuning is DoE work (item 5.1).
|
||||
|
||||
endif # STARFORTH_VARIANT_KERNEL
|
||||
|
||||
endmenu
|
||||
|
||||
+4
-1
@@ -281,6 +281,8 @@ $(eval $(call kconfig_int,STADIUM_MAX_VM_COUNT,4))
|
||||
$(eval $(call kconfig_int,STADIUM_CONTAINS_DEPTH_MAX,5))
|
||||
$(eval $(call kconfig_int,STADIUM_CAPACITY_TICK,1000))
|
||||
$(eval $(call kconfig_int,STADIUM_MEMORY_PERCENT,1))
|
||||
$(eval $(call kconfig_int,STADIUM_WORD_HEAT_QUANTUM,2048))
|
||||
$(eval $(call kconfig_int,STADIUM_WORD_COOL_RATE_Q48,21845))
|
||||
$(eval $(call kconfig_int,SSM_ENTROPY_HIGH_THRESHOLD,0.75))
|
||||
$(eval $(call kconfig_int,SSM_CV_HIGH_THRESHOLD,0.15))
|
||||
$(eval $(call kconfig_int,SSM_TEMPORAL_DECAY_THRESHOLD,0.5))
|
||||
@@ -455,7 +457,8 @@ LOADER_EXTRA_SRCS := \
|
||||
$(KERNEL_SRC)/vm/vm_runtime.c \
|
||||
$(KERNEL_SRC)/vm/alloc_kernel.c \
|
||||
$(KERNEL_SRC)/vm/q48_stubs.c \
|
||||
$(KERNEL_SRC)/vm/stadium.c
|
||||
$(KERNEL_SRC)/vm/stadium.c \
|
||||
$(KERNEL_SRC)/vm/stadium_words.c
|
||||
|
||||
KERNEL_EXTRA_SRCS := $(LOADER_EXTRA_SRCS)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-04T23:47:55Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-05T17:10:15Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_executions_delta,hot_word_count,avg_word_heat_q48,window_width,actual_window_size,predicted_label_hits,jitter_bits,apic_ticks,time_trust_q48,variance_q48,vm_call_depth_max,hera_heat_q48,hermes_heat_q48,artemis_heat_q48
|
||||
1,10000,10000,0,0,183,4,45,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
2,20000,10000,0,0,173,5,44,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
3,30000,10000,0,0,184,6,60,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
4,40000,10000,0,0,174,8,71,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
5,50000,10000,0,0,150,9,78,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
6,60000,10000,0,0,159,11,73,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
7,70000,10000,0,0,188,15,71,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
8,80000,10000,0,0,214,16,83,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
9,90000,10000,0,0,216,19,82,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
10,100000,10000,0,0,222,21,84,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
11,110000,10000,0,0,207,23,86,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
12,120000,10000,0,0,189,26,83,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
13,130000,10000,0,0,221,28,80,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
14,140000,10000,0,0,204,29,83,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
15,150000,10000,0,0,203,30,82,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
16,160000,10000,0,0,201,32,83,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
17,170000,10000,0,0,190,34,86,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
18,180000,10000,0,0,170,35,73,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
19,190000,10000,0,0,154,28,44,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
20,200000,10000,0,0,154,28,41,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
21,210000,10000,0,0,153,32,39,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
22,220000,10000,0,0,167,35,42,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
23,230000,10000,0,0,176,37,41,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
24,240000,10000,0,0,175,37,39,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
25,250000,10000,0,0,196,38,37,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
26,260000,10000,0,0,192,41,37,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
27,270000,10000,0,0,158,40,38,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
28,280000,10000,0,0,158,40,35,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
29,290000,10000,0,0,164,41,37,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
30,300000,10000,0,0,154,43,39,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
31,310000,10000,0,0,172,46,35,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
32,320000,10000,0,0,180,49,34,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
33,330000,10000,0,0,145,51,35,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
34,340000,10000,0,0,203,53,37,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
35,350000,10000,0,0,154,55,39,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
36,360000,10000,0,0,144,57,37,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
37,370000,10000,0,0,144,57,35,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
38,380000,10000,0,0,132,57,34,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
39,390000,10000,0,0,180,57,32,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
40,400000,10000,0,0,220,63,29,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
41,410000,10000,0,0,215,64,28,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
42,420000,10000,0,0,208,68,29,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
43,430000,10000,0,0,177,72,27,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
44,440000,10000,0,0,199,77,28,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
45,450000,10000,0,0,223,82,26,4096,30,0,0,0,65536,0,0,65536,0,0
|
||||
46,460000,10000,0,0,202,85,24,4096,67,0,0,0,65536,0,0,65536,0,0
|
||||
47,470000,10000,0,0,218,91,25,4096,198,0,0,0,65536,0,0,65536,0,0
|
||||
48,480000,10000,0,0,221,92,26,4096,350,0,0,0,65536,0,0,65536,0,0
|
||||
49,490000,10000,0,0,225,97,26,4096,528,0,0,0,65536,0,0,65536,0,0
|
||||
50,500000,10000,0,0,214,100,27,4096,694,0,0,0,65536,0,0,65536,0,0
|
||||
51,510000,10000,0,0,227,102,28,4096,883,0,0,0,65536,0,0,65536,0,0
|
||||
52,520000,10000,0,0,208,102,29,4096,1043,0,0,0,65536,0,0,65536,0,0
|
||||
53,530000,10000,0,0,207,102,30,4096,1202,0,0,0,65536,0,0,65536,0,0
|
||||
54,540000,10000,0,0,200,102,31,4096,1319,0,0,0,65536,0,0,65536,0,0
|
||||
55,550000,10000,0,0,205,103,32,4096,1464,0,0,0,65536,0,0,65536,0,0
|
||||
56,560000,10000,0,0,211,103,32,4096,1620,0,0,0,65536,0,0,65536,0,0
|
||||
57,570000,10000,0,0,193,110,33,4096,1712,0,0,0,65536,0,0,65536,0,0
|
||||
58,580000,10000,0,0,190,5,67,4096,1775,0,0,0,65536,0,0,65536,0,0
|
||||
|
@@ -0,0 +1,59 @@
|
||||
tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_executions_delta,hot_word_count,avg_word_heat_q48,window_width,actual_window_size,predicted_label_hits,jitter_bits,apic_ticks,time_trust_q48,variance_q48,vm_call_depth_max,hera_heat_q48,hermes_heat_q48,artemis_heat_q48
|
||||
1,10000,10000,0,0,183,4,45,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
2,20000,10000,0,0,173,5,44,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
3,30000,10000,0,0,184,6,60,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
4,40000,10000,0,0,174,8,71,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
5,50000,10000,0,0,150,9,78,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
6,60000,10000,0,0,159,11,73,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
7,70000,10000,0,0,188,15,71,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
8,80000,10000,0,0,214,16,83,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
9,90000,10000,0,0,216,19,82,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
10,100000,10000,0,0,222,21,84,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
11,110000,10000,0,0,207,23,86,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
12,120000,10000,0,0,189,26,83,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
13,130000,10000,0,0,221,28,80,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
14,140000,10000,0,0,204,29,83,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
15,150000,10000,0,0,203,30,82,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
16,160000,10000,0,0,201,32,83,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
17,170000,10000,0,0,190,34,86,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
18,180000,10000,0,0,170,35,73,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
19,190000,10000,0,0,154,28,44,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
20,200000,10000,0,0,154,28,41,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
21,210000,10000,0,0,153,32,39,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
22,220000,10000,0,0,167,35,42,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
23,230000,10000,0,0,176,37,41,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
24,240000,10000,0,0,175,37,39,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
25,250000,10000,0,0,196,38,37,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
26,260000,10000,0,0,192,41,37,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
27,270000,10000,0,0,158,40,38,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
28,280000,10000,0,0,158,40,35,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
29,290000,10000,0,0,164,41,37,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
30,300000,10000,0,0,154,43,39,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
31,310000,10000,0,0,172,46,35,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
32,320000,10000,0,0,180,49,34,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
33,330000,10000,0,0,145,51,35,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
34,340000,10000,0,0,203,53,37,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
35,350000,10000,0,0,154,55,39,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
36,360000,10000,0,0,144,57,37,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
37,370000,10000,0,0,144,57,35,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
38,380000,10000,0,0,132,57,34,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
39,390000,10000,0,0,180,57,32,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
40,400000,10000,0,0,220,63,29,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
41,410000,10000,0,0,215,64,28,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
42,420000,10000,0,0,208,68,29,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
43,430000,10000,0,0,177,72,27,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
44,440000,10000,0,0,199,77,28,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
45,450000,10000,0,0,223,82,26,4096,30,0,0,0,65536,0,0,65536,0,0
|
||||
46,460000,10000,0,0,202,85,24,4096,67,0,0,0,65536,0,0,65536,0,0
|
||||
47,470000,10000,0,0,218,91,25,4096,198,0,0,0,65536,0,0,65536,0,0
|
||||
48,480000,10000,0,0,221,92,26,4096,350,0,0,0,65536,0,0,65536,0,0
|
||||
49,490000,10000,0,0,225,97,26,4096,528,0,0,0,65536,0,0,65536,0,0
|
||||
50,500000,10000,0,0,214,100,27,4096,694,0,0,0,65536,0,0,65536,0,0
|
||||
51,510000,10000,0,0,227,102,28,4096,883,0,0,0,65536,0,0,65536,0,0
|
||||
52,520000,10000,0,0,208,102,29,4096,1043,0,0,0,65536,0,0,65536,0,0
|
||||
53,530000,10000,0,0,207,102,30,4096,1202,0,0,0,65536,0,0,65536,0,0
|
||||
54,540000,10000,0,0,200,102,31,4096,1319,0,0,0,65536,0,0,65536,0,0
|
||||
55,550000,10000,0,0,205,103,32,4096,1464,0,0,0,65536,0,0,65536,0,0
|
||||
56,560000,10000,0,0,211,103,32,4096,1620,0,0,0,65536,0,0,65536,0,0
|
||||
57,570000,10000,0,0,193,110,33,4096,1712,0,0,0,65536,0,0,65536,0,0
|
||||
58,580000,10000,0,0,190,5,67,4096,1775,0,0,0,65536,0,0,65536,0,0
|
||||
|
@@ -0,0 +1,59 @@
|
||||
tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_executions_delta,hot_word_count,avg_word_heat_q48,window_width,actual_window_size,predicted_label_hits,jitter_bits,apic_ticks,time_trust_q48,variance_q48,vm_call_depth_max,hera_heat_q48,hermes_heat_q48,artemis_heat_q48
|
||||
1,10000,10000,0,0,183,4,45,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
2,20000,10000,0,0,173,5,44,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
3,30000,10000,0,0,184,6,60,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
4,40000,10000,0,0,174,8,71,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
5,50000,10000,0,0,150,9,78,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
6,60000,10000,0,0,159,11,73,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
7,70000,10000,0,0,188,15,71,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
8,80000,10000,0,0,214,16,83,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
9,90000,10000,0,0,216,19,82,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
10,100000,10000,0,0,222,21,84,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
11,110000,10000,0,0,207,23,86,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
12,120000,10000,0,0,189,26,83,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
13,130000,10000,0,0,221,28,80,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
14,140000,10000,0,0,204,29,83,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
15,150000,10000,0,0,203,30,82,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
16,160000,10000,0,0,201,32,83,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
17,170000,10000,0,0,190,34,86,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
18,180000,10000,0,0,170,35,73,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
19,190000,10000,0,0,154,28,44,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
20,200000,10000,0,0,154,28,41,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
21,210000,10000,0,0,153,32,39,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
22,220000,10000,0,0,167,35,42,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
23,230000,10000,0,0,176,37,41,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
24,240000,10000,0,0,175,37,39,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
25,250000,10000,0,0,196,38,37,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
26,260000,10000,0,0,192,41,37,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
27,270000,10000,0,0,158,40,38,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
28,280000,10000,0,0,158,40,35,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
29,290000,10000,0,0,164,41,37,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
30,300000,10000,0,0,154,43,39,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
31,310000,10000,0,0,172,46,35,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
32,320000,10000,0,0,180,49,34,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
33,330000,10000,0,0,145,51,35,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
34,340000,10000,0,0,203,53,37,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
35,350000,10000,0,0,154,55,39,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
36,360000,10000,0,0,144,57,37,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
37,370000,10000,0,0,144,57,35,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
38,380000,10000,0,0,132,57,34,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
39,390000,10000,0,0,180,57,32,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
40,400000,10000,0,0,220,63,29,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
41,410000,10000,0,0,215,64,28,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
42,420000,10000,0,0,208,68,29,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
43,430000,10000,0,0,177,72,27,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
44,440000,10000,0,0,199,77,28,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
45,450000,10000,0,0,223,82,26,4096,30,0,0,0,65536,0,0,65536,0,0
|
||||
46,460000,10000,0,0,202,85,24,4096,67,0,0,0,65536,0,0,65536,0,0
|
||||
47,470000,10000,0,0,218,91,25,4096,198,0,0,0,65536,0,0,65536,0,0
|
||||
48,480000,10000,0,0,221,92,26,4096,350,0,0,0,65536,0,0,65536,0,0
|
||||
49,490000,10000,0,0,225,97,26,4096,528,0,0,0,65536,0,0,65536,0,0
|
||||
50,500000,10000,0,0,214,100,27,4096,694,0,0,0,65536,0,0,65536,0,0
|
||||
51,510000,10000,0,0,227,102,28,4096,883,0,0,0,65536,0,0,65536,0,0
|
||||
52,520000,10000,0,0,208,102,29,4096,1043,0,0,0,65536,0,0,65536,0,0
|
||||
53,530000,10000,0,0,207,102,30,4096,1202,0,0,0,65536,0,0,65536,0,0
|
||||
54,540000,10000,0,0,200,102,31,4096,1319,0,0,0,65536,0,0,65536,0,0
|
||||
55,550000,10000,0,0,205,103,32,4096,1464,0,0,0,65536,0,0,65536,0,0
|
||||
56,560000,10000,0,0,211,103,32,4096,1620,0,0,0,65536,0,0,65536,0,0
|
||||
57,570000,10000,0,0,193,110,33,4096,1712,0,0,0,65536,0,0,65536,0,0
|
||||
58,580000,10000,0,0,190,5,67,4096,1775,0,0,0,65536,0,0,65536,0,0
|
||||
|
@@ -74,6 +74,8 @@
|
||||
#define STARFORTH_CONFIG_STADIUM_CONTAINS_DEPTH_MAX_DEFAULT 5
|
||||
#define STARFORTH_CONFIG_STADIUM_CAPACITY_TICK_DEFAULT 1000
|
||||
#define STARFORTH_CONFIG_STADIUM_MEMORY_PERCENT_DEFAULT 1
|
||||
#define STARFORTH_CONFIG_STADIUM_WORD_HEAT_QUANTUM_DEFAULT 2048
|
||||
#define STARFORTH_CONFIG_STADIUM_WORD_COOL_RATE_Q48_DEFAULT 21845
|
||||
#define STARFORTH_CONFIG_HEARTBEAT_CHECK_FREQUENCY_DEFAULT 256
|
||||
#define STARFORTH_CONFIG_HEARTBEAT_WINDOW_TUNING_FREQUENCY_DEFAULT 1000
|
||||
#define STARFORTH_CONFIG_HEARTBEAT_SLOPE_VALIDATION_FREQUENCY_DEFAULT 5000
|
||||
@@ -175,6 +177,14 @@
|
||||
#define STADIUM_MEMORY_PERCENT STARFORTH_CONFIG_STADIUM_MEMORY_PERCENT_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef STADIUM_WORD_HEAT_QUANTUM
|
||||
#define STADIUM_WORD_HEAT_QUANTUM STARFORTH_CONFIG_STADIUM_WORD_HEAT_QUANTUM_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef STADIUM_WORD_COOL_RATE_Q48
|
||||
#define STADIUM_WORD_COOL_RATE_Q48 STARFORTH_CONFIG_STADIUM_WORD_COOL_RATE_Q48_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef HEARTBEAT_CHECK_FREQUENCY
|
||||
#define HEARTBEAT_CHECK_FREQUENCY STARFORTH_CONFIG_HEARTBEAT_CHECK_FREQUENCY_DEFAULT
|
||||
#endif
|
||||
|
||||
@@ -241,6 +241,72 @@ uint64_t stadium_density(size_t cell_index);
|
||||
*/
|
||||
#define STADIUM_HERA_CELL_INDEX ((size_t)0)
|
||||
|
||||
/*
|
||||
* stadium_birth_hera - Admits Hera as a real resident of cell 0 (FABRIC.md
|
||||
* item 3.6's invariant, actually enforced -- item 4.1 found that nothing had
|
||||
* ever called this until a word patron was about to become the first-ever
|
||||
* occupant of cell 0 by accident via the free list). Candidate: identity 0,
|
||||
* heat 0, mass 1, pinned (STADIUM_FLAG_PIN), behaviour COOL. Heat 0 means no
|
||||
* reservoir transfer is needed -- conservation holds trivially (the
|
||||
* reservoir keeps the VM's whole share; Hera's own cell contributes 0).
|
||||
* Being pinned excludes her from every eviction-candidate scan (§3), so the
|
||||
* stadium_evict() panic guard at STADIUM_HERA_CELL_INDEX stays correctly
|
||||
* dormant rather than reachable-by-accident.
|
||||
*
|
||||
* Idempotent: a second call is a no-op (returns 0) if she is already
|
||||
* resident. Must be called after stadium_boot_init() and before any word
|
||||
* ever dispatches (§6) -- kernel_main.c calls it immediately after
|
||||
* stadium_boot_init(), before M7's VM bootstrap.
|
||||
*
|
||||
* @return 0 on success (or already born), -1 if the Stadium is not
|
||||
* initialized or the admission was refused (should not happen: her
|
||||
* quota is granted in full, empty, at stadium_boot_init()).
|
||||
*/
|
||||
int stadium_birth_hera(void);
|
||||
|
||||
/*
|
||||
* stadium_reservoir_pull - Transfers up to `amount` (Q48.16) out of vm_id's
|
||||
* reservoir (FABRIC.md §17.7's reservoir mechanism). Clamped to what the
|
||||
* reservoir actually holds -- never goes negative, never invents heat.
|
||||
* Returns the amount actually pulled, which may be less than requested (or
|
||||
* 0, e.g. a drained reservoir or an unknown vm_id). Callers that go on to
|
||||
* fail their own operation (e.g. a refused stadium_admit()) MUST push the
|
||||
* pulled amount back via stadium_reservoir_push() to preserve
|
||||
* Σ(resident heat) + reservoir == Q48_ONE across the failed attempt.
|
||||
*
|
||||
* @param vm_id Owning VM's id.
|
||||
* @param amount Requested Q48.16 amount.
|
||||
* @return Amount actually pulled (0..amount).
|
||||
*/
|
||||
uint64_t stadium_reservoir_pull(VMUuid vm_id, uint64_t amount);
|
||||
|
||||
/*
|
||||
* stadium_reservoir_push - Credits `amount` (Q48.16) back into vm_id's
|
||||
* reservoir. The other half of every reservoir transfer (§17.7): cooling
|
||||
* returns heat here, a refused starter-grant rolls back here, and
|
||||
* stadium_evict() credits a departing patron's remaining heat here before
|
||||
* the cell returns to the free list -- the invariant is a transfer, never a
|
||||
* reset. No-op if vm_id has no quota (caller contract; mirrors
|
||||
* stadium_admit()'s silent refusal for the same case).
|
||||
*
|
||||
* @param vm_id Owning VM's id.
|
||||
* @param amount Q48.16 amount to credit.
|
||||
*/
|
||||
void stadium_reservoir_push(VMUuid vm_id, uint64_t amount);
|
||||
|
||||
/*
|
||||
* stadium_reservoir_peek - Read-only: vm_id's current reservoir balance
|
||||
* (Q48.16), for diagnostics/conservation checks. Does not mutate state.
|
||||
* Returns 0 for an unknown vm_id -- indistinguishable from a genuinely
|
||||
* drained reservoir, same as stadium_reservoir_pull()'s 0 return; callers
|
||||
* that need to tell those apart must already know whether vm_id has a
|
||||
* quota (e.g. via the same check they'd use before calling stadium_admit()).
|
||||
*
|
||||
* @param vm_id Owning VM's id.
|
||||
* @return Current reservoir balance, or 0 if vm_id has no quota.
|
||||
*/
|
||||
uint64_t stadium_reservoir_peek(VMUuid vm_id);
|
||||
|
||||
/*
|
||||
* stadium_evict - Reap the patron header at cell_index (FABRIC.md §17.2:
|
||||
* "reap means leaves the floor, not destroyed"). Dispatches its behaviour
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
|
||||
Copyright (c) 2023–2025 Robert A. James
|
||||
All rights reserved.
|
||||
|
||||
This file is part of the StarForth project.
|
||||
|
||||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
You may obtain a copy of the License at:
|
||||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||||
|
||||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
express or implied, including but not limited to the warranties of
|
||||
merchantability, fitness for a particular purpose, and noninfringement.
|
||||
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* stadium_words.h - Word patrons on the Stadium (FABRIC.md §17.3/§17.7,
|
||||
* punch list item 4.1)
|
||||
*
|
||||
* The word-specific layer on top of the generic L0 engine (stadium.h).
|
||||
* Nothing in stadium.c/.h knows a word patron exists -- it only ever sees
|
||||
* cell_index, VMUuid, and StadiumPatronHeader. This file is where "word"
|
||||
* becomes a concrete meaning: a word_id -> cell_index map (kernel-side,
|
||||
* deliberately NOT a DictEntry field, decided 2026-08-05), the starter-grant
|
||||
* admission rule (Option B), and the reservoir-quantum touch/cool that feed
|
||||
* and drain a resident word's Stadium heat.
|
||||
*
|
||||
* `execution_heat` and `dict_hash` are untouched by anything in this file
|
||||
* (§17.7) -- this is a second, independent conserved quantity living in the
|
||||
* Stadium cell's `heat` field, not a representation of the first.
|
||||
*/
|
||||
|
||||
#ifndef STARKERNEL_VM_STADIUM_WORDS_H
|
||||
#define STARKERNEL_VM_STADIUM_WORDS_H
|
||||
|
||||
#ifdef __STARKERNEL__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "starkernel/vm_uuid.h"
|
||||
|
||||
/*
|
||||
* stadium_words_init - Zeroes the word_id -> cell_index map (DICTIONARY_SIZE
|
||||
* entries, static -- no allocation). Must be called after stadium_boot_init()
|
||||
* and stadium_birth_hera(), before any word ever dispatches. Safe to call
|
||||
* again (re-zeroes); nothing does today.
|
||||
*/
|
||||
void stadium_words_init(void);
|
||||
|
||||
/*
|
||||
* stadium_word_dispatch - The per-dispatch entry point (FABRIC.md §17.7),
|
||||
* called once per DictEntry touched at each of vm_core.c's three
|
||||
* physics_execution_heat_increment() call sites -- deliberately mirroring
|
||||
* that function's existing call pattern 1:1, including the entry != canon
|
||||
* double-touch case, rather than inventing a different shape.
|
||||
*
|
||||
* If word_id is already resident: applies the redirected Loop #3 cooling
|
||||
* (fraction of the cell's own current heat, scaled by elapsed_ticks since
|
||||
* this word's own last touch -- STADIUM_WORD_COOL_RATE_Q48) crediting the
|
||||
* cooled amount back to vm_id's reservoir, then pulls
|
||||
* STADIUM_WORD_HEAT_QUANTUM from the reservoir into the cell (both clamped
|
||||
* to what the reservoir actually holds).
|
||||
*
|
||||
* If word_id is not resident (or the map's entry is stale -- self-healing
|
||||
* check against the cell's discriminator bit and identity, covers both a
|
||||
* prior eviction and a FORGET/redefine word_id reuse this function did not
|
||||
* itself clear): attempts Option B starter-grant admission -- pulls
|
||||
* STADIUM_WORD_HEAT_QUANTUM from the reservoir, builds an unpinned COOL
|
||||
* candidate, calls stadium_admit(). On refusal, pushes the pulled quantum
|
||||
* back (rollback, preserves conservation across the failed attempt). On
|
||||
* success, records the mapping and increments the promotion counter.
|
||||
*
|
||||
* No-op if word_id == WORD_ID_INVALID, word_id >= DICTIONARY_SIZE, or the
|
||||
* word layer has not been initialized.
|
||||
*
|
||||
* @param vm_id Owning VM. Hardcoded to vm_uuid_hera() at every
|
||||
* call site today -- Tripod is pruned to Hera alone
|
||||
* (item 0.1), and she is the only VM with a Stadium
|
||||
* quota. Revisit when item 4.2 restores Hermes.
|
||||
* @param word_id The dispatching DictEntry's stable word_id.
|
||||
* @param heartbeat_ticks Current vm->heartbeat.tick_count (virtual tick,
|
||||
* never wall-clock -- same convention as every other
|
||||
* decay computation in this tree).
|
||||
*/
|
||||
void stadium_word_dispatch(VMUuid vm_id, uint32_t word_id, uint64_t heartbeat_ticks);
|
||||
|
||||
/*
|
||||
* stadium_word_forget - Coherence hook for FORGET (word_id recycling).
|
||||
* vm_dictionary_untrack_entry() must call this BEFORE the word_id is pushed
|
||||
* onto vm->recycled_word_ids -- otherwise the next word assigned the same
|
||||
* recycled id would alias onto the forgotten word's still-resident cell and
|
||||
* its stale heat (same failure class as the 2026-08-02 block_words.c
|
||||
* aliasing bug). Evicts the cell if word_id is resident (crediting its heat
|
||||
* back to the reservoir via stadium_evict()'s own credit path) and clears
|
||||
* the map entry. No-op if word_id is not resident, out of range, or the
|
||||
* word layer is not initialized.
|
||||
*
|
||||
* @param word_id The DictEntry's word_id, about to be recycled.
|
||||
*/
|
||||
void stadium_word_forget(uint32_t word_id);
|
||||
|
||||
/*
|
||||
* stadium_words_stats - Promotion/eviction counters (same shape as the old
|
||||
* cache's HotwordsStats.promotions/.evictions, not that struct -- §25.5's
|
||||
* acceptance for item 4.1). Promotion = a successful starter-grant
|
||||
* admission. Eviction = this word's cell was reaped by another admission's
|
||||
* eviction fallback (stadium_admit()'s density comparison), detected
|
||||
* lazily via the self-healing stale check in stadium_word_dispatch(), or
|
||||
* explicitly via stadium_word_forget().
|
||||
*/
|
||||
void stadium_words_stats(uint64_t *promotions, uint64_t *evictions);
|
||||
|
||||
/*
|
||||
* stadium_words_print_boot_diagnostics - Console output satisfying item
|
||||
* 4.1's "observable via a diagnostic word or boot console output"
|
||||
* acceptance line. Prints promotions/evictions, then
|
||||
* Σ(resident heat) + reservoir against Q48_ONE as a conservation check --
|
||||
* not required by the acceptance text, but the mechanism proves nothing if
|
||||
* this silently doesn't hold. The heat sum is taken over ALL resident
|
||||
* Stadium cells, not scoped by owner -- correct only because vm_id is the
|
||||
* sole VM with any Stadium quota today (item 0.1's Hera-only pruning); the
|
||||
* per-cell owner byte is private to stadium.c and has no public accessor.
|
||||
* Revisit the scoping when item 4.2 restores Hermes.
|
||||
*
|
||||
* @param vm_id The VM whose reservoir to read (vm_uuid_hera() today).
|
||||
*/
|
||||
void stadium_words_print_boot_diagnostics(VMUuid vm_id);
|
||||
|
||||
#endif /* __STARKERNEL__ */
|
||||
|
||||
#endif /* STARKERNEL_VM_STADIUM_WORDS_H */
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -55,6 +55,10 @@
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#ifdef __STARKERNEL__
|
||||
#include "starkernel/vm/stadium_words.h" /* item 4.1: FORGET coherence hook */
|
||||
#include "starkernel/vm_uuid.h" /* vm_uuid_hera() */
|
||||
#endif
|
||||
/* LIKELY/UNLIKELY macros are defined in vm.h */
|
||||
|
||||
#ifndef SF_FC_BUCKETS
|
||||
@@ -112,14 +116,28 @@ void vm_dictionary_untrack_entry(VM *vm, DictEntry *entry) {
|
||||
|
||||
/* Cache coherence: the entry is about to be removed (FORGET frees it) —
|
||||
* a stale pointer left in the hot-words ring would be a use-after-free,
|
||||
* and an older same-named word may become visible again. */
|
||||
* and an older same-named word may become visible again.
|
||||
* §17.3 (item 4.1): retired under __STARKERNEL__ -- the kernel word
|
||||
* layer stays inert to this mechanism entirely, so there is nothing to
|
||||
* keep coherent here on the kernel side. Hosted is unaffected. */
|
||||
#ifndef __STARKERNEL__
|
||||
hotwords_cache_evict_entry(vm->hotwords_cache, entry);
|
||||
#endif
|
||||
|
||||
uint32_t word_id = entry->word_id;
|
||||
if (word_id == WORD_ID_INVALID || word_id >= DICTIONARY_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __STARKERNEL__
|
||||
/* item 4.1 coherence hook: if this word_id is resident on the Stadium,
|
||||
* reclaim its cell (crediting heat back to the reservoir) BEFORE the id
|
||||
* is recycled below -- otherwise the next word assigned this same id
|
||||
* would alias onto the forgotten word's stale cell (same failure class
|
||||
* as the 2026-08-02 block_words.c aliasing bug). */
|
||||
stadium_word_forget(word_id);
|
||||
#endif
|
||||
|
||||
if (vm->word_id_map[word_id] == entry) {
|
||||
vm->word_id_map[word_id] = NULL;
|
||||
}
|
||||
@@ -316,12 +334,18 @@ DictEntry *vm_find_word(VM *vm, const char *name, size_t len) {
|
||||
|
||||
/* Use hot-words cache for physics-driven frequency-based acceleration.
|
||||
* The cache's bucket fallback resolves via vm_dict_resolve_in_bucket(),
|
||||
* so a non-NULL result already honors newest-first shadowing. */
|
||||
* so a non-NULL result already honors newest-first shadowing.
|
||||
* §17.3 (item 4.1): bypassed under __STARKERNEL__ -- the kernel side
|
||||
* feeds the Stadium instead (vm_core.c's dispatch sites), and this
|
||||
* lookup fast path is retired in favor of the ordinary bucket scan
|
||||
* below. Hosted is unaffected. */
|
||||
#ifndef __STARKERNEL__
|
||||
DictEntry *found = hotwords_cache_lookup(vm, vm->hotwords_cache, bucket, n, name, len);
|
||||
if (found) {
|
||||
sf_mutex_unlock(&vm->dict_lock);
|
||||
return found;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Phase 2: Choose lookup strategy based on pattern diversity */
|
||||
if (vm->lookup_strategy == 1) {
|
||||
@@ -431,8 +455,12 @@ DictEntry *vm_create_word(VM *vm, const char *name, size_t len, word_func_t func
|
||||
|
||||
/* Cache coherence: this definition may shadow an older word of the same
|
||||
* name that the hot-words cache is still serving. Evict the name so the
|
||||
* next lookup re-resolves through the arbitrated bucket scan. */
|
||||
* next lookup re-resolves through the arbitrated bucket scan.
|
||||
* §17.3 (item 4.1): retired under __STARKERNEL__, same as the other
|
||||
* hotwords_cache_* call sites in this file. */
|
||||
#ifndef __STARKERNEL__
|
||||
hotwords_cache_evict_name(vm->hotwords_cache, name, len);
|
||||
#endif
|
||||
|
||||
log_message(LOG_DEBUG, "vm_create_word: '%.*s' len=%zu total=%zu @%p",
|
||||
(int) len, name, len, total, (void *) entry);
|
||||
@@ -492,9 +520,12 @@ void vm_hide_word(VM *vm) {
|
||||
if (vm && vm->latest) {
|
||||
vm->latest->flags |= WORD_HIDDEN;
|
||||
physics_metadata_refresh_state(vm->latest);
|
||||
/* Visibility changed: this name may now resolve to an older entry */
|
||||
/* Visibility changed: this name may now resolve to an older entry.
|
||||
* §17.3 (item 4.1): retired under __STARKERNEL__. */
|
||||
#ifndef __STARKERNEL__
|
||||
hotwords_cache_evict_name(vm->hotwords_cache,
|
||||
vm->latest->name, vm->latest->name_len);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,9 +539,12 @@ void vm_smudge_word(VM *vm) {
|
||||
}
|
||||
vm->latest->flags ^= WORD_SMUDGED;
|
||||
physics_metadata_refresh_state(vm->latest);
|
||||
/* Visibility changed in either direction: force re-resolution */
|
||||
/* Visibility changed in either direction: force re-resolution.
|
||||
* §17.3 (item 4.1): retired under __STARKERNEL__. */
|
||||
#ifndef __STARKERNEL__
|
||||
hotwords_cache_evict_name(vm->hotwords_cache,
|
||||
vm->latest->name, vm->latest->name_len);
|
||||
#endif
|
||||
}
|
||||
|
||||
void vm_pin_execution_heat(VM *vm) {
|
||||
|
||||
@@ -53,6 +53,7 @@ EFI_RUNTIME_SERVICES *g_sk_runtime_services = NULL;
|
||||
#include "starkernel/vm/bootstrap/sk_vm_bootstrap.h"
|
||||
#include "starkernel/vm/parity.h"
|
||||
#include "starkernel/vm/stadium.h"
|
||||
#include "starkernel/vm/stadium_words.h"
|
||||
#include "starkernel/capsule_generated.h"
|
||||
#include "starkernel/capsule_loader.h"
|
||||
#include "starkernel/capsule_birth.h" /* capsule_birth_mama, capsule_find_mama_init */
|
||||
@@ -484,6 +485,14 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
* yet, so a failed allocation logs and boot continues. */
|
||||
(void)stadium_boot_init();
|
||||
|
||||
/* item 4.1, FABRIC.md item 3.6/§17.7: actually enforce "Hera is patron
|
||||
* zero" before anything else can land on cell 0 via the free list, then
|
||||
* bring up the word layer's map. Both must happen before the first word
|
||||
* ever dispatches -- capsule birth below runs init.4th, which dispatches
|
||||
* words. */
|
||||
(void)stadium_birth_hera();
|
||||
stadium_words_init();
|
||||
|
||||
/* M7: VM Bootstrap and Parity Validation */
|
||||
console_println("VM: bootstrap parity...");
|
||||
ParityPacket parity_pkt;
|
||||
@@ -614,6 +623,11 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
#ifdef STARFORTH_ENABLE_VM
|
||||
VM *mama = (VM *)sk_get_mama_vm();
|
||||
|
||||
/* item 4.1 diagnostic (§25.5 acceptance: "observable via a diagnostic
|
||||
* word or boot console output"): word patrons already dispatched during
|
||||
* capsule birth above, so this is non-vacuous by this point. */
|
||||
stadium_words_print_boot_diagnostics(vm_uuid_hera());
|
||||
|
||||
/*
|
||||
* Runtime --doe flag: inject "EXEC-DOE BYE" if requested via boot args.
|
||||
* Checked before SK_STARTUP_FORTH so a runtime --doe takes precedence.
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "starkernel/pmm.h"
|
||||
#include "starkernel/console.h"
|
||||
#include "starkernel/hal/hal.h"
|
||||
#include "starkernel/q48_16.h" /* Q48_ONE -- item 4.1's reservoir starts each VM's quota at 1.0 */
|
||||
|
||||
static StadiumCell *stadium_cell_array = (StadiumCell *)0;
|
||||
static uint8_t *stadium_bitmap = (uint8_t *)0;
|
||||
@@ -71,6 +72,11 @@ typedef struct {
|
||||
VMUuid vm_id;
|
||||
int in_use;
|
||||
size_t free_head;
|
||||
uint64_t reservoir; /* item 4.1, FABRIC.md §17.7 -- Q48.16, heat this VM's
|
||||
* quota holds but no resident patron has claimed.
|
||||
* Invariant: Σ(resident patron heat) + reservoir ==
|
||||
* Q48_ONE, checked the same way vm_physics_conserved()
|
||||
* checks the fleet sum. */
|
||||
} StadiumVMQuota;
|
||||
|
||||
static StadiumVMQuota stadium_quotas[STADIUM_MAX_VM_COUNT];
|
||||
@@ -152,11 +158,16 @@ int stadium_boot_init(void) {
|
||||
stadium_quotas[i].vm_id = vm_uuid_none();
|
||||
stadium_quotas[i].in_use = 0;
|
||||
stadium_quotas[i].free_head = STADIUM_CELL_NONE;
|
||||
stadium_quotas[i].reservoir = 0;
|
||||
}
|
||||
}
|
||||
stadium_quotas[0].vm_id = vm_uuid_hera();
|
||||
stadium_quotas[0].in_use = 1;
|
||||
stadium_quotas[0].free_head = 0;
|
||||
/* item 4.1, §17.7: at quota-grant time, before any resident patron
|
||||
* exists, the reservoir holds the VM's entire conserved share -- mirrors
|
||||
* Hera holding the fleet's whole Q48_ONE before any other VM is born. */
|
||||
stadium_quotas[0].reservoir = Q48_ONE;
|
||||
|
||||
stadium_cell_array = cells;
|
||||
stadium_bitmap = bitmap;
|
||||
@@ -282,6 +293,13 @@ int stadium_evict(size_t cell_index) {
|
||||
bitmap_clear(cell_index);
|
||||
slot = stadium_owner[cell_index];
|
||||
|
||||
/* item 4.1, §17.7: the departing patron's remaining heat must flow back
|
||||
* to its owner's reservoir before the cell returns to the free list, or
|
||||
* every reap leaks heat and Σ(resident) + reservoir drifts below
|
||||
* Q48_ONE. Captured BEFORE the zero-fill below, which would otherwise
|
||||
* destroy it. */
|
||||
stadium_quotas[slot].reservoir += header->heat;
|
||||
|
||||
{
|
||||
uint8_t *raw = (uint8_t *)header;
|
||||
size_t i;
|
||||
@@ -380,4 +398,64 @@ size_t stadium_admit(VMUuid vm_id, const StadiumPatronHeader *candidate) {
|
||||
return idx;
|
||||
}
|
||||
|
||||
uint64_t stadium_reservoir_pull(VMUuid vm_id, uint64_t amount) {
|
||||
int slot = quota_slot_for_vm(vm_id);
|
||||
uint64_t pulled;
|
||||
|
||||
if (slot < 0) return 0;
|
||||
|
||||
pulled = (amount > stadium_quotas[slot].reservoir) ? stadium_quotas[slot].reservoir : amount;
|
||||
stadium_quotas[slot].reservoir -= pulled;
|
||||
return pulled;
|
||||
}
|
||||
|
||||
void stadium_reservoir_push(VMUuid vm_id, uint64_t amount) {
|
||||
int slot = quota_slot_for_vm(vm_id);
|
||||
|
||||
if (slot < 0) return;
|
||||
stadium_quotas[slot].reservoir += amount;
|
||||
}
|
||||
|
||||
uint64_t stadium_reservoir_peek(VMUuid vm_id) {
|
||||
int slot = quota_slot_for_vm(vm_id);
|
||||
|
||||
if (slot < 0) return 0;
|
||||
return stadium_quotas[slot].reservoir;
|
||||
}
|
||||
|
||||
/*
|
||||
* FABRIC.md item 3.6 / item 4.1: see stadium.h's doc. Idempotent via the
|
||||
* item-3.1 discriminator bitmap -- if cell 0 already reads as resident,
|
||||
* something already birthed her (or, if it isn't actually Hera, something
|
||||
* else already claimed cell 0 -- either way this must not clobber it).
|
||||
*/
|
||||
int stadium_birth_hera(void) {
|
||||
StadiumPatronHeader candidate;
|
||||
size_t idx;
|
||||
|
||||
if (!stadium_initialized) return -1;
|
||||
if (stadium_ncells > 0 && bitmap_get(STADIUM_HERA_CELL_INDEX)) return 0;
|
||||
|
||||
{
|
||||
uint8_t *raw = (uint8_t *)&candidate;
|
||||
size_t i;
|
||||
for (i = 0; i < sizeof(candidate); i++) raw[i] = 0;
|
||||
}
|
||||
candidate.identity = 0;
|
||||
candidate.heat = 0;
|
||||
candidate.ttl = 0;
|
||||
candidate.link = STADIUM_LINK_NONE;
|
||||
candidate.contains = STADIUM_CONTAINS_NONE;
|
||||
candidate.mass = 1;
|
||||
candidate.flags = STADIUM_FLAG_PIN;
|
||||
candidate.behaviour = (uint8_t)STADIUM_BEHAVIOUR_COOL;
|
||||
|
||||
idx = stadium_admit(vm_uuid_hera(), &candidate);
|
||||
if (idx == STADIUM_CELL_NONE) return -1; /* refused; should not happen (quota is fresh and empty) */
|
||||
if (idx != STADIUM_HERA_CELL_INDEX) {
|
||||
sk_hal_panic("Stadium: birth_hera did not land on cell 0 -- patron-zero invariant broken");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* __STARKERNEL__ */
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
|
||||
Copyright (c) 2023–2025 Robert A. James
|
||||
All rights reserved.
|
||||
|
||||
This file is part of the StarForth project.
|
||||
|
||||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
You may obtain a copy of the License at:
|
||||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||||
|
||||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
express or implied, including but not limited to the warranties of
|
||||
merchantability, fitness for a particular purpose, and noninfringement.
|
||||
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* stadium_words.c - Word patrons on the Stadium (FABRIC.md §17.3/§17.7,
|
||||
* punch list item 4.1). See stadium_words.h for the design rationale.
|
||||
*/
|
||||
|
||||
#include "starkernel/vm/stadium_words.h"
|
||||
|
||||
#ifdef __STARKERNEL__
|
||||
|
||||
#include "starkernel/vm/stadium.h"
|
||||
#include "starkernel/console.h"
|
||||
#include "starkernel/q48_16.h" /* Q48_ONE -- diagnostic print only */
|
||||
#include "vm.h" /* DICTIONARY_SIZE, WORD_ID_INVALID */
|
||||
|
||||
/*
|
||||
* StadiumWordSlot - the kernel-side word_id -> cell_index map (decided
|
||||
* 2026-08-05: no DictEntry field). `last_decay_tick` is this layer's own
|
||||
* bookkeeping, separate from DictEntry.physics.last_decay_tick -- that field
|
||||
* belongs to execution_heat's decay, which item 4.1 does not touch.
|
||||
*/
|
||||
typedef struct {
|
||||
size_t cell_index; /* STADIUM_CELL_NONE if not resident */
|
||||
uint64_t last_decay_tick;
|
||||
} StadiumWordSlot;
|
||||
|
||||
static StadiumWordSlot word_slots[DICTIONARY_SIZE];
|
||||
static int words_initialized = 0;
|
||||
static uint64_t stat_promotions = 0;
|
||||
static uint64_t stat_evictions = 0;
|
||||
|
||||
void stadium_words_init(void) {
|
||||
uint32_t i;
|
||||
for (i = 0; i < DICTIONARY_SIZE; i++) {
|
||||
word_slots[i].cell_index = STADIUM_CELL_NONE;
|
||||
word_slots[i].last_decay_tick = 0;
|
||||
}
|
||||
stat_promotions = 0;
|
||||
stat_evictions = 0;
|
||||
words_initialized = 1;
|
||||
}
|
||||
|
||||
static int cell_is_resident(size_t idx) {
|
||||
const uint8_t *bm = stadium_header_bitmap();
|
||||
if (!bm) return 0;
|
||||
return (bm[idx / 8u] >> (idx % 8u)) & 1u;
|
||||
}
|
||||
|
||||
/*
|
||||
* resolve_resident_cell - Self-healing lookup (advisor-flagged reverse
|
||||
* coherence gap): the map may claim word_id is resident at a cell that was
|
||||
* actually reclaimed by someone else's stadium_admit() eviction fallback
|
||||
* since the map was last written. Detected here, lazily, against ground
|
||||
* truth already public via stadium_cells()/stadium_header_bitmap() --
|
||||
* no new coupling from stadium.c into this file. A stale mapping is cleared
|
||||
* and counted as an eviction on discovery.
|
||||
*/
|
||||
static size_t resolve_resident_cell(uint32_t word_id) {
|
||||
size_t cell = word_slots[word_id].cell_index;
|
||||
StadiumCell *cells;
|
||||
|
||||
if (cell == STADIUM_CELL_NONE) return STADIUM_CELL_NONE;
|
||||
if (cell >= stadium_cell_count() || !cell_is_resident(cell)) {
|
||||
word_slots[word_id].cell_index = STADIUM_CELL_NONE;
|
||||
stat_evictions++;
|
||||
return STADIUM_CELL_NONE;
|
||||
}
|
||||
cells = stadium_cells();
|
||||
if (cells[cell].header.identity != (uint64_t)word_id) {
|
||||
word_slots[word_id].cell_index = STADIUM_CELL_NONE;
|
||||
stat_evictions++;
|
||||
return STADIUM_CELL_NONE;
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
void stadium_word_dispatch(VMUuid vm_id, uint32_t word_id, uint64_t heartbeat_ticks) {
|
||||
size_t cell;
|
||||
|
||||
if (!words_initialized) return;
|
||||
if (word_id == WORD_ID_INVALID || word_id >= DICTIONARY_SIZE) return;
|
||||
|
||||
cell = resolve_resident_cell(word_id);
|
||||
|
||||
if (cell != STADIUM_CELL_NONE) {
|
||||
StadiumPatronHeader *h = &stadium_cells()[cell].header;
|
||||
uint64_t elapsed = heartbeat_ticks - word_slots[word_id].last_decay_tick;
|
||||
|
||||
if (elapsed > 0) {
|
||||
/* Redirected Loop #3 (§17.7): a FRACTION of the cell's own
|
||||
* current heat per elapsed tick -- unit-safe for a conserved
|
||||
* share of 1.0, unlike execution_heat's flat per-tick amount.
|
||||
* STADIUM_WORD_COOL_RATE_Q48 / 65536 is that fraction. */
|
||||
uint64_t per_tick = (h->heat * (uint64_t)STADIUM_WORD_COOL_RATE_Q48) >> 16;
|
||||
uint64_t cooled = per_tick * elapsed;
|
||||
if (cooled > h->heat) cooled = h->heat;
|
||||
if (cooled > 0) {
|
||||
h->heat -= cooled;
|
||||
stadium_reservoir_push(vm_id, cooled);
|
||||
}
|
||||
word_slots[word_id].last_decay_tick = heartbeat_ticks;
|
||||
}
|
||||
|
||||
h->heat += stadium_reservoir_pull(vm_id, (uint64_t)STADIUM_WORD_HEAT_QUANTUM);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Not resident: Option B starter-grant admission (§17.7). execution_heat
|
||||
* plays no role -- density is decided entirely by the pulled quantum. */
|
||||
{
|
||||
uint64_t pulled = stadium_reservoir_pull(vm_id, (uint64_t)STADIUM_WORD_HEAT_QUANTUM);
|
||||
StadiumPatronHeader candidate;
|
||||
uint8_t *raw = (uint8_t *)&candidate;
|
||||
size_t i;
|
||||
size_t idx;
|
||||
|
||||
for (i = 0; i < sizeof(candidate); i++) raw[i] = 0;
|
||||
candidate.identity = (uint64_t)word_id;
|
||||
candidate.heat = pulled;
|
||||
candidate.ttl = 0;
|
||||
candidate.link = 0; /* unused for word patrons; no continuation/consumer yet */
|
||||
candidate.contains = STADIUM_CONTAINS_NONE;
|
||||
candidate.mass = 1;
|
||||
candidate.flags = 0; /* unpinned -- words carry no pin exception (§17.3) */
|
||||
candidate.behaviour = (uint8_t)STADIUM_BEHAVIOUR_COOL;
|
||||
|
||||
idx = stadium_admit(vm_id, &candidate);
|
||||
if (idx == STADIUM_CELL_NONE) {
|
||||
stadium_reservoir_push(vm_id, pulled); /* rollback: preserve conservation */
|
||||
return;
|
||||
}
|
||||
|
||||
word_slots[word_id].cell_index = idx;
|
||||
word_slots[word_id].last_decay_tick = heartbeat_ticks;
|
||||
stat_promotions++;
|
||||
}
|
||||
}
|
||||
|
||||
void stadium_word_forget(uint32_t word_id) {
|
||||
size_t cell;
|
||||
|
||||
if (!words_initialized) return;
|
||||
if (word_id == WORD_ID_INVALID || word_id >= DICTIONARY_SIZE) return;
|
||||
|
||||
cell = resolve_resident_cell(word_id);
|
||||
if (cell == STADIUM_CELL_NONE) return;
|
||||
|
||||
if (stadium_evict(cell) == 0) {
|
||||
word_slots[word_id].cell_index = STADIUM_CELL_NONE;
|
||||
stat_evictions++;
|
||||
}
|
||||
}
|
||||
|
||||
void stadium_words_stats(uint64_t *promotions, uint64_t *evictions) {
|
||||
if (promotions) *promotions = stat_promotions;
|
||||
if (evictions) *evictions = stat_evictions;
|
||||
}
|
||||
|
||||
/* Freestanding: no libc printf. Prints an unsigned decimal, no leading
|
||||
* zeros -- same small utility stadium.c already duplicates locally. */
|
||||
static void console_put_u64(uint64_t v) {
|
||||
char buf[21];
|
||||
int i = 20;
|
||||
buf[20] = '\0';
|
||||
if (v == 0) {
|
||||
console_puts("0");
|
||||
return;
|
||||
}
|
||||
while (v > 0 && i > 0) {
|
||||
buf[--i] = (char)('0' + (v % 10));
|
||||
v /= 10;
|
||||
}
|
||||
console_puts(&buf[i]);
|
||||
}
|
||||
|
||||
void stadium_words_print_boot_diagnostics(VMUuid vm_id) {
|
||||
uint64_t promotions = 0, evictions = 0;
|
||||
uint64_t resident_sum = 0;
|
||||
uint64_t reservoir;
|
||||
size_t ncells = stadium_cell_count();
|
||||
size_t i;
|
||||
|
||||
stadium_words_stats(&promotions, &evictions);
|
||||
|
||||
for (i = 0; i < ncells; i++) {
|
||||
if (cell_is_resident(i)) {
|
||||
resident_sum += stadium_cells()[i].header.heat;
|
||||
}
|
||||
}
|
||||
reservoir = stadium_reservoir_peek(vm_id);
|
||||
|
||||
console_puts("Stadium words: promotions=");
|
||||
console_put_u64(promotions);
|
||||
console_puts(" evictions=");
|
||||
console_put_u64(evictions);
|
||||
console_println("");
|
||||
|
||||
console_puts("Stadium conservation: resident_sum=");
|
||||
console_put_u64(resident_sum);
|
||||
console_puts(" reservoir=");
|
||||
console_put_u64(reservoir);
|
||||
console_puts(" sum=");
|
||||
console_put_u64(resident_sum + reservoir);
|
||||
console_puts(" (Q48_ONE=");
|
||||
console_put_u64((uint64_t)Q48_ONE);
|
||||
console_println(")");
|
||||
}
|
||||
|
||||
#endif /* __STARKERNEL__ */
|
||||
@@ -58,6 +58,7 @@
|
||||
#include "starkernel/vm/arena.h"
|
||||
#include "starkernel/console.h" /* g_sk_fault_word */
|
||||
#include "platform_alloc.h" /* sf_free for call_stack */
|
||||
#include "starkernel/vm/stadium_words.h" /* item 4.1: word patrons on the Stadium */
|
||||
#endif
|
||||
#include "word_source/include/vocabulary_words.h"
|
||||
#include "vm_internal.h"
|
||||
@@ -681,6 +682,12 @@ void execute_colon_word(VM* vm)
|
||||
|
||||
physics_execution_heat_increment(w);
|
||||
|
||||
/* item 4.1, FABRIC.md §17.7: feed the Stadium's independent
|
||||
* conserved heat wire. execution_heat above is untouched by
|
||||
* this call. vm_uuid_hera() is hardcoded here -- Tripod is
|
||||
* pruned to Hera alone (item 0.1); revisit at item 4.2. */
|
||||
stadium_word_dispatch(vm_uuid_hera(), w->word_id, vm->heartbeat.tick_count);
|
||||
|
||||
uint32_t word_id = w->word_id;
|
||||
if (word_id < DICTIONARY_SIZE)
|
||||
{
|
||||
@@ -874,6 +881,7 @@ void vm_interpret_word(VM* vm, const char* word_str, size_t len)
|
||||
entry->physics.last_decay_ns = lookup_ns;
|
||||
|
||||
physics_execution_heat_increment(entry);
|
||||
stadium_word_dispatch(vm_uuid_hera(), entry->word_id, vm->heartbeat.tick_count);
|
||||
if (canon && canon != entry)
|
||||
{
|
||||
/* Apply decay to canonical entry as well */
|
||||
@@ -884,6 +892,7 @@ void vm_interpret_word(VM* vm, const char* word_str, size_t len)
|
||||
canon->physics.last_decay_ns = lookup_ns;
|
||||
|
||||
physics_execution_heat_increment(canon);
|
||||
stadium_word_dispatch(vm_uuid_hera(), canon->word_id, vm->heartbeat.tick_count);
|
||||
physics_metadata_touch(canon, canon->execution_heat, lookup_ns);
|
||||
}
|
||||
sf_mutex_unlock(&vm->dict_lock);
|
||||
|
||||
Reference in New Issue
Block a user