Stage S-1b: dictionary word ids grow with the dictionary; a word that can't get an id is refused whole (FABRIC-3.9.0 §8.1)
- word_id_map / recycled_word_ids are heap tables (one shared capacity, 64 doubling), grown all-or-nothing by allocate-copy-free. Numbering is unchanged, so boot parity is unchanged. - vm_create_word() takes the id before linking; on failure it frees the entry, sets vm->error, logs LOG_ERROR and counts (vm_dictionary_word_id_refusals()). No word runs with WORD_ID_INVALID. - Readers of the id space bound by vm->next_word_id, not DICTIONARY_SIZE. - transition_heat gets its own capacity and grows on record (was sized once -- a heap overflow under a dynamic bound); every reader bounds by it. - Stadium id->cell rows start empty and grow on dispatch; a failed grow is logged, counted and not admitted. S-2 (the cell pool) stays open. Probe (reverted), identical on amd64/aarch64/riscv64: +2000 words get real ids (capacity 4096); a word at id 2738 lands 200x in the rolling window and is Stadium-admitted; FORGET recycles across the old boundary; an injected grow failure refuses the word whole (NULL, error, counter, dictionary unchanged) and the retry succeeds. 3-ISA acceptance passes; boot parity (531, 0x572daac83b797332) and the dict_hash set unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ee3db96ea8
commit
1dbd0c74ed
+15
-3
@@ -510,8 +510,15 @@ typedef struct VM
|
||||
uint8_t* memory; /**< Unified VM memory buffer */
|
||||
uintptr_t here; /**< Next free memory location (byte offset or absolute pointer if M7) */
|
||||
DictEntry* latest; /**< Most recent word */
|
||||
DictEntry* word_id_map[DICTIONARY_SIZE]; /**< Stable ID → entry map for speculation */
|
||||
uint32_t recycled_word_ids[DICTIONARY_SIZE]; /**< Reusable ID stack for FORGET */
|
||||
/* FABRIC-3.9.0.md §8.1 (Stage S-1b): the stable-id tables grow with the
|
||||
* dictionary (were fixed [DICTIONARY_SIZE] arrays -- word 1025 onward got
|
||||
* WORD_ID_INVALID and ran outside the physics). Both share one capacity:
|
||||
* the recycle stack can never hold more ids than were ever issued. Every
|
||||
* issued id, live or recycled, is < next_word_id -- that is the bound for
|
||||
* any reader of the id space. */
|
||||
DictEntry** word_id_map; /**< Stable ID → entry map for speculation (word_id_capacity entries) */
|
||||
uint32_t* recycled_word_ids; /**< Reusable ID stack for FORGET (word_id_capacity entries) */
|
||||
uint32_t word_id_capacity; /**< Allocated length of both tables */
|
||||
uint32_t recycled_word_id_count; /**< Depth of recycled stack */
|
||||
uint32_t next_word_id; /**< Next fresh ID when recycle stack empty */
|
||||
|
||||
@@ -767,7 +774,12 @@ DictEntry* vm_dictionary_lookup_by_word_id(VM* vm, uint32_t word_id);
|
||||
* popped off the data stack (EXECUTE, ENTROPY@/!, etc). */
|
||||
int vm_dict_entry_ok(VM* vm, DictEntry* candidate);
|
||||
|
||||
void vm_dictionary_track_entry(VM* vm, DictEntry* entry);
|
||||
/* Assign `entry` a stable word id, growing the id tables if needed (S-1b).
|
||||
* Returns 0, or -1 if the tables can't grow: logged at LOG_ERROR, counted
|
||||
* in vm_dictionary_word_id_refusals(), and the caller must refuse the word. */
|
||||
int vm_dictionary_track_entry(VM* vm, DictEntry* entry);
|
||||
|
||||
uint32_t vm_dictionary_word_id_refusals(void);
|
||||
|
||||
void vm_dictionary_untrack_entry(VM* vm, DictEntry* entry);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user