Migrates Hermes's message/channel lifecycle onto the Stadium's unified heat/capacity economy: MSG-ALLOC/FREE-NODE and CH-ALLOC/FREE-NODE now route entirely through stadium_admit()/stadium_evict(), replacing the old local free-list + independent heat-field mechanism. Eight kernel-only STADIUM-* FORTH primitives (ADMIT, EVICT, RES@, RES-PULL, RES-PUSH, HEAT@, HEAT!, WORD-HEAT), VM.stadium_vm_id threaded through all three vm_core.c dispatch sites (replacing item 4.1's hardcoded vm_uuid_hera()), and the stadium_owner[idx] fix so evict-credit lands in the VM that actually admitted a patron, not whoever owned cell 0. This session's own contribution, on top of that pre-existing implementation: found and fixed two bugs blocking the item's own K≡1.0 conservation self-check (HERMES-K was reading 0, not 65536): - Q.SLOT admission-heat fix (capsules/hermes/init.4th): MSG-SEND/ CH-ACCEPT admitted with Q.1 (the entire fleet-wide "1.0" unit) per item, a leftover from before the Stadium migration when each message/channel had its own unconstrained heat field. Instantly drained the shared, finite reservoir. - Reservoir floor for word-execution admission (stadium_words.c): stadium_word_dispatch() (item 4.1) pulls STADIUM_WORD_HEAT_QUANTUM on every word dispatch, not just first admission -- exhausts a VM's entire reservoir in ~32 dispatches, starving any application-level economy sharing that VM's reservoir before it gets a chance to pull anything. word_dispatch_pull() now clamps word-execution's own pulls to leave a Q48_ONE/3 floor (same fair-share figure COMMON-CH's own floor already uses); application-level pulls are unaffected. - STADIUM-WORD-HEAT primitive + stadium_words_resident_heat(): the floor deliberately leaves word-execution residents holding real heat, invisible to HERMES-K's original formula (MSG+CH+reservoir, no term for word patrons). Adding this term closes K to exactly 65536 on all three architectures. Also rules on two open scope questions in FABRIC.md: MBR-ALLOC/ MBR-FREE-NODE stay off the Stadium (membership records have no heat field, never did -- the acceptance bullet's inclusion of them was a completeness gesture predating a check of the actual layout), and records the effort number (12 implementation files, +759/-120 lines). Verified: all three architectures boot clean, full self-test passes, Stadium conservation closes exactly (resident_sum + reservoir = Q48_ONE) at both the C/Stadium level and the FORTH-level HERMES-K check. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
728 lines
30 KiB
C
728 lines
30 KiB
C
/*
|
||
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.
|
||
|
||
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.
|
||
|
||
*/
|
||
|
||
#ifndef VM_H
|
||
#define VM_H
|
||
|
||
/* Bare metal - no system headers, but we need FILE for heartbeat CSV export */
|
||
#include <stdint.h>
|
||
#include <stddef.h>
|
||
#if defined(__STDC_HOSTED__) && __STDC_HOSTED__
|
||
#include <stdio.h>
|
||
#endif
|
||
|
||
#include "platform_lock.h"
|
||
#include "starforth_config.h"
|
||
|
||
/* Forward declarations */
|
||
struct VM;
|
||
struct HotwordsCache_s; /* Physics hot-words cache (avoids circular include) */
|
||
typedef struct HotwordsCache_s HotwordsCache;
|
||
struct WordTransitionMetrics; /* Pipelining metrics (avoids circular include) */
|
||
typedef struct WordTransitionMetrics WordTransitionMetrics;
|
||
struct VMHostServices; /* Host allocator/time hooks (defined in vm_host.h) */
|
||
typedef struct VMHostServices VMHostServices;
|
||
struct InferenceOutputs; /* Adaptive tuning inference engine outputs (avoids circular include) */
|
||
typedef struct InferenceOutputs InferenceOutputs;
|
||
struct HeartbeatWorker; /* Background heartbeat dispatcher */
|
||
typedef struct HeartbeatWorker HeartbeatWorker;
|
||
|
||
/* Bare metal type definitions */
|
||
#if defined(__amd64__) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_X64)
|
||
typedef int64_t cell_t;
|
||
typedef uint64_t ucell_t;
|
||
#else
|
||
typedef signed long cell_t;
|
||
typedef unsigned long ucell_t;
|
||
#endif
|
||
|
||
/* ============================================================================
|
||
* Rolling Window of Truth (Embedded to avoid circular includes)
|
||
* ============================================================================
|
||
*
|
||
* Circular buffer capturing execution history for deterministic metric seeding.
|
||
* Defined here to avoid include dependency issues.
|
||
*
|
||
* Knob #7: ROLLING_WINDOW_SIZE (tunable initial/conservative size)
|
||
* - Default: 4096 (conservative capture of full pattern diversity at cold start)
|
||
* - Usage: make ROLLING_WINDOW_SIZE=8192 (or 2048, 16384, etc. for different workloads)
|
||
* - Starting large ensures statistical significance on cold systems
|
||
* - System automatically shrinks during execution if diminishing returns detected
|
||
* - Window is "warm" after ROLLING_WINDOW_SIZE executions
|
||
* (once warm, adaptive shrinking logic can reduce size if beneficial)
|
||
* - Knobs control adaptive shrinking behavior (rate, bounds, thresholds)
|
||
*/
|
||
|
||
typedef struct
|
||
{
|
||
uint32_t* execution_history; /* Circular buffer of word IDs */
|
||
uint32_t* snapshot_buffers[2]; /* Double-buffer snapshots for readers */
|
||
uint32_t window_pos; /* Current write position */
|
||
uint64_t total_executions; /* Lifetime execution counter */
|
||
int is_warm; /* 1 if window contains representative data */
|
||
|
||
/* Adaptive window sizing: continuous self-tuning during execution */
|
||
uint32_t effective_window_size; /* Current effective size (may shrink from initial) */
|
||
uint64_t last_pattern_diversity; /* Pattern diversity at last check */
|
||
uint64_t pattern_diversity_check_count; /* How many times we've checked diversity */
|
||
volatile uint32_t snapshot_index; /* Published snapshot buffer */
|
||
volatile uint32_t snapshot_pending; /* Writer signaled new data */
|
||
uint32_t snapshot_window_pos[2];
|
||
uint64_t snapshot_total_executions[2];
|
||
uint32_t snapshot_effective_window_size[2];
|
||
int snapshot_is_warm[2];
|
||
uint32_t adaptive_check_accumulator; /* Execution counter toward next check */
|
||
volatile uint32_t adaptive_pending; /* Heartbeat should run adaptive shrink */
|
||
} RollingWindowOfTruth;
|
||
|
||
|
||
typedef void (*word_func_t)(struct VM* vm);
|
||
|
||
/* ===== VM address model (Phase 1 scaffolding) ============================
|
||
* StarForth rule: addresses on the data stack are VM OFFSETS, not C pointers.
|
||
* vaddr_t is a byte offset into the VM's virtual address space.
|
||
* Implementations come in a later change; these are declarations only.
|
||
*/
|
||
typedef uint64_t vaddr_t;
|
||
|
||
/* Bounds check: nonzero if [addr, addr+len) is a valid VM range */
|
||
int vm_addr_ok(struct VM* vm, vaddr_t addr, size_t len);
|
||
|
||
/* Internal pointer materialization for subsystems (NOT for word sources) */
|
||
uint8_t* vm_ptr(struct VM* vm, vaddr_t addr);
|
||
|
||
/* Canonical memory accessors; all word code will funnel through these */
|
||
uint8_t vm_load_u8(struct VM* vm, vaddr_t addr);
|
||
|
||
void vm_store_u8(struct VM* vm, vaddr_t addr, uint8_t v);
|
||
|
||
cell_t vm_load_cell(struct VM* vm, vaddr_t addr); /* requires alignment */
|
||
void vm_store_cell(struct VM* vm, vaddr_t addr, cell_t v);
|
||
|
||
/* Explicit stack<->offset conversions (keep intent obvious) */
|
||
static inline vaddr_t VM_ADDR(cell_t c) { return (vaddr_t)(uint64_t)c; }
|
||
static inline cell_t CELL(vaddr_t a) { return (cell_t)(int64_t)a; }
|
||
/* ======================================================================== */
|
||
|
||
#define STACK_SIZE 1024
|
||
#define DICTIONARY_SIZE 1024
|
||
#define WORD_ID_INVALID UINT32_MAX
|
||
#define VM_MEMORY_SIZE (5 * 1024 * 1024) /* 5 MB total VM memory */
|
||
#define INPUT_BUFFER_SIZE 1025
|
||
#define WORD_NAME_MAX 31
|
||
#define COMPILE_BUFFER_SIZE 1024
|
||
|
||
/* Block system configuration */
|
||
#define BLOCK_SIZE 1024 /* 1KB per block */
|
||
#define MAX_BLOCKS (VM_MEMORY_SIZE / BLOCK_SIZE) /* 5120 blocks from 5MB */
|
||
|
||
/* Block I/O window: FORTH-visible copies of block content inside vm->memory.
|
||
* Sits at the top of vm->memory, above the dictionary limit (2MB). BLOCK and
|
||
* BUFFER copy C-layer buffers into these slots and return VM offsets so that
|
||
* C@/C!/@ work correctly. UPDATE copies the slot back to the C layer. */
|
||
#define BLK_VM_SLOTS 4
|
||
#define BLK_VM_WINDOW_SIZE (BLK_VM_SLOTS * BLOCK_SIZE)
|
||
#define BLK_VM_WINDOW_BASE (VM_MEMORY_SIZE - BLK_VM_WINDOW_SIZE)
|
||
|
||
/* Memory layout constants */
|
||
#define DICTIONARY_BLOCKS 2048 /* First 2048 blocks (2MB) for dictionary */
|
||
#define DICTIONARY_MEMORY_SIZE (DICTIONARY_BLOCKS * BLOCK_SIZE)
|
||
#define USER_BLOCKS_START DICTIONARY_BLOCKS /* User blocks start at block 2048 */
|
||
|
||
/* Persistent log configuration */
|
||
#define LOG_LINE_MAX 64 /* Each log line is 64 bytes */
|
||
#define LOG_LINES_PER_BLOCK (BLOCK_SIZE / LOG_LINE_MAX) /* 16 lines per 1KB block */
|
||
#define LOG_BLOCKS_START 3072 /* Log starts at block 3072 */
|
||
#define LOG_BLOCKS_END 5120 /* Log ends at block 5120 (exclusive) */
|
||
#define LOG_BLOCKS (LOG_BLOCKS_END - LOG_BLOCKS_START) /* 2048 blocks (2MB) for logs */
|
||
#define LOG_LAYER1_MAX_LINES (LOG_BLOCKS * LOG_LINES_PER_BLOCK) /* 32768 max lines */
|
||
|
||
/* Word flags */
|
||
#define WORD_IMMEDIATE 0x80 /* Word executes immediately even in compile mode */
|
||
#define WORD_HIDDEN 0x40 /* Word is hidden from dictionary searches */
|
||
#define WORD_SMUDGED 0x20 /* Word is smudged (being defined) - FORTH-79 */
|
||
#define WORD_COMPILED 0x10 /* Word is user-compiled (not built-in) */
|
||
#define WORD_PINNED 0x08 /* Word's execution heat is pinned (cannot decay to zero) */
|
||
#define WORD_FROZEN 0x04 /* Word is frozen: execution heat does not decay (Phase 2) */
|
||
|
||
/* ACL (Access Control List) defaults - stub implementation */
|
||
#define ACL_USER_DEFAULT 0x01 /* Default access: users can execute and compile */
|
||
|
||
/* ACL mode constants */
|
||
#define ACL_MODE_TTL 0 /* Adaptive TTL countdown; 0 → calls ACL-RECHECK */
|
||
#define ACL_MODE_STRICT 1 /* Re-check every execution (STRICT mode) */
|
||
#define ACL_TTL_OPEN UINT32_MAX /* Permissive TTL before ACL.4th loads */
|
||
|
||
/* Physics model constants - execution heat drives optimization decisions */
|
||
#define SPIN_IDLE 0 /* Particle spin state: idle */
|
||
#define CHARGE_NEUTRAL 0 /* Particle charge state: neutral (execution heat acts as charge) */
|
||
|
||
/* Physics state flags */
|
||
#define PHYSICS_STATE_IMMEDIATE 0x01
|
||
#define PHYSICS_STATE_PINNED 0x02
|
||
#define PHYSICS_STATE_HIDDEN 0x04
|
||
#define PHYSICS_STATE_COMPILED 0x08
|
||
|
||
/* Phase 2: Decay mechanism configuration (tunable via starforth_config.h) */
|
||
|
||
#ifndef HEAT_CACHE_DEMOTION_THRESHOLD
|
||
#define HEAT_CACHE_DEMOTION_THRESHOLD 10 /* Demotion from hotwords cache threshold */
|
||
#endif
|
||
|
||
/* Heartbeat tuning frequencies (in ticks) */
|
||
/* Defaults live in starforth_config.h */
|
||
|
||
|
||
typedef struct
|
||
{
|
||
uint64_t published_tick; /* Tick counter when snapshot committed */
|
||
uint64_t published_ns; /* Host monotonic timestamp of publish */
|
||
uint32_t window_width; /* Effective rolling window size */
|
||
uint32_t actual_window_size; /* Actual ring buffer fill level: min(total_executions, ROLLING_WINDOW_SIZE) */
|
||
uint64_t decay_slope_q48; /* Active decay slope */
|
||
uint64_t hot_word_count; /* Words above heat threshold */
|
||
uint64_t stale_word_count; /* Words in cooling band */
|
||
uint64_t total_heat; /* Aggregate execution heat */
|
||
} HeartbeatSnapshot;
|
||
|
||
/* ===== Per-Tick Instrumentation (Phase 2: Multivariate Dynamics) ==========
|
||
*
|
||
* Lightweight circular buffer capture for multivariate systems analysis.
|
||
* Captures 7 key metrics every heartbeat tick (~1ms) with negligible overhead.
|
||
* Used to measure coupled dynamics, convergence, coupling strength, stability.
|
||
*/
|
||
typedef struct
|
||
{
|
||
uint32_t tick_number; /* Sequential tick counter */
|
||
uint64_t elapsed_ns; /* Total elapsed since run start */
|
||
uint64_t tick_interval_ns; /* Actual tick interval from prior tick */
|
||
|
||
uint32_t cache_hits_delta; /* Cache hits during this tick */
|
||
uint32_t bucket_hits_delta; /* Bucket hits during this tick */
|
||
uint32_t word_executions_delta; /* Words executed during this tick */
|
||
|
||
uint64_t hot_word_count; /* Words above heat threshold */
|
||
double avg_word_heat; /* Mean execution heat (Q48.16 / 65536) */
|
||
uint32_t window_width; /* Current rolling window size */
|
||
uint32_t actual_window_size; /* Actual ring buffer fill level: min(total_executions, ROLLING_WINDOW_SIZE) */
|
||
|
||
uint32_t predicted_label_hits; /* Successful context predictions */
|
||
double estimated_jitter_ns; /* Deviation from nominal tick */
|
||
|
||
uint8_t l8_mode; /* L8 Jacquard mode selector state (0-15) */
|
||
} HeartbeatTickSnapshot;
|
||
|
||
#define HEARTBEAT_TICK_BUFFER_SIZE 100000 /* ~100K ticks = ~100 seconds at 1kHz */
|
||
|
||
/* Phase 1 physics metadata */
|
||
typedef struct
|
||
{
|
||
uint16_t temperature_q8; /* Execution-heat-backed hotness scaled to Q8 */
|
||
uint64_t last_active_ns; /* Monotonic timestamp of last execution -- diagnostics only, never read for decay */
|
||
uint64_t last_decay_ns; /* Timestamp of last decay application -- diagnostics only, never read for decay */
|
||
uint64_t last_decay_tick; /* vm->heartbeat.tick_count as of last decay application -- the only clock Loop #3 decay reads */
|
||
uint32_t mass_bytes; /* Header + body footprint */
|
||
uint32_t avg_latency_ns; /* Rolling average latency (PROFILE_DETAILED+) */
|
||
uint8_t state_flags; /* Encoded execution traits (immediate, pinned, etc.) */
|
||
uint8_t acl_hint; /* Reserved for governance-driven ACLs */
|
||
uint16_t pubsub_mask; /* Reserved topic bitmap */
|
||
} DictPhysics;
|
||
|
||
/* ===== VM Heartbeat Architecture (Loop #3 & #5 Coordination) ============
|
||
*
|
||
* Centralized time-driven tuning dispatcher coordinating:
|
||
* - Loop #3: Heat decay validation (slope_validator, replaced by inference engine)
|
||
* - Loop #5: Context-aware window tuning (window_tuner, replaced by inference engine)
|
||
* - Unified Inference Engine: ANOVA early-exit + window width + decay slope inference
|
||
*
|
||
* Can run synchronously (now) or in background thread (future).
|
||
* Designed as plugin architecture for extensibility.
|
||
*/
|
||
typedef struct
|
||
{
|
||
uint64_t tick_count; /* Total heartbeat ticks since VM init */
|
||
uint64_t last_inference_tick; /* Last tick we ran full inference engine */
|
||
uint32_t check_counter; /* Counter to trigger vm_tick() every N executions */
|
||
int heartbeat_enabled; /* 1 = heartbeat active, 0 = disabled */
|
||
uint64_t tick_target_ns; /* Wake-up cadence (ns) */
|
||
volatile uint32_t snapshot_index; /* Published snapshot slot */
|
||
HeartbeatSnapshot snapshots[2]; /* Double-buffered read-only snapshots */
|
||
HeartbeatWorker* worker; /* Background thread context (opaque) */
|
||
|
||
/* === DoE Observation Counters (2^7 Factorial) === */
|
||
uint64_t inference_run_count; /* Times inference engine was invoked */
|
||
uint64_t early_exit_count; /* ANOVA early-exits (variance stable) */
|
||
uint64_t words_executed; /* Total word executions */
|
||
uint64_t dictionary_lookups; /* Dictionary search operations */
|
||
|
||
/* === Per-tick instrumentation (Phase 2: Multivariate Dynamics) === */
|
||
HeartbeatTickSnapshot* tick_buffer; /* Circular buffer of per-tick snapshots */
|
||
uint32_t tick_buffer_size; /* Allocated size (HEARTBEAT_TICK_BUFFER_SIZE) */
|
||
uint64_t tick_buffer_write_index; /* Current write position (wraps) */
|
||
uint64_t tick_count_total; /* Total ticks since run start (monotonic) */
|
||
uint64_t run_start_ns; /* Monotonic time at run start */
|
||
uint32_t tick_number_offset; /* Tick counter for this run */
|
||
|
||
/* === L8 Attractor Bucket Statistics === */
|
||
double bucket_sum_K; /* Sum of K values across bucket */
|
||
double bucket_sum_K_squared; /* Sum of K^2 for variance calculation */
|
||
double bucket_sum_window_variance; /* Sum of window variance across bucket */
|
||
double bucket_sum_heat_variance; /* Sum of heat variance across bucket */
|
||
uint64_t bucket_mode_transitions; /* Count of L8 mode transitions in bucket */
|
||
uint32_t bucket_collapse_flag; /* Flag indicating bucket collapse event */
|
||
uint32_t bucket_tick_count; /* Number of ticks in current bucket */
|
||
|
||
/* === M5 Time Trust Fields === */
|
||
uint64_t m5_time_trust; /* TIME-TRUST in Q48.16 format */
|
||
uint64_t m5_variance; /* Timing variance in Q48.16 format */
|
||
} HeartbeatState;
|
||
|
||
/* ===== Pipelining Global Metrics (Loop #4 & #5 Feedback) ================
|
||
*
|
||
* Aggregated metrics for speculative prefetch accuracy tracking.
|
||
* Used to guide window size tuning via binary chop (Loop #5).
|
||
*/
|
||
typedef struct
|
||
{
|
||
uint64_t prefetch_attempts; /* Total speculative prefetch calls */
|
||
uint64_t prefetch_hits; /* Successful hits (word was looked up next) */
|
||
uint64_t window_tuning_checks; /* How many times we've checked window size */
|
||
uint32_t last_checked_window_size; /* Window size at last tuning check */
|
||
double last_checked_accuracy; /* Prefetch accuracy at last check */
|
||
uint32_t suggested_next_size; /* What binary chop recommends trying */
|
||
} PipelineGlobalMetrics;
|
||
|
||
/* Dictionary entry - enhanced for FORTH-79 compatibility */
|
||
typedef struct DictEntry
|
||
{
|
||
struct DictEntry* link; /* Previous word (linked list) */
|
||
word_func_t func; /* Function pointer for execution */
|
||
cell_t flags; /* Word flags - increased to cell_t for alignment and absolute flags if needed */
|
||
uint8_t name_len; /* Length of name */
|
||
cell_t execution_heat; /* Execution frequency counter */
|
||
uint8_t acl_default; /* Access control list default permissions */
|
||
uint32_t acl_ttl; /* countdown; 0 → ACL-RECHECK; default 0 */
|
||
uint8_t acl_allow; /* cached decision: 1=allow 0=deny; default 1 */
|
||
uint8_t acl_mode; /* ACL_MODE_TTL=0, ACL_MODE_STRICT=1; default TTL */
|
||
uint8_t acl_pinned; /* 1=immutable forever; default 0 */
|
||
uint32_t word_id; /* Stable dictionary identifier */
|
||
DictPhysics physics; /* Physics properties */
|
||
WordTransitionMetrics* transition_metrics; /* Absolute pointer to metrics */
|
||
char name[]; /* Variable-length name */
|
||
} __attribute__((aligned(8))) DictEntry;
|
||
|
||
/* VM modes */
|
||
typedef enum
|
||
{
|
||
MODE_INTERPRET = 0,
|
||
MODE_COMPILE = 1
|
||
} vm_mode_t;
|
||
|
||
#ifdef __STARKERNEL__
|
||
#include "starkernel/vm_uuid.h" /* VMUuid -- item 4.2, VM.stadium_vm_id */
|
||
|
||
/* Saved interpreter state for VM lifecycle calls (BIRTH, VM-EXEC, START).
|
||
* Dynamically allocated; grows on demand via sf_realloc. */
|
||
typedef struct {
|
||
int rsp;
|
||
int exit_colon;
|
||
int ecw_nesting;
|
||
} VMCallState;
|
||
#endif
|
||
|
||
/**
|
||
* @struct VM
|
||
* @brief Main virtual machine state container
|
||
*
|
||
* Contains all state information for a StarForth VM instance including stacks,
|
||
* dictionary, memory, and execution state.
|
||
*/
|
||
typedef struct VM
|
||
{
|
||
/** @name Stack Management
|
||
* @{
|
||
*/
|
||
cell_t data_stack[STACK_SIZE]; /**< Parameter stack storage */
|
||
cell_t return_stack[STACK_SIZE]; /**< Return stack storage */
|
||
int dsp; /**< Data stack pointer */
|
||
int rsp; /**< Return stack pointer */
|
||
int exit_colon; /**< Exit flag for colon definitions */
|
||
int abort_requested; /**< ABORT flag for immediate termination */
|
||
int ecw_nesting; /**< Depth of execute_colon_word call stack; >0 means inside a colon word */
|
||
volatile int doe_row_printing; /**< Non-zero while EMIT-ROW assembles a row; heartbeat output must defer */
|
||
uint8_t emergency_console; /**< 1 = fault handler active; bypasses all ACL checks (C-only write) */
|
||
uint8_t zuse_session; /**< 1 = zuse authenticated at console; shows zuse)ok> prompt */
|
||
uint8_t acl_skip; /**< 1 = skip all ACL hooks (Ananke enforcement VM; prevents recursion) */
|
||
/** @} */
|
||
|
||
/** @name Dictionary Management
|
||
* @{
|
||
*/
|
||
const VMHostServices* host; /**< Host allocator/time services */
|
||
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 */
|
||
uint32_t recycled_word_id_count; /**< Depth of recycled stack */
|
||
uint32_t next_word_id; /**< Next fresh ID when recycle stack empty */
|
||
|
||
/* Dictionary protection fence: words at/older than this are protected from FORGET */
|
||
DictEntry* dict_fence_latest;
|
||
size_t dict_fence_here;
|
||
sf_mutex_t dict_lock; /**< Protects dictionary structural mutations */
|
||
|
||
/* Input system */
|
||
char input_buffer[INPUT_BUFFER_SIZE];
|
||
size_t input_length;
|
||
size_t input_pos;
|
||
|
||
/* Compiler state */
|
||
vm_mode_t mode;
|
||
DictEntry* compiling_word; /* Word being compiled */
|
||
int interpreter_enabled;
|
||
|
||
/* Compilation support */
|
||
char current_word_name[WORD_NAME_MAX + 1];
|
||
cell_t* compile_buffer;
|
||
size_t compile_pos;
|
||
size_t compile_size;
|
||
|
||
/* FORTH-79 Dictionary manipulation support */
|
||
cell_t state_var; /* STATE variable (0 interpret, -1 compile) */
|
||
|
||
/* Execution bookkeeping */
|
||
DictEntry* current_executing_entry;
|
||
cell_t* ip; /* direct-threaded instruction pointer (points into memory in CELLS) */
|
||
|
||
/* VM state */
|
||
int error;
|
||
int halted;
|
||
|
||
/* Numeric base (Forth BASE). Default 10. */
|
||
cell_t base;
|
||
|
||
/* TIB stuff */
|
||
unsigned char* tib_buf; /* pointer to TIB buffer (host alloc for now) */
|
||
size_t tib_cap; /* capacity in bytes */
|
||
cell_t* in_var; /* >IN (legacy; will migrate to VM addr) */
|
||
cell_t* span_var; /* SPAN (legacy; will migrate to VM addr) */
|
||
|
||
/* Block system (VM-backed variables / addresses) */
|
||
vaddr_t scr_addr;
|
||
vaddr_t state_addr; /* VM cell holding STATE (0=interp, -1=compile) */
|
||
vaddr_t base_addr; /* VM cell: numeric base (2..36), default 10 */
|
||
vaddr_t hold_addr; /* VM memory offset of the 64-byte pictured-number hold buffer */
|
||
int hold_pos; /* Current fill count in hold buffer (<# ... #> state) */
|
||
|
||
/* Block I/O window — BLK_VM_SLOTS slots at BLK_VM_WINDOW_BASE in vm->memory.
|
||
* BLOCK/BUFFER copy C-layer buffers here; UPDATE copies back; lbn==0 means empty. */
|
||
uint32_t blk_vm_lbn[BLK_VM_SLOTS]; /* LBN currently in each slot (0 = empty) */
|
||
uint8_t *blk_vm_cbuf[BLK_VM_SLOTS]; /* C buffer pointer from blk_get_buffer */
|
||
uint8_t blk_vm_dirty[BLK_VM_SLOTS]; /* 1 = vm->memory modified since last sync */
|
||
int blk_vm_next; /* Round-robin eviction cursor */
|
||
|
||
/** @name Physics Hot-Words Cache
|
||
* @{
|
||
*/
|
||
HotwordsCache* hotwords_cache; /**< Frequency-driven dictionary acceleration cache */
|
||
/** @} */
|
||
|
||
/** @name Rolling Window of Truth (Deterministic Metrics)
|
||
* @{
|
||
*/
|
||
RollingWindowOfTruth rolling_window; /**< Execution history for reproducible seeding */
|
||
/** @} */
|
||
|
||
/** @name Phase 2: Heat-Aware Dictionary Optimization
|
||
* @{
|
||
*/
|
||
cell_t heat_threshold_25th; /**< 25th percentile heat (bucket search priority) */
|
||
cell_t heat_threshold_50th; /**< 50th percentile heat */
|
||
cell_t heat_threshold_75th; /**< 75th percentile heat */
|
||
uint64_t last_bucket_reorg_ns; /**< When we last reorganized buckets */
|
||
int lookup_strategy; /**< 0=naive, 1=heat-aware, 2=inference-reorg */
|
||
/** @} */
|
||
|
||
/** @name VM Heartbeat (Loop #3 & #5 Coordination)
|
||
* @{
|
||
*/
|
||
HeartbeatState heartbeat; /**< Centralized time-driven tuning dispatcher */
|
||
uint32_t heartbeat_decay_cursor_id; /**< Continuation cursor for background decay sweeps */
|
||
/** @} */
|
||
|
||
/** @name Pipelining Global Metrics (Loop #4 & #5 Feedback)
|
||
* @{
|
||
*/
|
||
PipelineGlobalMetrics pipeline_metrics; /**< Aggregated prefetch accuracy tracking */
|
||
/** @} */
|
||
|
||
/** @name Physics Loop #3: Adaptive Heat Decay Tuning
|
||
* @{
|
||
*/
|
||
uint64_t decay_slope_q48; /**< Current decay rate (Q48.16 fixed-point, starts at 2:1 = 131072) */
|
||
uint64_t last_decay_check_ns; /**< Timestamp of last decay effectiveness measurement */
|
||
uint64_t total_heat_at_last_check; /**< Total dictionary heat at last check (for slope inference) */
|
||
uint64_t hot_word_count_at_check; /**< Hot words observed at last inference */
|
||
uint64_t stale_word_count_at_check; /**< Stale words at last check (for trend analysis) */
|
||
uint32_t word_count_at_check; /**< Total word count at last check (for ratio calculation) */
|
||
int decay_slope_direction; /**< -1 = decrease slope, 0 = stable, +1 = increase slope (inference) */
|
||
sf_mutex_t tuning_lock; /**< Guards shared tuning knobs (slope, snapshots) */
|
||
/** @} */
|
||
|
||
/** @name Unified Inference Engine (Phase 2: Replacing Loops #3 & #5)
|
||
* @{
|
||
*/
|
||
InferenceOutputs* last_inference_outputs; /**< Cached inference outputs (for ANOVA early-exit and tuning application) */
|
||
/** @} */
|
||
|
||
/** @name SSM L8: Jacquard Mode Selector (Steady State Machine)
|
||
* @{
|
||
*/
|
||
void* ssm_l8_state; /**< Opaque pointer to ssm_l8_state_t (avoid circular include) */
|
||
void* ssm_config; /**< Opaque pointer to ssm_config_t (L3/L4 mode bits) */
|
||
/** @} */
|
||
|
||
#ifdef __STARKERNEL__
|
||
/** @name VM Lifecycle Call State Stack
|
||
* @{
|
||
*/
|
||
VMCallState *call_stack; /**< Dynamically-allocated lifecycle state stack */
|
||
int call_sp; /**< Current depth (0 = empty) */
|
||
int call_stack_cap; /**< Allocated capacity in entries */
|
||
int call_stack_max; /**< High-water mark depth (DoE metric) */
|
||
/** @} */
|
||
|
||
/** @name Stadium Identity (item 4.2, FABRIC.md §25.5)
|
||
* @{
|
||
*/
|
||
VMUuid stadium_vm_id; /**< This VM's own identity, set once at birth from
|
||
* VMRegistryEntry.vm_id. Lets vm_core.c's
|
||
* stadium_word_dispatch() call sites attribute
|
||
* word-execution heat to the VM that actually
|
||
* ran the word, instead of item 4.1's hardcoded
|
||
* vm_uuid_hera(). */
|
||
/** @} */
|
||
#endif
|
||
} VM;
|
||
|
||
/** @name Core VM Functions
|
||
* @{
|
||
*/
|
||
/*
|
||
* @brief Initialize a new VM instance
|
||
* @param vm Pointer to VM structure to initialize
|
||
*/
|
||
void vm_init(VM* vm);
|
||
|
||
/**
|
||
* @brief Initialize VM with explicit host services
|
||
* @param vm Pointer to VM structure to initialize
|
||
* @param host Host services (allocator/time). If NULL, uses build default.
|
||
*/
|
||
void vm_init_with_host(VM* vm, const VMHostServices *host);
|
||
|
||
/*
|
||
* @brief Interpret a string of Forth code
|
||
* @param vm VM instance to use
|
||
* @param input String containing Forth code to interpret
|
||
*/
|
||
void vm_interpret(VM* vm, const char* input);
|
||
void vm_enable_interpreter(VM* vm);
|
||
|
||
/*
|
||
* @brief Start the VM's read-eval-print loop
|
||
* @param vm VM instance to run
|
||
* @param script_mode If 1, suppress prompts and "ok" output (for piped input)
|
||
*/
|
||
void vm_repl(VM* vm, int script_mode);
|
||
|
||
/** @} */
|
||
|
||
/* Stack operations */
|
||
void vm_push(VM* vm, cell_t value);
|
||
|
||
cell_t vm_pop(VM* vm);
|
||
|
||
void vm_rpush(VM* vm, cell_t value);
|
||
|
||
cell_t vm_rpop(VM* vm);
|
||
|
||
/* Dictionary operations */
|
||
DictEntry* vm_find_word(VM* vm, const char* name, size_t len);
|
||
|
||
/* Arbitrated bucket scan: returns the newest visible (non-hidden, non-smudged)
|
||
* entry matching name among ALL candidates in the bucket. Buckets can be
|
||
* reordered by heat (dict_reorganize_buckets_by_heat), so first-match scans
|
||
* are unsound for shadowed/redefined names; every lookup path must resolve
|
||
* through this. Age is arbitrated via the vm->latest link chain. */
|
||
DictEntry* vm_dict_resolve_in_bucket(VM* vm, DictEntry** bucket, size_t n,
|
||
const char* name, size_t len);
|
||
|
||
DictEntry* vm_create_word(VM* vm, const char* name, size_t len, word_func_t func);
|
||
|
||
void vm_make_immediate(VM* vm);
|
||
void vm_bootstrap_root_vocabulary(VM *vm, const char *name);
|
||
|
||
void vm_hide_word(VM* vm);
|
||
|
||
void vm_smudge_word(VM* vm); /* Added for FORTH-79 SMUDGE */
|
||
void vm_pin_execution_heat(VM* vm); /* Pin execution heat (prevent decay) */
|
||
void vm_unpin_execution_heat(VM* vm); /* Unpin execution heat (allow decay) */
|
||
|
||
/* Enhanced dictionary search functions */
|
||
DictEntry* vm_dictionary_find_by_func(VM* vm, word_func_t func);
|
||
|
||
DictEntry* vm_dictionary_find_latest_by_func(VM* vm, word_func_t func);
|
||
|
||
DictEntry* vm_dictionary_lookup_by_word_id(VM* vm, uint32_t word_id);
|
||
|
||
void vm_dictionary_track_entry(VM* vm, DictEntry* entry);
|
||
|
||
void vm_dictionary_untrack_entry(VM* vm, DictEntry* entry);
|
||
|
||
cell_t* vm_dictionary_get_data_field(DictEntry* entry);
|
||
|
||
void vm_compile_word(VM* vm, DictEntry* entry);
|
||
|
||
/* Memory management */
|
||
void* vm_allot(VM* vm, size_t bytes);
|
||
|
||
void vm_align(VM* vm);
|
||
|
||
/* Input parsing */
|
||
int vm_parse_word(VM* vm, char* word, size_t max_len);
|
||
|
||
int vm_parse_number(VM* vm, const char* str, cell_t* value);
|
||
|
||
/* Compilation */
|
||
void vm_enter_compile_mode(VM* vm, const char* name, size_t len);
|
||
|
||
void vm_exit_compile_mode(VM* vm);
|
||
|
||
/* Colon word execution (exposed for SEE decompiler) */
|
||
void execute_colon_word(VM* vm);
|
||
|
||
void vm_compile_call(VM* vm, word_func_t func);
|
||
|
||
void vm_compile_literal(VM* vm, cell_t value);
|
||
|
||
void vm_compile_exit(VM* vm);
|
||
|
||
void vm_interpret_word(VM* vm, const char* word_str, size_t len);
|
||
|
||
/* Block system integration */
|
||
void* vm_get_block_addr(VM* vm, int block_num);
|
||
|
||
int vm_addr_to_block(VM* vm, void* addr);
|
||
|
||
/* Testing functions */
|
||
void vm_run_smoke_tests(VM* vm);
|
||
|
||
/* Add cleanup function declaration */
|
||
void vm_cleanup(VM* vm);
|
||
|
||
/* ===== VM Heartbeat (Time-Driven Tuning Dispatcher) =====================
|
||
* Centralized coordination of Loop #3 and Loop #5 operations.
|
||
* Called periodically from main execution loop.
|
||
*/
|
||
void vm_tick(VM* vm); /* Main heartbeat dispatcher */
|
||
void vm_tick_inference_engine(VM* vm); /* Unified inference engine (Phase 2, replaces Loops #3 & #5) */
|
||
void vm_tick_window_tuner(VM* vm); /* Loop #5: Context-aware window tuning (legacy, kept for compatibility) */
|
||
void vm_tick_slope_validator(VM* vm); /* Loop #3: Heat decay validation (legacy, kept for compatibility) */
|
||
void vm_snapshot_read(const VM* vm, HeartbeatSnapshot* out_snapshot); /* Copy latest heartbeat snapshot */
|
||
|
||
/* Real-time heartbeat tick emission (Phase 2: Multivariate Dynamics) */
|
||
void heartbeat_capture_tick_snapshot(VM* vm, HeartbeatTickSnapshot* snapshot); /* Capture current VM metrics into tick snapshot */
|
||
void heartbeat_emit_tick_row(VM* vm, HeartbeatTickSnapshot* snapshot); /* Emit tick snapshot as CSV row to stderr */
|
||
|
||
/* Start background heartbeat thread and/or enable heartbeat processing. */
|
||
void vm_heartbeat_start(VM* vm);
|
||
|
||
/* ===== Performance optimizations for release builds ==================== */
|
||
#ifdef STARFORTH_PERFORMANCE
|
||
|
||
/* Fast inline stack operations - skip bounds checking in performance builds */
|
||
static inline void vm_push_fast(VM* vm, cell_t value)
|
||
{
|
||
vm->data_stack[++vm->dsp] = value;
|
||
}
|
||
|
||
static inline cell_t vm_pop_fast(VM* vm)
|
||
{
|
||
return vm->data_stack[vm->dsp--];
|
||
}
|
||
|
||
static inline void vm_rpush_fast(VM* vm, cell_t value)
|
||
{
|
||
vm->return_stack[++vm->rsp] = value;
|
||
}
|
||
|
||
static inline cell_t vm_rpop_fast(VM* vm)
|
||
{
|
||
return vm->return_stack[vm->rsp--];
|
||
}
|
||
|
||
/* Performance macros - use fast paths in hot code */
|
||
#define VM_PUSH(vm, val) vm_push_fast(vm, val)
|
||
#define VM_POP(vm) vm_pop_fast(vm)
|
||
#define VM_RPUSH(vm, val) vm_rpush_fast(vm, val)
|
||
#define VM_RPOP(vm) vm_rpop_fast(vm)
|
||
|
||
/* Likely/unlikely branch prediction hints */
|
||
#define LIKELY(x) __builtin_expect(!!(x), 1)
|
||
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||
|
||
#else
|
||
|
||
/* Safe versions with bounds checking for debug builds */
|
||
#define VM_PUSH(vm, val) vm_push(vm, val)
|
||
#define VM_POP(vm) vm_pop(vm)
|
||
#define VM_RPUSH(vm, val) vm_rpush(vm, val)
|
||
#define VM_RPOP(vm) vm_rpop(vm)
|
||
#define LIKELY(x) (x)
|
||
#define UNLIKELY(x) (x)
|
||
|
||
#endif
|
||
|
||
#endif /* VM_H */
|