Stage K-2: COLD forgets to the fence; PAD moved below the block window (FABRIC-3.10.8 §4.2)
- Defect 1: COLD now forgets every word since the fence exactly as FORGET does (new vm_dictionary_forget_to_fence(): untrack, metrics, entry) and rewinds HERE to the fence. It rewound HERE alone to 1024, leaving live words to be compiled over. - Defect 2: PAD = BLK_VM_WINDOW_BASE - 512 (was inside the window's top slot, shared with BLOCK/BUFFER). - Probe (plain FORTH, all three ISAs identical): PAD..PAD+511 = 5238272..5238783 below the window (5238784); on a worker KA/KC found, COLD, then KA/KB/KC not found, HERE at the fence (1424), new word works. - Standard sequence on amd64/aarch64/riscv64: hash and G unchanged, MINT/WIREBIND/USE ok, conserved, 0 idle growth. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
27c430e0ca
commit
c9c97cf769
@@ -479,3 +479,36 @@ In the same run, after the second churn, rajames's `1 2 + .` was **refused by ke
|
|||||||
|
|
||||||
- The `dict_hash` set, the G string and the ledger line are unchanged.
|
- The `dict_hash` set, the G string and the ledger line are unchanged.
|
||||||
- **K-1 closes §8 defects 5–9 and the `sf_realloc()`/call-state-stack bug.** Its churn-leak criterion carries into K-4.
|
- **K-1 closes §8 defects 5–9 and the `sf_realloc()`/call-state-stack bug.** Its churn-leak criterion carries into K-4.
|
||||||
|
|
||||||
|
### 4.2 K-2 — `COLD` and `PAD` (2026-09-26)
|
||||||
|
|
||||||
|
**Change.**
|
||||||
|
- **Defect 1 (`COLD`).** `reset_vm_state()`'s cold branch now calls a new `vm_dictionary_forget_to_fence()` (dictionary_management.c). It forgets every word defined since the fence exactly as FORGET does, and rewinds `HERE` to the fence:
|
||||||
|
- untrack (the id recycled, the Stadium cell reclaimed);
|
||||||
|
- free the transition metrics and their arrays;
|
||||||
|
- free the entry.
|
||||||
|
|
||||||
|
It used to rewind `HERE` alone, to 1024, leaving `latest` at live words that the next definitions compiled over.
|
||||||
|
- The fence is set after `vm_init()` allots the input buffers, so `HERE` can't land below them.
|
||||||
|
- What `COLD` forgets is therefore what FORGET could forget. For a worker that includes its workload capsule's words (the fence is set at `vm_init()`). Hera's SDK capsule raises FENCE, so her base and SDK words survive.
|
||||||
|
- FORGET itself is unchanged.
|
||||||
|
- **Defect 2 (`PAD`).** `PAD` is now `BLK_VM_WINDOW_BASE - 512`, the 512 bytes just below the block I/O window. It was `VM_MEMORY_SIZE - 512`, inside the window's top slot. The span between the 2 MB dictionary limit and the window is otherwise unused.
|
||||||
|
|
||||||
|
**Probe** (plain FORTH, `k2.sh`, run on the clean build after O-A; no temporary code):
|
||||||
|
- `7770 . PAD . PAD 511 + .` on Hera;
|
||||||
|
- a worker `kc` defines KA, KB and KC and prints whether KA and KC are findable, then HERE;
|
||||||
|
- then `COLD`, `FIND` for each of the three, HERE, and a new definition `: KD 4 ; KD .`.
|
||||||
|
|
||||||
|
The results are identical on all three ISAs:
|
||||||
|
|
||||||
|
| ISA | log | PAD … PAD+511 | window base / dictionary limit | before COLD: KA, KC found; HERE | after COLD: KA, KB, KC; HERE; KD |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| amd64 | `logs/20260926-023300` | 5,238,272 … 5,238,783 | 5,238,784 / 2,097,152 | yes, yes; 1856 | 0, 0, 0; 1424 (the fence); 4 |
|
||||||
|
| aarch64 | `logs/20260926-024629` | same | same | same | same |
|
||||||
|
| riscv64 | `logs/20260926-030013` | same | same | same | same |
|
||||||
|
|
||||||
|
**Standard sequence** (the same runs, after the probe).
|
||||||
|
- All three ISAs pass: hash `0x99b01134c3d3049c`, G byte-identical (A=40 B=40, run before the probe), MINT / WIREBIND / USE / `1 2 + .` → `3`, 0 B idle growth, 0 UNKNOWN and 0 faults.
|
||||||
|
- The ledger totals (pulled 262144 / returned 259359) are higher than the usual line only because the probe worker's messages are counted; held and consumed are unchanged, and `stadium_conserved(self)=true`.
|
||||||
|
- The hosted `make` builds.
|
||||||
|
- **K-2 closes §8 defects 1 and 2.**
|
||||||
|
|||||||
@@ -808,6 +808,11 @@ uint32_t vm_dictionary_word_id_refusals(void);
|
|||||||
|
|
||||||
void vm_dictionary_untrack_entry(VM* vm, DictEntry* entry);
|
void vm_dictionary_untrack_entry(VM* vm, DictEntry* entry);
|
||||||
|
|
||||||
|
/* FABRIC-3.10.8 K-2: forget every word defined since the fence (as FORGET
|
||||||
|
* does) and rewind HERE to the fence -- COLD's dictionary reset. Returns
|
||||||
|
* the number of words forgotten. */
|
||||||
|
int vm_dictionary_forget_to_fence(VM* vm);
|
||||||
|
|
||||||
cell_t* vm_dictionary_get_data_field(DictEntry* entry);
|
cell_t* vm_dictionary_get_data_field(DictEntry* entry);
|
||||||
|
|
||||||
void vm_compile_word(VM* vm, DictEntry* entry);
|
void vm_compile_word(VM* vm, DictEntry* entry);
|
||||||
|
|||||||
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
@@ -692,3 +692,33 @@ void vm_unpin_execution_heat(VM *vm) {
|
|||||||
|
|
||||||
/* MC-0c: vm_dictionary_index_reset() (the global index's hard reset, no
|
/* MC-0c: vm_dictionary_index_reset() (the global index's hard reset, no
|
||||||
* callers) is gone; a VM's index is freed with the VM, vm_dictionary_index_free(). */
|
* callers) is gone; a VM's index is freed with the VM, vm_dictionary_index_free(). */
|
||||||
|
|
||||||
|
/* FABRIC-3.10.8 K-2: forget everything defined since the fence, exactly as
|
||||||
|
* FORGET removes words (untrack -- id recycled, Stadium cell reclaimed --
|
||||||
|
* then metrics, then the entry), and rewind HERE to the fence. COLD's
|
||||||
|
* dictionary half: it used to rewind HERE alone, leaving `latest` pointing
|
||||||
|
* at words whose code HERE would then compile over. Returns the number of
|
||||||
|
* words forgotten. */
|
||||||
|
int vm_dictionary_forget_to_fence(VM *vm) {
|
||||||
|
int n = 0;
|
||||||
|
DictEntry *e;
|
||||||
|
if (!vm) return 0;
|
||||||
|
sf_mutex_lock(&vm->dict_lock);
|
||||||
|
e = vm->latest;
|
||||||
|
while (e && e != vm->dict_fence_latest) {
|
||||||
|
DictEntry *next = e->link;
|
||||||
|
vm_dictionary_untrack_entry(vm, e);
|
||||||
|
if (e->transition_metrics) {
|
||||||
|
transition_metrics_cleanup(e->transition_metrics);
|
||||||
|
sf_free(e->transition_metrics);
|
||||||
|
e->transition_metrics = NULL;
|
||||||
|
}
|
||||||
|
sf_free(e);
|
||||||
|
n++;
|
||||||
|
e = next;
|
||||||
|
}
|
||||||
|
vm->latest = vm->dict_fence_latest;
|
||||||
|
vm->here = vm->dict_fence_here;
|
||||||
|
sf_mutex_unlock(&vm->dict_lock);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|||||||
@@ -161,10 +161,14 @@ void dictionary_word_2comma(VM *vm) {
|
|||||||
/**
|
/**
|
||||||
* @brief PAD ( -- addr ) Get scratch pad buffer address
|
* @brief PAD ( -- addr ) Get scratch pad buffer address
|
||||||
* @param vm Pointer to the VM structure
|
* @param vm Pointer to the VM structure
|
||||||
* @details Returns VM address of 512-byte temporary text buffer at top of memory
|
* @details Returns VM address of the 512-byte temporary text buffer: the
|
||||||
|
* 512 bytes just below the block I/O window. FABRIC-3.10.8 K-2: it used to
|
||||||
|
* be VM_MEMORY_SIZE - 512, inside the window's top slot, so BLOCK/BUFFER
|
||||||
|
* and PAD overwrote each other. The span between the dictionary limit
|
||||||
|
* (DICTIONARY_MEMORY_SIZE) and the window is otherwise unused.
|
||||||
*/
|
*/
|
||||||
void dictionary_word_pad(VM *vm) {
|
void dictionary_word_pad(VM *vm) {
|
||||||
size_t pad_offset = VM_MEMORY_SIZE - 512;
|
size_t pad_offset = BLK_VM_WINDOW_BASE - 512;
|
||||||
vm_push(vm, (cell_t) pad_offset);
|
vm_push(vm, (cell_t) pad_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,18 +88,14 @@ static void reset_vm_state(VM *vm, int cold_start) {
|
|||||||
vm->mode = MODE_INTERPRET;
|
vm->mode = MODE_INTERPRET;
|
||||||
|
|
||||||
if (cold_start) {
|
if (cold_start) {
|
||||||
/* In a complete system, this would restore a boot image.
|
/* FABRIC-3.10.8 K-2: back to the post-boot dictionary -- every word
|
||||||
For now we keep a minimal fence by not crushing the base dictionary. */
|
* defined since the fence forgotten (as FORGET does it) and HERE at
|
||||||
if (vm->here > 1024) {
|
* the fence. This used to rewind HERE alone, to 1024, leaving
|
||||||
/* S-1c: never below the input buffers allotted at vm_init()
|
* `latest` pointing at live words that the next definitions then
|
||||||
* (TIB/>IN/SPAN/WORD buffer end past 1024). */
|
* compiled over. The fence is set after vm_init() allots the input
|
||||||
size_t floor = 1024;
|
* buffers (S-1c), so HERE never lands below them. */
|
||||||
if (vm->word_buf) {
|
int n = vm_dictionary_forget_to_fence(vm);
|
||||||
size_t buf_end = (size_t)(vm->word_buf - vm->memory) + WORD_BUFFER_SIZE;
|
log_message(LOG_INFO, "COLD: %d word(s) forgotten, HERE=%ld", n, (long) vm->here);
|
||||||
if (buf_end > floor) floor = buf_end;
|
|
||||||
}
|
|
||||||
if (vm->here > floor) vm->here = floor;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
system_running = 1;
|
system_running = 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user