Reorganize source tree: kernel/, v3/, v4/ split and board infrastructure
Source tree reorganization: - Move StarForth v3 engine to v3/ (src/, include/, Makefile) - Move kernel to kernel/ (src/, include/, linker/, Makefile) - Create v4/ skeleton for F18-ISA golden model (DECOMPOSITION.md, JUSTIFICATION.md) - Move FABRIC-0..4.md to docs/fabric/ - Move ONTOLOGY.md and ROADMAP.md to docs/ Board infrastructure: - Add boards/ser5/, boards/raspi/, boards/milkv/, boards/zynq7020/ - Each board has board.mk (ISA, CPU flags, boot recipe) and README.md - Root Makefile becomes thin dispatcher: boot_image, all, clean, docs take TARGET - make boot_image TARGET=SER5|RASPI|MILKV builds one GPT/MBR image per board - ZYNQ7020 target exists but stops with clear error (ARMv7 port not built yet) - scripts/mkdiskimage.sh builds disk images for all boards Docs pipeline: - docs/book/ with LaTeX master (main.tex) and Makefile - pandoc converts Markdown to LaTeX at build time - Two Lua filters: table-widths.lua (wide tables wrap), code-breaks.lua (inline code breaks) - make docs builds single PDF (754 pages, 0 missing characters) - make docs TARGET=<board> adds board appendix - build/docs/<book|board>/meta.tex stamps git commit into PDF Bug fixes: - 42 include paths that only worked by accident now use correct relative paths - clang-18 hardcode replaced with configurable CC variable (fixed aarch64 build) - Pi 5: kernel_2712.img linked at 0x80000, .bss zeroed, memory reserved - Doxyfile, .clang-tidy, README.md, Kconfig paths updated Verified: - Hosted v3 build passes 1012 tests, 0 failures - SER5 image boots in QEMU (OVMF), POST passes, K exact (65536 = Q48_ONE) - Milk-V image boots in QEMU (OpenSBI + U-Boot + bootefi), POST passes - make clean TARGET=<board> removes only that board and its ISA objects - make all builds all boards, hosted v3, and docs in one run Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
# include/
|
||||
|
||||
Public headers for the hosted StarForth VM (`src/`). Kernel-only headers
|
||||
live under `include/starkernel/`.
|
||||
|
||||
**Core VM**
|
||||
- `vm.h` — the `VM` struct and all core types (`DictEntry`, `DictPhysics`,
|
||||
stacks, dictionary state); the central header most other files include.
|
||||
- `vm_api.h` — external VM API surface.
|
||||
- `vm_host.h`, `vm_debug.h`, `startup.h`, `version.h` — host integration,
|
||||
debug utilities, startup sequencing, version string.
|
||||
- `cli.h`, `repl.h`, `io.h`, `log.h` — CLI parsing, REPL loop, I/O, logging.
|
||||
- `word_registry.h` — word registration system shared by every
|
||||
`src/word_source/*.c` file.
|
||||
- `compudynamics.h` — the generic compudynamics module: tuning-word/config
|
||||
lookups (`cd_tuning_word()`, `cd_tuning_vm()`) consumed by `ssm_jacquard.c`.
|
||||
|
||||
**Memory / blocks**
|
||||
- `memory_management.h`, `dictionary_management.h` — dictionary allocator
|
||||
and search.
|
||||
- `block_subsystem.h`, `blkcfg.h`, `blkio.h`, `blkio_factory.h` — logical→
|
||||
physical block mapper and pluggable block I/O backends (file/RAM).
|
||||
- `platform_alloc.h`, `platform_lock.h`, `platform_time.h` — platform
|
||||
abstraction shims (hosted vs. kernel allocation/locking/timing).
|
||||
|
||||
**Physics-driven adaptive runtime (7 feedback loops)**
|
||||
- `physics_runtime.h` — main physics coordinator.
|
||||
- `physics_hotwords_cache.h` — Loop #1, execution-heat hot-words cache.
|
||||
- `physics_metadata.h` — per-word `DictPhysics` metadata tracking.
|
||||
- `physics_pipelining_metrics.h` — Loop #4, word-transition prediction.
|
||||
- `physics_execution_hooks.h` — execution instrumentation hook points.
|
||||
- `rolling_window_of_truth.h`, `rolling_window_knobs.h` — Loop #2 circular
|
||||
execution-history buffer and its tuning knobs.
|
||||
- `inference_engine.h` — Loops #5/#6, window-width and decay-slope
|
||||
statistical inference.
|
||||
- `dictionary_heat_optimization.h` — Loop #1 heat counters.
|
||||
- `ssm_jacquard.h` — L8 Jacquard steady-state mode selector.
|
||||
- `doe_metrics.h` — Design of Experiments (2^7 factorial) metrics.
|
||||
- `profiler.h` — performance profiling hooks.
|
||||
|
||||
**Arithmetic / codegen**
|
||||
- `q48_16.h` — Q48.16 deterministic fixed-point arithmetic (used instead of
|
||||
IEEE-754 float specifically to keep cross-architecture behavior
|
||||
bit-identical).
|
||||
- `math_portable.h` — portable math helpers.
|
||||
- `arch_detect.h`, `starforth_config.h` — architecture detection and the
|
||||
build-flag fallback-default layer (used when a `.c` file is compiled by
|
||||
hand without `make`).
|
||||
- `vm_asm_opt.h`, `vm_asm_opt_arm64.h`, `vm_asm_opt_riscv64.h`,
|
||||
`vm_inner_interp_asm.h`, `vm_inner_interp_arm64.h`,
|
||||
`vm_inner_interp_riscv64.h` — per-architecture assembler-optimized inner
|
||||
interpreter (`USE_ASM_OPT=1`).
|
||||
|
||||
See `include/starkernel/README.md` for the bare-metal kernel headers.
|
||||
@@ -0,0 +1,512 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file arch_detect.h
|
||||
* @brief Architecture Detection and Unified API for StarForth
|
||||
* @details Automatically selects x86_64 or ARM64 optimizations and provides
|
||||
* a unified interface for architecture-specific optimizations.
|
||||
*
|
||||
* This work is released under license terms in LICENSE file.
|
||||
* No warranty. Use at your own risk.
|
||||
*/
|
||||
|
||||
#ifndef ARCH_DETECT_H
|
||||
#define ARCH_DETECT_H
|
||||
|
||||
/**
|
||||
* @defgroup arch_detect Architecture Detection
|
||||
* @{
|
||||
*
|
||||
* @brief Automatic detection of target architecture and inclusion of optimization headers
|
||||
*
|
||||
* This module automatically detects the target architecture at compile time and includes
|
||||
* the appropriate assembly optimization headers.
|
||||
*
|
||||
* Supported architectures:
|
||||
* - x86_64 (AMD64, Intel 64)
|
||||
* - ARM64 (AArch64, ARMv8-A)
|
||||
*
|
||||
* Unsupported architectures fall back to pure C implementations.
|
||||
* @}
|
||||
*/
|
||||
|
||||
/* Detect architecture at compile time */
|
||||
#if defined(__x86_64__) || defined(_M_X64) || defined(__amd64__)
|
||||
#define ARCH_X86_64 1
|
||||
#define ARCH_ARM64 0
|
||||
#define ARCH_RISCV64 0
|
||||
#define ARCH_NAME "x86_64"
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64) || defined(__arm64__)
|
||||
#define ARCH_X86_64 0
|
||||
#define ARCH_ARM64 1
|
||||
#define ARCH_RISCV64 0
|
||||
#define ARCH_NAME "ARM64"
|
||||
#elif defined(__riscv) && (__riscv_xlen == 64)
|
||||
#define ARCH_X86_64 0
|
||||
#define ARCH_ARM64 0
|
||||
#define ARCH_RISCV64 1
|
||||
#define ARCH_NAME "RISC-V 64"
|
||||
#else
|
||||
#define ARCH_X86_64 0
|
||||
#define ARCH_ARM64 0
|
||||
#define ARCH_RISCV64 0
|
||||
#define ARCH_NAME "Unknown"
|
||||
#warning "Unknown architecture, assembly optimizations disabled"
|
||||
#endif
|
||||
|
||||
/* Include architecture-specific headers */
|
||||
#if ARCH_X86_64 && USE_ASM_OPT
|
||||
#include "vm_asm_opt.h"
|
||||
#if USE_DIRECT_THREADING
|
||||
#include "vm_inner_interp_asm.h"
|
||||
#endif
|
||||
#elif ARCH_ARM64 && USE_ASM_OPT
|
||||
#include "vm_asm_opt_arm64.h"
|
||||
#if USE_DIRECT_THREADING
|
||||
#include "vm_inner_interp_arm64.h"
|
||||
#endif
|
||||
#elif ARCH_RISCV64 && USE_ASM_OPT
|
||||
#include "vm_asm_opt_riscv64.h"
|
||||
#if USE_DIRECT_THREADING
|
||||
#include "vm_inner_interp_riscv64.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup unified_api Unified API Wrappers
|
||||
* @{
|
||||
*
|
||||
* @brief Consistent API macros across all architectures
|
||||
*
|
||||
* These macros provide a consistent API across all architectures by automatically
|
||||
* dispatching to the appropriate optimized or fallback implementation based on
|
||||
* the target architecture and available optimizations.
|
||||
* @}
|
||||
*/
|
||||
|
||||
/* ========== Stack Operations ========== */
|
||||
|
||||
#if USE_ASM_OPT && (ARCH_X86_64 || ARCH_ARM64 || ARCH_RISCV64)
|
||||
/* Use optimized assembly implementations */
|
||||
#define vm_push_opt(vm, val) vm_push_asm(vm, val)
|
||||
#define vm_pop_opt(vm) vm_pop_asm(vm)
|
||||
#define vm_rpush_opt(vm, val) vm_rpush_asm(vm, val)
|
||||
#define vm_rpop_opt(vm) vm_rpop_asm(vm)
|
||||
#else
|
||||
/* Fall back to standard C implementations */
|
||||
#define vm_push_opt(vm, val) vm_push(vm, val)
|
||||
#define vm_pop_opt(vm) vm_pop(vm)
|
||||
#define vm_rpush_opt(vm, val) vm_rpush(vm, val)
|
||||
#define vm_rpop_opt(vm) vm_rpop(vm)
|
||||
#endif
|
||||
|
||||
/* ========== Arithmetic Operations ========== */
|
||||
|
||||
#if USE_ASM_OPT && (ARCH_X86_64 || ARCH_ARM64 || ARCH_RISCV64)
|
||||
#define vm_add_overflow_opt(a, b, res) vm_add_check_overflow(a, b, res)
|
||||
#define vm_sub_overflow_opt(a, b, res) vm_sub_check_overflow(a, b, res)
|
||||
#define vm_mul_double_opt(a, b, hi, lo) vm_mul_double(a, b, hi, lo)
|
||||
#define vm_divmod_opt(dividend, divisor, quot, rem) vm_divmod(dividend, divisor, quot, rem)
|
||||
#else
|
||||
/* Fallback implementations */
|
||||
static inline int vm_add_overflow_opt(cell_t a, cell_t b, cell_t *res) {
|
||||
*res = a + b;
|
||||
/* Simple overflow check (not perfect) */
|
||||
return ((a > 0 && b > 0 && *res < 0) || (a < 0 && b < 0 && *res > 0));
|
||||
}
|
||||
|
||||
static inline int vm_sub_overflow_opt(cell_t a, cell_t b, cell_t *res) {
|
||||
*res = a - b;
|
||||
return ((a > 0 && b < 0 && *res < 0) || (a < 0 && b > 0 && *res > 0));
|
||||
}
|
||||
|
||||
static inline void vm_mul_double_opt(cell_t a, cell_t b, cell_t *hi, cell_t *lo) {
|
||||
/* Simplified - assumes 64-bit cell_t */
|
||||
*lo = a * b;
|
||||
*hi = 0; /* Would need 128-bit arithmetic for proper high word */
|
||||
}
|
||||
|
||||
static inline void vm_divmod_opt(cell_t dividend, cell_t divisor, cell_t *quot, cell_t *rem) {
|
||||
*quot = dividend / divisor;
|
||||
*rem = dividend % divisor;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ========== String Operations ========== */
|
||||
|
||||
#if USE_ASM_OPT && (ARCH_X86_64 || ARCH_ARM64 || ARCH_RISCV64)
|
||||
#define vm_strcmp_opt(s1, s2, len) vm_strcmp_asm(s1, s2, len)
|
||||
#define vm_memcpy_opt(dst, src, len) vm_memcpy_asm(dst, src, len)
|
||||
#define vm_memzero_opt(dst, len) vm_memzero_asm(dst, len)
|
||||
#else
|
||||
#include <string.h>
|
||||
|
||||
static inline int vm_strcmp_opt(const char *s1, const char *s2, size_t len) {
|
||||
return memcmp(s1, s2, len) != 0;
|
||||
}
|
||||
|
||||
static inline void vm_memcpy_opt(void *dst, const void *src, size_t len) {
|
||||
memcpy(dst, src, len);
|
||||
}
|
||||
|
||||
static inline void vm_memzero_opt(void *dst, size_t len) {
|
||||
memset(dst, 0, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ========== Min/Max Operations ========== */
|
||||
|
||||
#if USE_ASM_OPT && (ARCH_X86_64 || ARCH_ARM64 || ARCH_RISCV64)
|
||||
#define vm_min_opt(a, b) vm_min_asm(a, b)
|
||||
#define vm_max_opt(a, b) vm_max_asm(a, b)
|
||||
#else
|
||||
static inline cell_t vm_min_opt(cell_t a, cell_t b) {
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
|
||||
static inline cell_t vm_max_opt(cell_t a, cell_t b) {
|
||||
return (a > b) ? a : b;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ========== Absolute Value ========== */
|
||||
|
||||
#if USE_ASM_OPT && (ARCH_ARM64 || ARCH_RISCV64)
|
||||
#define vm_abs_opt(a) vm_abs_asm(a)
|
||||
#else
|
||||
static inline cell_t vm_abs_opt(cell_t a) {
|
||||
return (a < 0) ? -a : a;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ========== Bit Operations ========== */
|
||||
|
||||
#if USE_ASM_OPT && (ARCH_ARM64 || ARCH_RISCV64)
|
||||
#define vm_clz_opt(x) vm_clz(x)
|
||||
#define vm_ctz_opt(x) vm_ctz(x)
|
||||
#define vm_popcnt_opt(x) vm_popcnt(x)
|
||||
#elif USE_ASM_OPT && ARCH_X86_64
|
||||
static inline int vm_clz_opt(cell_t x) {
|
||||
return __builtin_clzll((unsigned long long) x);
|
||||
}
|
||||
static inline int vm_ctz_opt(cell_t x) {
|
||||
return __builtin_ctzll((unsigned long long) x);
|
||||
}
|
||||
static inline int vm_popcnt_opt(cell_t x) {
|
||||
return __builtin_popcountll((unsigned long long) x);
|
||||
}
|
||||
#else
|
||||
/* Software fallback */
|
||||
static inline int vm_clz_opt(cell_t x) {
|
||||
if (x == 0) return 64;
|
||||
int n = 0;
|
||||
if ((x & 0xFFFFFFFF00000000ULL) == 0) {
|
||||
n += 32;
|
||||
x <<= 32;
|
||||
}
|
||||
if ((x & 0xFFFF000000000000ULL) == 0) {
|
||||
n += 16;
|
||||
x <<= 16;
|
||||
}
|
||||
if ((x & 0xFF00000000000000ULL) == 0) {
|
||||
n += 8;
|
||||
x <<= 8;
|
||||
}
|
||||
if ((x & 0xF000000000000000ULL) == 0) {
|
||||
n += 4;
|
||||
x <<= 4;
|
||||
}
|
||||
if ((x & 0xC000000000000000ULL) == 0) {
|
||||
n += 2;
|
||||
x <<= 2;
|
||||
}
|
||||
if ((x & 0x8000000000000000ULL) == 0) { n += 1; }
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline int vm_ctz_opt(cell_t x) {
|
||||
if (x == 0) return 64;
|
||||
int n = 0;
|
||||
if ((x & 0x00000000FFFFFFFFULL) == 0) {
|
||||
n += 32;
|
||||
x >>= 32;
|
||||
}
|
||||
if ((x & 0x000000000000FFFFULL) == 0) {
|
||||
n += 16;
|
||||
x >>= 16;
|
||||
}
|
||||
if ((x & 0x00000000000000FFULL) == 0) {
|
||||
n += 8;
|
||||
x >>= 8;
|
||||
}
|
||||
if ((x & 0x000000000000000FULL) == 0) {
|
||||
n += 4;
|
||||
x >>= 4;
|
||||
}
|
||||
if ((x & 0x0000000000000003ULL) == 0) {
|
||||
n += 2;
|
||||
x >>= 2;
|
||||
}
|
||||
if ((x & 0x0000000000000001ULL) == 0) { n += 1; }
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline int vm_popcnt_opt(cell_t x) {
|
||||
int count = 0;
|
||||
while (x) {
|
||||
count += x & 1;
|
||||
x >>= 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ========== Cache Operations ========== */
|
||||
|
||||
#if USE_ASM_OPT && (ARCH_ARM64 || ARCH_RISCV64)
|
||||
#define vm_prefetch_opt(addr) vm_prefetch(addr)
|
||||
#define vm_prefetch_stream_opt(addr) vm_prefetch_stream(addr)
|
||||
#elif USE_ASM_OPT && ARCH_X86_64
|
||||
static inline void vm_prefetch_opt(const void *addr) {
|
||||
__builtin_prefetch(addr, 0, 3); /* Read, high temporal locality */
|
||||
}
|
||||
static inline void vm_prefetch_stream_opt(const void *addr) {
|
||||
__builtin_prefetch(addr, 0, 0); /* Read, no temporal locality */
|
||||
}
|
||||
#else
|
||||
#define vm_prefetch_opt(addr) ((void)0)
|
||||
#define vm_prefetch_stream_opt(addr) ((void)0)
|
||||
#endif
|
||||
|
||||
/* ========== Inner Interpreter (Direct Threading) ========== */
|
||||
|
||||
#if USE_DIRECT_THREADING && ARCH_RISCV64
|
||||
#define NEXT_OPT() NEXT_RV64()
|
||||
#define vm_setup_registers_opt(vm) vm_setup_registers_rv64(vm)
|
||||
#define vm_save_registers_opt(vm) vm_save_registers_rv64(vm)
|
||||
|
||||
#define PRIM_DUP_OPT() PRIM_DUP_RV64()
|
||||
#define PRIM_DROP_OPT() PRIM_DROP_RV64()
|
||||
#define PRIM_SWAP_OPT() PRIM_SWAP_RV64()
|
||||
#define PRIM_OVER_OPT() PRIM_OVER_RV64()
|
||||
#define PRIM_ROT_OPT() PRIM_ROT_RV64()
|
||||
#define PRIM_PLUS_OPT() PRIM_PLUS_RV64()
|
||||
#define PRIM_MINUS_OPT() PRIM_MINUS_RV64()
|
||||
#define PRIM_STAR_OPT() PRIM_STAR_RV64()
|
||||
#define PRIM_SLASH_OPT() PRIM_SLASH_RV64()
|
||||
#define PRIM_MOD_OPT() PRIM_MOD_RV64()
|
||||
#define PRIM_AND_OPT() PRIM_AND_RV64()
|
||||
#define PRIM_OR_OPT() PRIM_OR_RV64()
|
||||
#define PRIM_XOR_OPT() PRIM_XOR_RV64()
|
||||
#define PRIM_INVERT_OPT() PRIM_INVERT_RV64()
|
||||
#define PRIM_NEGATE_OPT() PRIM_NEGATE_RV64()
|
||||
#define PRIM_FETCH_OPT() PRIM_FETCH_RV64()
|
||||
#define PRIM_STORE_OPT() PRIM_STORE_RV64()
|
||||
#define PRIM_C_FETCH_OPT() PRIM_C_FETCH_RV64()
|
||||
#define PRIM_C_STORE_OPT() PRIM_C_STORE_RV64()
|
||||
#define PRIM_TO_R_OPT() PRIM_TO_R_RV64()
|
||||
#define PRIM_R_FROM_OPT() PRIM_R_FROM_RV64()
|
||||
#define PRIM_R_FETCH_OPT() PRIM_R_FETCH_RV64()
|
||||
#define PRIM_2DUP_OPT() PRIM_2DUP_RV64()
|
||||
#define PRIM_2DROP_OPT() PRIM_2DROP_RV64()
|
||||
#define PRIM_ZERO_EQUALS_OPT() PRIM_ZERO_EQUALS_RV64()
|
||||
#define PRIM_ZERO_LESS_OPT() PRIM_ZERO_LESS_RV64()
|
||||
#define PRIM_EQUALS_OPT() PRIM_EQUALS_RV64()
|
||||
#define PRIM_LESS_OPT() PRIM_LESS_RV64()
|
||||
#define PRIM_GREATER_OPT() PRIM_GREATER_RV64()
|
||||
#define PRIM_BRANCH_OPT() PRIM_BRANCH_RV64()
|
||||
#define PRIM_ZBRANCH_OPT() PRIM_ZBRANCH_RV64()
|
||||
#define PRIM_EXECUTE_OPT() PRIM_EXECUTE_RV64()
|
||||
|
||||
#elif USE_DIRECT_THREADING && ARCH_X86_64
|
||||
#define NEXT_OPT() NEXT_ASM()
|
||||
#define vm_setup_registers_opt(vm) vm_setup_registers(vm)
|
||||
#define vm_save_registers_opt(vm) vm_save_registers(vm)
|
||||
|
||||
/* Primitive word macros */
|
||||
#define PRIM_DUP_OPT() PRIM_DUP()
|
||||
#define PRIM_DROP_OPT() PRIM_DROP()
|
||||
#define PRIM_SWAP_OPT() PRIM_SWAP()
|
||||
#define PRIM_PLUS_OPT() PRIM_PLUS()
|
||||
#define PRIM_MINUS_OPT() PRIM_MINUS()
|
||||
#define PRIM_STAR_OPT() PRIM_STAR()
|
||||
#define PRIM_FETCH_OPT() PRIM_FETCH()
|
||||
#define PRIM_STORE_OPT() PRIM_STORE()
|
||||
#define PRIM_TO_R_OPT() PRIM_TO_R()
|
||||
#define PRIM_R_FROM_OPT() PRIM_R_FROM()
|
||||
#define PRIM_R_FETCH_OPT() PRIM_R_FETCH()
|
||||
#define PRIM_BRANCH_OPT() PRIM_BRANCH()
|
||||
#define PRIM_ZBRANCH_OPT() PRIM_ZBRANCH()
|
||||
|
||||
#elif USE_DIRECT_THREADING && ARCH_ARM64
|
||||
#define NEXT_OPT() NEXT_ARM64()
|
||||
#define vm_setup_registers_opt(vm) vm_setup_registers_arm64(vm)
|
||||
#define vm_save_registers_opt(vm) vm_save_registers_arm64(vm)
|
||||
|
||||
/* Primitive word macros */
|
||||
#define PRIM_DUP_OPT() PRIM_DUP_ARM64()
|
||||
#define PRIM_DROP_OPT() PRIM_DROP_ARM64()
|
||||
#define PRIM_SWAP_OPT() PRIM_SWAP_ARM64()
|
||||
#define PRIM_OVER_OPT() PRIM_OVER_ARM64()
|
||||
#define PRIM_ROT_OPT() PRIM_ROT_ARM64()
|
||||
#define PRIM_PLUS_OPT() PRIM_PLUS_ARM64()
|
||||
#define PRIM_MINUS_OPT() PRIM_MINUS_ARM64()
|
||||
#define PRIM_STAR_OPT() PRIM_STAR_ARM64()
|
||||
#define PRIM_SLASH_OPT() PRIM_SLASH_ARM64()
|
||||
#define PRIM_MOD_OPT() PRIM_MOD_ARM64()
|
||||
#define PRIM_AND_OPT() PRIM_AND_ARM64()
|
||||
#define PRIM_OR_OPT() PRIM_OR_ARM64()
|
||||
#define PRIM_XOR_OPT() PRIM_XOR_ARM64()
|
||||
#define PRIM_INVERT_OPT() PRIM_INVERT_ARM64()
|
||||
#define PRIM_NEGATE_OPT() PRIM_NEGATE_ARM64()
|
||||
#define PRIM_FETCH_OPT() PRIM_FETCH_ARM64()
|
||||
#define PRIM_STORE_OPT() PRIM_STORE_ARM64()
|
||||
#define PRIM_C_FETCH_OPT() PRIM_C_FETCH_ARM64()
|
||||
#define PRIM_C_STORE_OPT() PRIM_C_STORE_ARM64()
|
||||
#define PRIM_TO_R_OPT() PRIM_TO_R_ARM64()
|
||||
#define PRIM_R_FROM_OPT() PRIM_R_FROM_ARM64()
|
||||
#define PRIM_R_FETCH_OPT() PRIM_R_FETCH_ARM64()
|
||||
#define PRIM_2DUP_OPT() PRIM_2DUP_ARM64()
|
||||
#define PRIM_2DROP_OPT() PRIM_2DROP_ARM64()
|
||||
#define PRIM_ZERO_EQUALS_OPT() PRIM_ZERO_EQUALS_ARM64()
|
||||
#define PRIM_ZERO_LESS_OPT() PRIM_ZERO_LESS_ARM64()
|
||||
#define PRIM_EQUALS_OPT() PRIM_EQUALS_ARM64()
|
||||
#define PRIM_LESS_OPT() PRIM_LESS_ARM64()
|
||||
#define PRIM_GREATER_OPT() PRIM_GREATER_ARM64()
|
||||
#define PRIM_BRANCH_OPT() PRIM_BRANCH_ARM64()
|
||||
#define PRIM_ZBRANCH_OPT() PRIM_ZBRANCH_ARM64()
|
||||
#define PRIM_EXECUTE_OPT() PRIM_EXECUTE_ARM64()
|
||||
|
||||
#else
|
||||
/* No direct threading - use standard C function calls */
|
||||
#define NEXT_OPT() do {} while(0)
|
||||
#define vm_setup_registers_opt(vm) do {} while(0)
|
||||
#define vm_save_registers_opt(vm) do {} while(0)
|
||||
|
||||
/* Primitives fall back to function calls */
|
||||
#define PRIM_DUP_OPT() do {} while(0)
|
||||
#define PRIM_DROP_OPT() do {} while(0)
|
||||
/* ... etc ... */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup runtime_detect Runtime Architecture Detection
|
||||
* @{
|
||||
*
|
||||
* @brief Runtime detection of CPU features
|
||||
*
|
||||
* Functions for detecting available CPU features at runtime, such as SIMD
|
||||
* support (SSE4.2, AVX, NEON) and other architecture-specific capabilities.
|
||||
* @}
|
||||
*/
|
||||
|
||||
#if ARCH_X86_64
|
||||
/* Check for SSE4.2, AVX, etc. */
|
||||
static inline int vm_has_sse42_opt(void) {
|
||||
|
||||
|
||||
#if USE_ASM_OPT
|
||||
return vm_has_sse42();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#elif ARCH_ARM64
|
||||
/* Check for NEON, CRC32, etc. */
|
||||
static inline int vm_has_neon_opt(void) {
|
||||
|
||||
|
||||
#if USE_ASM_OPT
|
||||
return vm_has_neon();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
static inline int vm_has_sse42_opt(void) { return 0; }
|
||||
static inline int vm_has_neon_opt(void) { return 0; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup build_info Build Information
|
||||
* @{
|
||||
*
|
||||
* @brief Functions for querying build configuration
|
||||
*
|
||||
* Provides information about the build configuration, including target
|
||||
* architecture, enabled optimizations, and threading model.
|
||||
* @}
|
||||
*/
|
||||
|
||||
static inline const char *vm_get_arch_name(void) {
|
||||
return ARCH_NAME;
|
||||
}
|
||||
|
||||
static inline int vm_has_asm_opt(void) {
|
||||
#if USE_ASM_OPT
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int vm_has_direct_threading(void) {
|
||||
#if USE_DIRECT_THREADING
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline const char *vm_get_build_info(void) {
|
||||
static char info[256];
|
||||
snprintf(info, sizeof(info),
|
||||
"Architecture: %s, ASM: %s, Direct Threading: %s",
|
||||
vm_get_arch_name(),
|
||||
vm_has_asm_opt() ? "enabled" : "disabled",
|
||||
vm_has_direct_threading() ? "enabled" : "disabled"
|
||||
);
|
||||
return info;
|
||||
}
|
||||
|
||||
#endif /* ARCH_DETECT_H */
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
*** StarForth ***
|
||||
Block Subsystem Configuration API
|
||||
----------------------------------
|
||||
Purpose:
|
||||
- Provide a common, reusable configuration interface for selecting
|
||||
a "block subsystem" (FILE image vs RAM) and its geometry WITHOUT
|
||||
touching any real I/O or backend code.
|
||||
|
||||
Notes:
|
||||
- No file system calls; no mmap; no blkio includes.
|
||||
- Geometries are computed deterministically from inputs only.
|
||||
- FILE backend: we record the path and intent; size/blocks remain 0
|
||||
until a real backend attaches later.
|
||||
- RAM backend: ram_mb -> size_bytes -> blocks (floor to FBS multiple).
|
||||
|
||||
Typical flow:
|
||||
- Parse CLI flags (--disk-img=<path> | --ram-disk=<MB>).
|
||||
- Call blkcfg_init_from_options().
|
||||
- Use g_blkcfg elsewhere to make decisions (later you'll attach drivers).
|
||||
|
||||
License: See LICENSE file. No warranty.
|
||||
|
||||
*/
|
||||
#ifndef STARFORTH_BLKCFG_H
|
||||
#define STARFORTH_BLKCFG_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef BLKCFG_DEFAULT_FBS
|
||||
#define BLKCFG_DEFAULT_FBS 1024u /* default Forth Block Size in bytes */
|
||||
#endif
|
||||
|
||||
#ifndef BLKCFG_PATH_MAX
|
||||
#define BLKCFG_PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
BLKCFG_BACKEND_NONE = 0,
|
||||
BLKCFG_BACKEND_FILE = 1,
|
||||
BLKCFG_BACKEND_RAM = 2
|
||||
} blkcfg_backend_t;
|
||||
|
||||
typedef struct {
|
||||
blkcfg_backend_t backend; /* FILE or RAM (or NONE) */
|
||||
uint32_t fbs; /* Forth Block Size (bytes) */
|
||||
uint8_t read_only; /* 1 = intended RO, else 0 */
|
||||
|
||||
/* Geometry (no I/O performed to derive these) */
|
||||
uint64_t size_bytes; /* For RAM: computed; FILE: 0 */
|
||||
uint32_t blocks; /* For RAM: computed; FILE: 0 */
|
||||
|
||||
/* FILE plan (no I/O): path is recorded only */
|
||||
char file_path[BLKCFG_PATH_MAX];
|
||||
} blkcfg_t;
|
||||
|
||||
/* Global config (optional convenience) */
|
||||
extern blkcfg_t g_blkcfg;
|
||||
|
||||
/**
|
||||
* Initialize a block configuration from CLI-style options.
|
||||
*
|
||||
* Inputs:
|
||||
* - disk_img_path: if non-NULL and non-empty => choose FILE backend
|
||||
* - ram_mb: megabytes for RAM fallback (only used if no disk image)
|
||||
* - fbs: forth block size (bytes). If 0 => BLKCFG_DEFAULT_FBS
|
||||
* - read_only: intended read-only flag (advisory)
|
||||
*
|
||||
* Behavior:
|
||||
* - FILE: record path, set blocks/size_bytes = 0 (unknown until attached)
|
||||
* - RAM : compute size_bytes = ram_mb*MiB; blocks = size_bytes/fbs (floored)
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success; -1 on invalid args.
|
||||
*/
|
||||
int blkcfg_init_from_options(blkcfg_t *cfg,
|
||||
const char *disk_img_path,
|
||||
uint64_t ram_mb,
|
||||
uint32_t fbs,
|
||||
uint8_t read_only);
|
||||
|
||||
/**
|
||||
* Format a single-line summary into 'dst' (capacity 'dst_cap' bytes).
|
||||
* Example:
|
||||
* "backend=FILE path=./disks/os.img fbs=1024 ro=0"
|
||||
* "backend=RAM size=1024MB fbs=1024 blocks=1048576 ro=0"
|
||||
*
|
||||
* Returns number of bytes that would have been written (like snprintf).
|
||||
*/
|
||||
int blkcfg_format_summary(const blkcfg_t *cfg, char *dst, size_t dst_cap);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* STARFORTH_BLKCFG_H */
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
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 STARFORTH_BLKIO_H
|
||||
#define STARFORTH_BLKIO_H
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
StarForth - Block I/O Core API
|
||||
- C99, deterministic core (no hidden malloc)
|
||||
- Public types / errors / thin inline wrapper
|
||||
- Backends provide a vtable implementing: open, close, read, write, flush, info
|
||||
|
||||
License: See LICENSE file. No warranty.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef BLKIO_FORTH_BLOCK_SIZE
|
||||
#define BLKIO_FORTH_BLOCK_SIZE 1024u
|
||||
#endif
|
||||
|
||||
/* Error codes (negative = error) */
|
||||
enum {
|
||||
BLKIO_OK = 0,
|
||||
BLKIO_EINVAL = -1,
|
||||
BLKIO_ENOSUP = -2,
|
||||
BLKIO_EIO = -3,
|
||||
BLKIO_ENOSPACE = -4,
|
||||
BLKIO_ECLOSED = -5
|
||||
};
|
||||
|
||||
/* Forward decl */
|
||||
struct blkio_dev;
|
||||
|
||||
/* Open parameters (generic) */
|
||||
typedef struct {
|
||||
uint32_t forth_block_size; /* 0 => BLKIO_FORTH_BLOCK_SIZE */
|
||||
uint32_t total_blocks; /* number of Forth blocks; FILE may derive when 0 */
|
||||
void *opaque; /* backend-specific */
|
||||
} blkio_params_t;
|
||||
|
||||
/* Device info */
|
||||
typedef struct {
|
||||
uint32_t forth_block_size;
|
||||
uint32_t total_blocks;
|
||||
uint32_t phys_sector_size; /* best-effort; 0 if N/A */
|
||||
uint64_t phys_size_bytes; /* best-effort; 0 if N/A */
|
||||
uint8_t read_only;
|
||||
} blkio_info_t;
|
||||
|
||||
/* Backend vtable */
|
||||
typedef struct {
|
||||
int (*open)(struct blkio_dev *dev, const blkio_params_t *p);
|
||||
|
||||
int (*close)(struct blkio_dev *dev);
|
||||
|
||||
int (*read)(struct blkio_dev *dev, uint32_t fblock, void *dst);
|
||||
|
||||
int (*write)(struct blkio_dev *dev, uint32_t fblock, const void *src);
|
||||
|
||||
int (*flush)(struct blkio_dev *dev);
|
||||
|
||||
int (*info)(struct blkio_dev *dev, blkio_info_t *out);
|
||||
} blkio_vtable_t;
|
||||
|
||||
/* Device (public) */
|
||||
typedef struct blkio_dev {
|
||||
const blkio_vtable_t *vt;
|
||||
uint32_t forth_block_size;
|
||||
uint32_t total_blocks;
|
||||
void *state; /* backend private */
|
||||
uint8_t is_open;
|
||||
} blkio_dev_t;
|
||||
|
||||
/* Core wrappers */
|
||||
static inline int blkio_open(blkio_dev_t *dev, const blkio_vtable_t *vt, const blkio_params_t *p) {
|
||||
if (!dev || !vt || !vt->open || !vt->close || !vt->read || !vt->write || !vt->flush || !vt->info)
|
||||
return BLKIO_EINVAL;
|
||||
dev->vt = vt;
|
||||
dev->is_open = 0u;
|
||||
dev->state = 0;
|
||||
dev->forth_block_size = (p && p->forth_block_size) ? p->forth_block_size : BLKIO_FORTH_BLOCK_SIZE;
|
||||
if (dev->forth_block_size == 0) return BLKIO_EINVAL;
|
||||
dev->total_blocks = p ? p->total_blocks : 0u;
|
||||
const int rc = vt->open(dev, p);
|
||||
if (rc == BLKIO_OK) dev->is_open = 1u;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline int blkio_close(blkio_dev_t *dev) {
|
||||
if (!dev || !dev->is_open) return BLKIO_ECLOSED;
|
||||
const int rc = dev->vt->close(dev);
|
||||
dev->is_open = 0u;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline int blkio_read(blkio_dev_t *dev, uint32_t fblock, void *dst) {
|
||||
if (!dev || !dev->is_open || !dst) return BLKIO_EINVAL;
|
||||
if (fblock >= dev->total_blocks) return BLKIO_EINVAL;
|
||||
return dev->vt->read(dev, fblock, dst);
|
||||
}
|
||||
|
||||
static inline int blkio_write(blkio_dev_t *dev, uint32_t fblock, const void *src) {
|
||||
if (!dev || !dev->is_open || !src) return BLKIO_EINVAL;
|
||||
if (fblock >= dev->total_blocks) return BLKIO_EINVAL;
|
||||
return dev->vt->write(dev, fblock, src);
|
||||
}
|
||||
|
||||
static inline int blkio_flush(blkio_dev_t *dev) {
|
||||
if (!dev || !dev->is_open) return BLKIO_ECLOSED;
|
||||
return dev->vt->flush(dev);
|
||||
}
|
||||
|
||||
static inline int blkio_info(blkio_dev_t *dev, blkio_info_t *out) {
|
||||
if (!dev || !dev->is_open || !out) return BLKIO_EINVAL;
|
||||
const int rc = dev->vt->info(dev, out);
|
||||
if (rc == BLKIO_OK) {
|
||||
if (!out->forth_block_size) out->forth_block_size = dev->forth_block_size;
|
||||
if (!out->total_blocks) out->total_blocks = dev->total_blocks;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
static inline void blkio_bzero(void *p, uint32_t fbs) {
|
||||
uint8_t *q = (uint8_t *) p;
|
||||
for (uint32_t i = 0; i < fbs; i++) q[i] = 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* STARFORTH_BLKIO_H */
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
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 STARFORTH_BLKIO_FACTORY_H
|
||||
#define STARFORTH_BLKIO_FACTORY_H
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
StarForth - Block I/O Factory (Public API)
|
||||
Chooses FILE backend when a disk image path is provided; otherwise RAM.
|
||||
No dynamic allocation; caller supplies backend state memory.
|
||||
|
||||
NOTE: This header avoids referencing blkio typedefs to prevent "unknown type"
|
||||
errors when include order or local headers differ. We forward-declare only the
|
||||
device struct here. The implementation includes "blkio.h".
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Forward declaration; real definition is in blkio.h */
|
||||
struct blkio_dev;
|
||||
|
||||
/**
|
||||
* Open a block device using either a file-backed image (if disk_img_path is
|
||||
* non-empty) or a RAM-backed buffer (fallback).
|
||||
*
|
||||
* Parameters:
|
||||
* - dev : output device object (caller-allocated)
|
||||
* - disk_img_path : path to raw image; if NULL/empty => RAM fallback
|
||||
* - read_only : 1 = open read-only; 0 = read/write
|
||||
* - total_blocks : FILE: 0 => derive from file size; >0 => size hint
|
||||
* RAM : must match ram_blocks
|
||||
* - fbs : forth block size in bytes (0 => default per blkio.h)
|
||||
* - state_mem : caller-provided backend state buffer (opaque to caller)
|
||||
* - state_len : size of state_mem in bytes
|
||||
* - ram_base : RAM storage base pointer (RAM backend only)
|
||||
* - ram_blocks : number of Forth blocks in ram_base (RAM backend only)
|
||||
* - out_used_file : optional; set to 1 if FILE backend selected, else 0
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success; negative blkio error code on failure.
|
||||
*/
|
||||
int blkio_factory_open(struct blkio_dev *dev,
|
||||
const char *disk_img_path,
|
||||
uint8_t read_only,
|
||||
uint32_t total_blocks,
|
||||
uint32_t fbs,
|
||||
void *state_mem,
|
||||
size_t state_len,
|
||||
uint8_t *ram_base,
|
||||
uint32_t ram_blocks,
|
||||
uint8_t *out_used_file);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* STARFORTH_BLKIO_FACTORY_H */
|
||||
@@ -0,0 +1,597 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
*** StarForth ***
|
||||
Block Subsystem - Layer 2: Mapping & Business Logic (v2)
|
||||
---------------------------------------------------------
|
||||
Architecture (unified block address space):
|
||||
- LBN 0..2047: FAST RAM (volatile, g.ram_base)
|
||||
- LBN 2048..x: RAMDRIVE (raw RAM buffer, volatile; first attached device)
|
||||
- LBN x..y: DISK IMG (virtio-blk, persistent; second attached device)
|
||||
- LBN y+: USB / future devices (chained)
|
||||
|
||||
Each non-RAM device is attached via blk_subsys_attach_device() (formatted disk)
|
||||
or blk_subsys_add_raw_device() (volatile RAM buffer). Devices are appended to
|
||||
a chain; each gets a contiguous slot in the unified logical BAM (g.bam[]).
|
||||
Physical BAMs (on-disk) are synced to/from their logical BAM slot at attach/flush.
|
||||
|
||||
blkio NOTE:
|
||||
- blkio backends operate on 1 KiB units.
|
||||
- One 4 KiB “devblock” == 4 consecutive 1 KiB blkio blocks.
|
||||
|
||||
License: See LICENSE file. No warranty.
|
||||
*/
|
||||
#ifndef STARFORTH_BLOCK_SUBSYSTEM_H
|
||||
#define STARFORTH_BLOCK_SUBSYSTEM_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "vm.h"
|
||||
#include "blkio.h" /* ensure struct blkio_dev is fully visible */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Core configuration constants */
|
||||
#define BLK_FORTH_SIZE 1024u /* Forth block size */
|
||||
#define BLK_RAM_BLOCKS 2080u /* Physical RAM blocks (user-visible LBN 0..2047) */
|
||||
#define BLK_DISK_START 2080u /* Device-backed Forth blocks start at LBN 2080 */
|
||||
#define BLK_DEVICE_SECTOR 4096u /* Physical “devblock” size (4×1 KiB blkio units) */
|
||||
#define BLK_PACK_RATIO 3u /* 3× 1 KiB data per 4 KiB devblock (plus 1 KiB metadata) */
|
||||
#define BLK_META_TOTAL 1024u /* Last 1 KiB in a 4 KiB devblock is metadata */
|
||||
#define BLK_META_PER_BLOCK 341u /* 341×3 ~= 1023, padded to 1024 */
|
||||
|
||||
/* Forth-friendly reserved ranges */
|
||||
#ifndef BLK_FORTH_SYS_RESERVED
|
||||
#define BLK_FORTH_SYS_RESERVED 32u /* RAM physical blocks 0..31 hidden; user LBN 0 = PBN 32 */
|
||||
#endif
|
||||
#ifndef BLK_DISK_SYS_RESERVED
|
||||
#define BLK_DISK_SYS_RESERVED 32u /* First 32 blocks of each disk reserved (byte-aligned BAM offset) */
|
||||
#endif
|
||||
#ifndef BLK_META_FENCE_INIT
|
||||
#define BLK_META_FENCE_INIT 128u /* Starting size (4 KiB devblocks -- 512 KiB) of the
|
||||
* top-of-device system-metadata fence, grows downward
|
||||
* from here. */
|
||||
#endif
|
||||
|
||||
/* =========================
|
||||
* On-disk volume header v2
|
||||
* =========================
|
||||
* Serialized into device devblock 0 (4 KiB = 4×1 KiB blkio blocks 0..3).
|
||||
* BAM lives in external devblocks [bam_start .. bam_start+bam_devblocks-1] (each 4 KiB).
|
||||
* All *_devblocks indices are in 4 KiB units.
|
||||
*/
|
||||
typedef struct {
|
||||
/* Identification & versioning */
|
||||
uint32_t magic; /* 0x53544652 "STFR" */
|
||||
uint32_t version; /* 2 */
|
||||
|
||||
/* Administrative info */
|
||||
uint32_t total_volumes;
|
||||
uint32_t flags;
|
||||
char label[64];
|
||||
|
||||
/* Physical device geometry (4 KiB devblocks) */
|
||||
uint64_t total_devblocks; /* count of 4 KiB devblocks (blkio_info.total_blocks / 4) */
|
||||
|
||||
/* BAM placement (external 1-bit bitmap region, stored in 4 KiB pages) */
|
||||
uint32_t bam_start; /* usually 1 */
|
||||
uint32_t bam_devblocks; /* number of 4 KiB pages used by BAM */
|
||||
uint32_t devblock_base; /* first payload devblock = reloc_start + reloc_devblocks */
|
||||
|
||||
/* Capacity modeling (Forth 1 KiB blocks tracked/usable) */
|
||||
uint64_t tracked_blocks; /* 32768 * bam_devblocks (bits per 4 KiB page) */
|
||||
uint64_t total_blocks; /* min(tracked, 3 * (total_devblocks - 1 - bam_devblocks)) */
|
||||
uint64_t free_blocks;
|
||||
|
||||
/* Allocation hints */
|
||||
uint64_t first_free; /* next free Forth block (>= 1024) */
|
||||
uint64_t last_allocated;
|
||||
|
||||
/* Reserved low ranges */
|
||||
uint32_t reserved_disk_lo; /* e.g., 32 blocks reserved at 1024.. */
|
||||
uint32_t reserved_ram_lo; /* e.g., 33 blocks reserved at 0..32 */
|
||||
|
||||
/* Timestamps (optional) */
|
||||
uint64_t created_time;
|
||||
uint64_t mounted_time;
|
||||
|
||||
/* Optional integrity (unused yet) */
|
||||
uint64_t hdr_crc;
|
||||
|
||||
/* Relocation-exception table placement (Milestone 2h+ single-block relocation).
|
||||
* Appended after hdr_crc, carved out of what was previously _pad[] -- appending
|
||||
* (not inserting) preserves every earlier field's byte offset, so a pre-existing
|
||||
* formatted volume's zeroed padding reads back here as reloc_devblocks=0 ("no
|
||||
* reloc capacity"), gracefully, not a format-breaking change. See
|
||||
* block_subsystem.c's reloc_flush_to_disk()/reloc_load_from_disk(). */
|
||||
uint32_t reloc_start; /* usually bam_start + bam_devblocks */
|
||||
uint32_t reloc_devblocks; /* number of 4 KiB pages used by the reloc table (0 = none) */
|
||||
|
||||
/* System-metadata fence (Phase 8, 2026-08-26): a reserved zone of
|
||||
* whole, RAW (unpacked -- same shape as the header/BAM/reloc-table
|
||||
* devblocks, not the 3-Forth-blocks-per-devblock user payload
|
||||
* packing) devblocks at the TOP of the device's payload region,
|
||||
* opposite end from reserved_disk_lo's bottom BAM reservation,
|
||||
* growing DOWNWARD as system metadata (starting with Zuse's cert)
|
||||
* needs more room. Never RAM-backed -- this field only exists on
|
||||
* real disk-backed slots. Units are 4 KiB devblocks, same as
|
||||
* bam_devblocks/reloc_devblocks (NOT Forth 1 KiB blocks -- corrected
|
||||
* 2026-08-26, before anything depended on the original wrong unit,
|
||||
* so this reads as a clean field, not a migration). Folded directly
|
||||
* into compute_totals_from_B()'s payload4k calculation, so
|
||||
* total_blocks/user_blocks/free_blocks all shrink correctly with no
|
||||
* separate subtraction needed elsewhere. Appended after
|
||||
* reloc_devblocks, carved out of what was previously _pad[] -- same
|
||||
* graceful-default technique as reloc_devblocks itself: a
|
||||
* pre-existing formatted volume's zeroed padding reads back here as
|
||||
* meta_fence_blocks=0 ("no fence yet"), not a format-breaking
|
||||
* change. See FABRIC-2.md's Phase 8 §C writeup for the full design. */
|
||||
uint32_t meta_fence_blocks; /* current fence size, in 4 KiB devblocks (0 = none yet) */
|
||||
|
||||
/* Padding to keep header ≤ 4096 bytes */
|
||||
uint8_t _pad[4096 - (
|
||||
4 + 4 + /* magic, version */
|
||||
4 + 4 + 64 + /* total_volumes, flags, label */
|
||||
4 + /* compiler alignment gap before total_devblocks'
|
||||
* uint64_t sibling tracked_blocks -- verified via
|
||||
* offsetof(), not hand-derived (2026-08-26: this
|
||||
* exact formula was off by 4 bytes from trusting
|
||||
* arithmetic alone before this fix) */
|
||||
8 + /* total_devblocks */
|
||||
4 + 4 + 4 + /* bam_start, bam_devblocks, devblock_base */
|
||||
8 + 8 + 8 + /* tracked_blocks, total_blocks, free_blocks */
|
||||
8 + 8 + /* first_free, last_allocated */
|
||||
4 + 4 + /* reserved ranges */
|
||||
8 + 8 + /* timestamps */
|
||||
8 + /* hdr_crc */
|
||||
4 + 4 + /* reloc_start, reloc_devblocks */
|
||||
4 /* meta_fence_blocks */
|
||||
)];
|
||||
} blk_volume_meta_t;
|
||||
|
||||
/* Same discipline homeblocks_sig.h's own header-size check uses: the pad
|
||||
* math above is meant to keep this struct at exactly one 4 KiB devblock,
|
||||
* verified at compile time rather than trusted by inspection -- adding
|
||||
* meta_fence_blocks is exactly the kind of edit that could silently
|
||||
* throw this off by a few bytes. */
|
||||
_Static_assert(sizeof(blk_volume_meta_t) == 4096,
|
||||
"blk_volume_meta_t must be exactly one 4 KiB devblock");
|
||||
|
||||
/* blk_meta_t.flags bit values -- FABRIC-2.md §F.4/§H.6/§H.12 step 13,
|
||||
* decided 2026-09-02/03. Orthogonal bits, not a mutually-exclusive enum:
|
||||
* a block can be both CLAIMED and MIGRATING at once. Grounded in the only
|
||||
* states §F.4 actually motivated by a real need (MIGSM/UNCLEAN, two
|
||||
* then-currently-blocked graph nodes) plus CLAIMED/STALE, the names
|
||||
* already used loosely in that pass's own prose. 61 bits remain reserved. */
|
||||
#define BLK_FLAG_CLAIMED (1ull << 0) /* owned, per BMAPFMT's owner_fp */
|
||||
#define BLK_FLAG_MIGRATING (1ull << 1) /* mid-migration; serves MIGSM */
|
||||
#define BLK_FLAG_STALE (1ull << 2) /* interrupted flush; serves UNCLEAN */
|
||||
|
||||
/* Per-1 KiB block metadata (packed into top 1 KiB region of each 4 KiB sector). */
|
||||
typedef struct {
|
||||
/* Core integrity (16 bytes) */
|
||||
uint64_t checksum; /* CRC64 of block data */
|
||||
uint64_t magic; /* 0x424C4B5F5354524BULL "BLK_STRK" */
|
||||
|
||||
/* Timestamps (16 bytes) */
|
||||
uint64_t created_time; /* Unix timestamp (creation) */
|
||||
uint64_t modified_time; /* Unix timestamp (last write) */
|
||||
|
||||
/* Block status (16 bytes) */
|
||||
uint64_t flags; /* Status flags -- BLK_FLAG_* bits above */
|
||||
uint64_t write_count; /* Number of writes (wear leveling) */
|
||||
|
||||
/* Content identification (32 bytes) */
|
||||
uint64_t content_type; /* 0=empty, 1=source, 2=data, ... */
|
||||
uint64_t encoding; /* 0=ASCII, 1=UTF-8, 2=binary, ... */
|
||||
uint64_t content_length; /* Actual data length (≤ 1024) */
|
||||
uint64_t reserved1; /* Alignment/future use */
|
||||
|
||||
/* Cryptographic (64 bytes) */
|
||||
uint64_t entropy[4]; /* 256-bit entropy/random seed */
|
||||
uint64_t hash[4]; /* SHA-256 (optional) */
|
||||
|
||||
/* Security & ownership -- FABRIC-2.md §F.4/§H.6/§H.12 step 12, decided
|
||||
* 2026-08-27/2026-09-03: BMAPFMT repurposes this slot rather than
|
||||
* building a separate on-drive block-map table (distributed
|
||||
* ownership/ACL, travels with the block itself). Replaces the old
|
||||
* owner_id/permissions/acl_block/signature[2] fields, which predated
|
||||
* and directly conflicted with both the anti-POSIX principle and
|
||||
* VMIdentity's pubkey-based model. Not the same 40-byte budget the
|
||||
* old fields occupied -- natural alignment padding (uint32_t acl_ttl
|
||||
* and uint64_t reserved_future each force a few bytes of compiler-
|
||||
* inserted padding after the preceding uint8_t fields) makes this
|
||||
* section's real footprint smaller; verified below via
|
||||
* _Static_assert on the whole struct's actual sizeof(), not trusted
|
||||
* by hand (see the blk_volume_meta_t padding-bug lesson this project
|
||||
* already learned once). */
|
||||
uint8_t owner_fp[8]; /* truncated fingerprint of owner's VMIdentity
|
||||
* pubkey -- cheap per-block; full pubkey
|
||||
* resolves via the drive's own identity
|
||||
* record. */
|
||||
uint8_t acl_allow; /* cached fast-deny bit, checked first --
|
||||
* vm.c:611-624's exact pattern, applied to a
|
||||
* block instead of a word. */
|
||||
uint32_t acl_ttl; /* countdown, same shape as DictEntry's
|
||||
* acl_ttl -- blocks support temporary
|
||||
* elevation too, same ACL-TTL-reuse
|
||||
* mechanism and Zuse-eligibility-list gating
|
||||
* as the word card (§H.5). */
|
||||
uint8_t acl_reserved[3]; /* still genuinely undecided -- deliberate
|
||||
* slack per "flexibility until we understand
|
||||
* the recipe," not a placeholder to fill
|
||||
* reflexively. */
|
||||
uint64_t reserved_future; /* untouched budget, same reasoning. */
|
||||
|
||||
/* Link/chain support (32 bytes) */
|
||||
uint64_t prev_block; /* Previous in chain (0=none) */
|
||||
uint64_t next_block; /* Next in chain (0=none) */
|
||||
uint64_t parent_block; /* Parent/index (0=none) */
|
||||
uint64_t chain_length; /* Total blocks in chain */
|
||||
|
||||
/* Application-specific (120 bytes) */
|
||||
uint64_t app_data[15]; /* 15×64-bit app-defined fields */
|
||||
|
||||
uint8_t padding[5]; /* trailing slack, unrelated to any exact size target --
|
||||
* the old "341-byte slice" comment here was already
|
||||
* inaccurate before FABRIC-2.md §H.12 step 12's edit
|
||||
* (sizeof(blk_meta_t) was 344, not 341, due to
|
||||
* ordinary trailing struct-alignment padding after
|
||||
* this array -- harmless since BLK_META_PER_BLOCK,
|
||||
* the only thing that constant would matter to, has
|
||||
* zero callers anywhere in this codebase). Verify
|
||||
* this struct's real size with the _Static_assert
|
||||
* below, not by re-deriving it from this comment. */
|
||||
} blk_meta_t;
|
||||
|
||||
/* Verified via offsetof()/sizeof(), not trusted by hand -- see the
|
||||
* blk_volume_meta_t padding-bug lesson this project already learned once
|
||||
* (a hand-summed struct padding formula hid a real 4-byte alignment gap).
|
||||
* 336, not the BLK_META_PER_BLOCK/"341-byte slice" figure this struct's
|
||||
* own comments have long claimed -- that mismatch predates this assert and
|
||||
* is harmless today (see padding[5]'s own comment above), but this assert
|
||||
* now makes any future drift in either direction fail the build instead of
|
||||
* silently mismatching a constant nothing currently checks against it. */
|
||||
_Static_assert(sizeof(blk_meta_t) == 336,
|
||||
"blk_meta_t size changed -- update this assert and check BLK_META_PER_BLOCK");
|
||||
|
||||
/* Error codes */
|
||||
enum {
|
||||
BLK_OK = 0,
|
||||
BLK_EINVAL = -1,
|
||||
BLK_ERANGE = -2,
|
||||
BLK_EIO = -3,
|
||||
BLK_ENODEV = -4,
|
||||
BLK_ERESERVED = -5,
|
||||
BLK_EDIRTY = -6,
|
||||
BLK_ENOMEM = -7,
|
||||
BLK_ENOSPC = -8 /* not enough free devblocks -- blk_firsttouch_claim() */
|
||||
};
|
||||
|
||||
/* Per-block BAM entry — one per user block in each device slot */
|
||||
typedef struct {
|
||||
uint8_t allocated; /* 0=free, 1=in use */
|
||||
uint8_t dirty; /* 1=UPDATE called; content needs flush */
|
||||
} blk_bam_entry_t;
|
||||
|
||||
/* Low-level disk container format state. This is distinct from — and known
|
||||
* nothing about — any higher-level content classification (e.g. Artemis's
|
||||
* own BLANK/LithosAnanke/Unrecognized marker check). A slot stays
|
||||
* PROVISIONAL, and all writes to it are refused, until its owner explicitly
|
||||
* calls blk_subsys_confirm_format() after deciding the disk is safe to
|
||||
* touch. This is what makes "halt, disk preserved" actually true instead
|
||||
* of being overwritten by this layer's own opportunistic reformat before
|
||||
* the owner ever gets a look. */
|
||||
enum { BLK_FMT_FORMATTED = 0, BLK_FMT_PROVISIONAL = 1 };
|
||||
|
||||
/* ===== Public API ===== */
|
||||
int blk_subsys_init(VM *vm, uint8_t *ram_base, size_t ram_size);
|
||||
|
||||
int blk_subsys_attach_device(struct blkio_dev *dev);
|
||||
|
||||
/* Milestone 2h hot-detach. Refuses (BLK_EINVAL) unless dev's slot is the
|
||||
* current chain tail -- see this function's own doc comment in
|
||||
* block_subsystem.c for why. Discards any dirty cache/BAM/vol_meta state
|
||||
* rather than attempting to flush it (the device is already physically
|
||||
* gone by the time this is called). Returns BLK_ENODEV if dev isn't
|
||||
* attached, BLK_EINVAL if dev is NULL or not the chain tail.
|
||||
*/
|
||||
int blk_subsys_detach_device(struct blkio_dev *dev);
|
||||
|
||||
/* Monotonic counter, bumped on every attach/detach (Milestone 2h). A raw
|
||||
* pointer comparison against a blk_get_buffer() result cannot reliably
|
||||
* detect a same-address device swap (this kernel's own first-fit kmalloc,
|
||||
* src/starkernel/memory/kmalloc.c, can hand back the exact address just
|
||||
* free()'d by a detach to the very next attach's calloc() -- confirmed
|
||||
* live) -- callers that cache a blk_get_buffer() result across calls
|
||||
* (block_words.c's VM block window) must instead compare this epoch
|
||||
* against the value they last observed, invalidating their whole cache on
|
||||
* any change rather than trusting a stored pointer's identity.
|
||||
*/
|
||||
uint64_t blk_subsys_epoch(void);
|
||||
|
||||
/* Single-block relocation. Copies home_lbn's current content to target_lbn
|
||||
* (both must already be valid LBNs -- target_lbn is expected to be a block
|
||||
* the caller's own identity already owns, on whichever device it's being
|
||||
* relocated to; this function does not itself validate ownership, that's
|
||||
* policy, left to the caller -- see this session's ACL-owns-policy
|
||||
* direction in FABRIC-1.md), frees home_lbn's original BAM entry, records
|
||||
* an LBN->LBN redirect so every future access to home_lbn transparently
|
||||
* resolves to target_lbn instead, and bumps blk_subsys_epoch() so any VM's
|
||||
* cached block window correctly invalidates. Persisted immediately to the
|
||||
* relocation-owner device's on-disk table (see block_subsystem.c's
|
||||
* reloc_flush_to_disk()) if one exists.
|
||||
*
|
||||
* Returns BLK_OK on success.
|
||||
* Returns BLK_EINVAL if home_lbn == target_lbn, or home_lbn is already
|
||||
* relocated (call again with a different target to re-relocate --
|
||||
* not supported by simply calling this twice on the same home_lbn).
|
||||
* Returns BLK_ERANGE if either LBN doesn't resolve to a valid device.
|
||||
* Returns BLK_EIO if the content copy fails (e.g. target device refuses
|
||||
* the write -- a read-only backend like blkio_usb.c today).
|
||||
* Returns BLK_ENOMEM if the in-memory relocation table is full.
|
||||
*/
|
||||
int blk_subsys_relocate_block(uint32_t home_lbn, uint32_t target_lbn);
|
||||
|
||||
/* blk_get_device_range - the LBN range (start_lbn, count of 1 KiB FORTH
|
||||
* blocks) a specific already-attached device occupies (FABRIC-2.md §I.2,
|
||||
* 2026-09-04). Mirrors blk_subsys_detach_device()'s own dev-pointer slot
|
||||
* lookup, exposed publicly for the first time -- needed so a caller can
|
||||
* scope a scan/query to exactly one attached device rather than the
|
||||
* global, multi-device LBN space blk_get_total_blocks()/blk_get_meta()
|
||||
* otherwise address. Returns BLK_ENODEV if dev isn't attached.
|
||||
*/
|
||||
int blk_get_device_range(struct blkio_dev *dev, uint32_t *out_start_lbn, uint32_t *out_count);
|
||||
|
||||
/* blk_lbn_device_handle - the missing direction from blk_get_device_range()
|
||||
* above (that goes device -> LBN range; this goes LBN -> device). Returns
|
||||
* an opaque, stable handle -- directly comparable with == to test "do
|
||||
* these two LBNs belong to the same physical device" -- or NULL if lbn
|
||||
* doesn't resolve to any attached device. Never dereference the returned
|
||||
* pointer; it exists only for equality comparison (Phase 8 v3, 2026-09-23:
|
||||
* added so MOVE/CMOVE/CMOVE> can refuse a same-VM, cross-device raw block
|
||||
* copy through the block-window mechanism -- see those words' own callers
|
||||
* in memory_words.c/string_words.c).
|
||||
*/
|
||||
const void *blk_lbn_device_handle(uint32_t lbn);
|
||||
|
||||
/* blk_get_device_free_blocks - free vs. total 1 KiB FORTH-block count for
|
||||
* a specific already-attached device (same slot lookup as
|
||||
* blk_get_device_range()). RAM-backed slots (no on-disk vol_meta) report
|
||||
* out_total == out_free == 0, matching blk_mark_allocated()/_free()'s own
|
||||
* "!slot->raw_base" gate on touching free_blocks bookkeeping at all.
|
||||
* Returns BLK_ENODEV if dev isn't attached.
|
||||
*/
|
||||
int blk_get_device_free_blocks(struct blkio_dev *dev, uint64_t *out_free, uint64_t *out_total);
|
||||
|
||||
/* blk_get_first_disk_range - the LBN range of first_disk_slot(), i.e.
|
||||
* whichever disk-backed device blk_get_volume_meta()/blk_meta_zone_read()
|
||||
* already implicitly target -- Artemis's own disk.img in practice, since
|
||||
* it attaches at boot before any USB hotplug (the same boot-order
|
||||
* convention those two functions already rely on, not a new assumption).
|
||||
* FIRSTTOUCH (below) scans exactly this range, not the global LBN space.
|
||||
* Returns BLK_ENODEV if no disk-backed device is attached at all.
|
||||
*/
|
||||
int blk_get_first_disk_range(uint32_t *out_start_lbn, uint32_t *out_count);
|
||||
|
||||
/* blk_firsttouch_claim - FABRIC-2.md §F.11, decisions 1-3, built exactly
|
||||
* as scoped 2026-08-27 (re-verified against current blk_meta_t 2026-09-04
|
||||
* before building -- the chain fields below are real and untouched by
|
||||
* BMAPFMT's unrelated owner_fp/acl_allow/acl_ttl/flags field addition).
|
||||
*
|
||||
* One linear scan over blk_get_first_disk_range()'s devblocks (4 KiB
|
||||
* granularity -- one blk_meta_t per devblock, not per 1 KiB FORTH block),
|
||||
* via blk_get_meta(). Answers both questions the scan needs in the same
|
||||
* pass: does owner_fp already own a claim (not reused by this call --
|
||||
* every call makes a *new* claim of exactly `count` additional devblocks,
|
||||
* per §F.11's own text; an existing claim is a separate, not-yet-decided
|
||||
* question), and which devblocks are free (owner_fp all-zero, BLK_FLAG_
|
||||
* CLAIMED clear). A claim is a scattered chain, not a required contiguous
|
||||
* run -- links devblocks via blk_meta_t's prev_block/next_block/
|
||||
* chain_length fields; owner_fp is stamped onto *every* member devblock,
|
||||
* not just the head, so ownership reads locally from any member without
|
||||
* walking the chain. Fails outright (BLK_ENOSPC), no partial claim, if
|
||||
* fewer than `count` free devblocks exist.
|
||||
*
|
||||
* @param owner_fp 8-byte fingerprint (this session's convention: the
|
||||
* first 8 bytes of the claiming identity's 32-byte
|
||||
* VMIdentity.owner_pubkey -- owner_fp had no real
|
||||
* producer anywhere in this codebase before this,
|
||||
* only accessors, so this call site sets the
|
||||
* convention rather than following one).
|
||||
* @param count Devblocks to claim (whole devblocks, §F.4 decision
|
||||
* 4, already inherited, not revisited here).
|
||||
* @param out_chain_head The claim's identity to the caller: its
|
||||
* chain-head devblock number (a 1 KiB FORTH-block
|
||||
* LBN, the representative/first sub-block of that
|
||||
* devblock -- not a separate devblock-index
|
||||
* namespace; blk_get_meta()/blk_set_meta() already
|
||||
* resolve any LBN within a devblock to the same
|
||||
* shared blk_meta_t, so this is a valid handle for
|
||||
* blk_meta_relocate_devblock() below).
|
||||
* @return BLK_OK, BLK_ENODEV (no disk-backed device), BLK_EINVAL (NULL
|
||||
* arg or count == 0), or BLK_ENOSPC (not enough free devblocks).
|
||||
*/
|
||||
int blk_firsttouch_claim(const uint8_t owner_fp[8], uint32_t count, uint32_t *out_chain_head);
|
||||
|
||||
/* blk_owner_has_claim - FABRIC-2.md §I.2's own "an existing claim is a
|
||||
* separate, not-yet-decided question" (see blk_firsttouch_claim()'s doc
|
||||
* comment above), decided 2026-09-05 for the overflow-trigger call site
|
||||
* (capsule_wirebind.c): answered by a disk scan, not a RAM flag, matching
|
||||
* BMAPFMT's own "ownership travels with the block, no centralized table"
|
||||
* model -- the answer survives a reboot/reattach for free, the same way
|
||||
* blk_firsttouch_claim()'s own free/claimed scan already does.
|
||||
*
|
||||
* One linear scan over blk_get_first_disk_range()'s representative LBNs
|
||||
* (same shape as blk_firsttouch_claim()'s own scan); returns as soon as
|
||||
* any devblock's blk_meta_t has BLK_FLAG_CLAIMED set and owner_fp equal
|
||||
* to the argument.
|
||||
*
|
||||
* @param owner_fp 8-byte fingerprint, same convention as
|
||||
* blk_firsttouch_claim()'s own owner_fp argument.
|
||||
* @return 1 if owner_fp already owns at least one claimed devblock, 0 if
|
||||
* not (including "no disk-backed device attached" -- nothing to
|
||||
* own in that case).
|
||||
*/
|
||||
int blk_owner_has_claim(const uint8_t owner_fp[8]);
|
||||
|
||||
/* blk_meta_relocate_devblock - the real migration primitive (FABRIC-2.md
|
||||
* §I.2, migration state machine, 2026-09-04): moves one devblock's full
|
||||
* content and blk_meta_t ownership from home_devblock to target_devblock.
|
||||
* BLK_FLAG_MIGRATING is set on home_devblock for the duration -- the
|
||||
* state machine genuinely is just this one flag's two states (clear =
|
||||
* STABLE, set = MIGRATING), matching what BLK_FLAG_MIGRATING was already
|
||||
* reserved for.
|
||||
*
|
||||
* Devblock granularity (4 KiB) holds BLK_PACK_RATIO (3, not 4 -- the 4th
|
||||
* 1 KiB slice is the packed blk_meta_t metadata region) FORTH blocks
|
||||
* (1 KiB each); the actual content copy runs blk_subsys_relocate_block()
|
||||
* BLK_PACK_RATIO times, once per FORTH-block offset within the devblock
|
||||
* -- that function is the only existing content-relocation primitive,
|
||||
* and it operates at FORTH-block granularity, not devblock granularity,
|
||||
* so this function is the bridge between the two. On success, transfers
|
||||
* owner_fp/acl_allow/acl_ttl from home's blk_meta_t to target's, clears
|
||||
* home's (freed, BLK_FLAG_CLAIMED unset), and sets BLK_FLAG_CLAIMED on
|
||||
* target.
|
||||
*
|
||||
* NOT atomic across all BLK_PACK_RATIO FORTH-block copies -- a mid-loop
|
||||
* failure (e.g. blk_subsys_relocate_block()'s own reloc-table
|
||||
* exhaustion) leaves however many already succeeded in place, redirected, with
|
||||
* BLK_FLAG_MIGRATING still set on home_devblock and blk_meta_t ownership
|
||||
* NOT yet transferred. This is a known, documented limitation, not
|
||||
* silently accepted: true all-or-nothing atomicity across BLK_PACK_RATIO
|
||||
* separate relocation-table entries would need its own transaction log, out of
|
||||
* scope for this pass. A caller that hits a non-BLK_OK return should
|
||||
* treat home_devblock as still MIGRATING and not yet safe to reclaim.
|
||||
*
|
||||
* @return BLK_OK, BLK_EINVAL (home == target, or invalid devblock), or
|
||||
* whichever blk_subsys_relocate_block() error stopped the loop.
|
||||
*/
|
||||
int blk_meta_relocate_devblock(uint32_t home_devblock, uint32_t target_devblock);
|
||||
|
||||
/* blk_migration_idle_check - heat/wear-leveling migration trigger, meant
|
||||
* to be called once per idle tick (FABRIC-2.md §I.2, 2026-09-04). See
|
||||
* block_subsystem.c's own doc comment for the full scan/threshold
|
||||
* description and what's deliberately left open (overflow-triggered
|
||||
* migration). */
|
||||
void blk_migration_idle_check(void);
|
||||
|
||||
int blk_subsys_shutdown(void);
|
||||
|
||||
uint8_t *blk_get_buffer(uint32_t block_num, int writable);
|
||||
|
||||
uint8_t *blk_get_empty_buffer(uint32_t block_num);
|
||||
|
||||
int blk_flush(uint32_t block_num);
|
||||
|
||||
int blk_update(uint32_t block_num);
|
||||
|
||||
int blk_get_volume_meta(blk_volume_meta_t *meta);
|
||||
|
||||
int blk_set_volume_meta(const blk_volume_meta_t *meta);
|
||||
|
||||
/* Top-of-device system-metadata fence I/O (Phase 8, 2026-08-26) -- raw,
|
||||
* unpacked 4 KiB devblocks, no FORTH word wraps either. See
|
||||
* block_subsystem.c's own doc comment on these two functions for the
|
||||
* full addressing/refusal rules. buf must point to exactly 4096 bytes.
|
||||
* Returns BLK_OK, BLK_ENODEV (no disk-backed device attached),
|
||||
* BLK_EINVAL (devblock_from_top >= the on-disk fence size, or NULL buf),
|
||||
* or BLK_EIO (underlying blkio read/write failed). */
|
||||
int blk_meta_zone_read(uint32_t devblock_from_top, uint8_t buf[4096]);
|
||||
|
||||
int blk_meta_zone_write(uint32_t devblock_from_top, const uint8_t buf[4096]);
|
||||
|
||||
/* CRC-64/ISO (poly 0x42F0E1EBA9EA3693), reflected, init/final all-ones --
|
||||
* exposed for homeblocks_sig.c's drive-signature integrity check, which
|
||||
* needs the exact same algorithm this file already uses for per-block
|
||||
* checksums rather than a second, duplicate CRC implementation. */
|
||||
uint64_t compute_crc64(const uint8_t *data, size_t len);
|
||||
|
||||
int blk_is_valid(uint32_t block_num);
|
||||
|
||||
uint32_t blk_get_total_blocks(void);
|
||||
|
||||
int blk_get_meta(uint32_t block_num, blk_meta_t *meta);
|
||||
|
||||
int blk_set_meta(uint32_t block_num, const blk_meta_t *meta);
|
||||
|
||||
/* BMAPFMT field accessors -- FABRIC-2.md §F.4/§H.6/§H.12 step 14. Thin
|
||||
* read-modify-write wrappers over blk_get_meta()/blk_set_meta() (which
|
||||
* already own the caching/dirty-tracking), one per new blk_meta_t field.
|
||||
* FORTH wrappers (BLK-ACL-ALLOW@/! etc., §H.12 step 15) call these, not
|
||||
* blk_get_meta()/blk_set_meta() directly -- same C-primitive/FORTH-policy
|
||||
* split as the existing word-level ACL system. */
|
||||
int blk_owner_fp_get(uint32_t block_num, uint8_t out_fp[8]);
|
||||
int blk_owner_fp_set(uint32_t block_num, const uint8_t fp[8]);
|
||||
|
||||
int blk_acl_allow_get(uint32_t block_num, uint8_t *out_allow);
|
||||
int blk_acl_allow_set(uint32_t block_num, uint8_t allow);
|
||||
|
||||
int blk_acl_ttl_get(uint32_t block_num, uint32_t *out_ttl);
|
||||
int blk_acl_ttl_set(uint32_t block_num, uint32_t ttl);
|
||||
|
||||
int blk_flags_get(uint32_t block_num, uint64_t *out_flags);
|
||||
int blk_flags_set(uint32_t block_num, uint64_t flags);
|
||||
|
||||
int blk_is_allocated(uint32_t block_num);
|
||||
|
||||
int blk_mark_allocated(uint32_t block_num);
|
||||
|
||||
int blk_mark_free(uint32_t block_num);
|
||||
|
||||
int blk_allocate(uint32_t * block_num);
|
||||
|
||||
int blk_subsys_add_raw_device(uint8_t *buf, uint32_t nblocks);
|
||||
|
||||
/* Commit the low-level format (write header + BAM) for the disk slot that
|
||||
* owns lbn. No-op (returns BLK_OK) if already FORMATTED. Must be called
|
||||
* by the disk's owner before any write to that slot will succeed. */
|
||||
int blk_subsys_confirm_format(uint32_t lbn);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* STARFORTH_BLOCK_SUBSYSTEM_H */
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
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 STARFORTH_CLI_H
|
||||
#define STARFORTH_CLI_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "log.h"
|
||||
|
||||
/**
|
||||
* @brief Heartbeat logging modes for DoE experiments
|
||||
*/
|
||||
typedef enum {
|
||||
HEARTBEAT_LOG_OFF = 0, /* No heartbeat output, only final summary */
|
||||
HEARTBEAT_LOG_SUMMARY = 1, /* Bucket aggregates every N ticks */
|
||||
HEARTBEAT_LOG_FULL = 2 /* Per-tick CSV output to hb/run-<id>.csv */
|
||||
} HeartbeatLogMode;
|
||||
|
||||
/**
|
||||
* @brief Configuration structure from command-line arguments
|
||||
* Consolidates all parsed CLI options into a single, cleanly organized struct
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/* Test modes */
|
||||
int run_tests;
|
||||
int break_me;
|
||||
int fail_fast;
|
||||
|
||||
/* Benchmark mode */
|
||||
int benchmark;
|
||||
int benchmark_iterations;
|
||||
|
||||
/* Design of Experiments mode */
|
||||
int doe_experiment; /* 1 if --doe-experiment flag set, run harness once and collect metrics */
|
||||
HeartbeatLogMode heartbeat_log_mode; /* off|summary|full for DoE time-series data */
|
||||
|
||||
/* REPL mode */
|
||||
int script_mode; /* -s: silent/script mode (no prompts, no "ok" output) */
|
||||
|
||||
/* Logging */
|
||||
LogLevel log_level;
|
||||
int log_level_explicitly_set;
|
||||
|
||||
/* Block I/O */
|
||||
const char* disk_img_path;
|
||||
uint32_t ram_disk_mb;
|
||||
|
||||
/* Legacy flags (internal use) */
|
||||
int run_tests_flag_observed;
|
||||
} CLIConfig;
|
||||
|
||||
/**
|
||||
* @brief Parse command-line arguments into CLIConfig
|
||||
*
|
||||
* @param argc Argument count
|
||||
* @param argv Argument vector
|
||||
* @return CLIConfig struct with parsed options, or exits on --version/--help
|
||||
*/
|
||||
CLIConfig cli_parse(int argc, char* argv[]);
|
||||
|
||||
/**
|
||||
* @brief Print usage/help information
|
||||
* @param program_name Name of the executable
|
||||
*/
|
||||
void cli_print_usage(const char* program_name);
|
||||
|
||||
#endif /* STARFORTH_CLI_H */
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
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 COMPUDYNAMICS_H
|
||||
#define COMPUDYNAMICS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ============================================================================
|
||||
* Compudynamics: Generic Entity-Agnostic Physics Engine
|
||||
* ============================================================================
|
||||
*
|
||||
* Four operational levels observe and adapt to their own execution history:
|
||||
* - words (dictionary entries, driven by ssm_jacquard.c) -- LIVE
|
||||
* - VMs (Tripod fleet, driven by capsule_vm_physics.c) -- reserved
|
||||
* - blocks (Artemis block storage) -- reserved
|
||||
* - messages (Hermes event routing) -- reserved
|
||||
*
|
||||
* This module provides the entity-agnostic machinery shared by all four:
|
||||
* a deterministic regime classifier over an entity-ID touch history, and a
|
||||
* generic UCB1 bandit over a caller-sized config space. "Entity-ID" means
|
||||
* whatever the level touches -- word_id for words, vm_id for VMs, block
|
||||
* LBN for blocks, message type for messages.
|
||||
*
|
||||
* Determinism is the entire point of this module. Every input is an
|
||||
* execution count or an entity-ID sequence -- never a wall-clock timestamp,
|
||||
* never a duration. Two architectures executing the same instruction
|
||||
* sequence at different real speeds (e.g. QEMU/TCG amd64 vs aarch64) must
|
||||
* produce byte-identical classifier output. This was violated once
|
||||
* (ssm_jacquard.c's temporal_decay input, derived from a wall-clock-tainted
|
||||
* decay slope) and broke cross-architecture dict_hash parity; this module
|
||||
* exists so that mistake has nowhere to hide the second time.
|
||||
*
|
||||
* Word-level and VM-level are the only two levels with real, instantiated
|
||||
* CDTuning constants today (CD_TUNING_WORD, CD_TUNING_VM). Only word-level
|
||||
* is actually wired through this module (ssm_jacquard.c); VM-level's own
|
||||
* fleet mechanism (capsule_vm_physics.c, VMFleetWindow) is a separate,
|
||||
* already-validated, already-accepted design that legitimately depends on
|
||||
* real elapsed time for its rate-recovery math (fleet_transfer_slope_q48)
|
||||
* and is out of scope here. CD_TUNING_VM documents what this module's
|
||||
* classifier-side parameters would be if VM-level regime classification
|
||||
* were ever migrated onto it; its bandit-side fields are zeroed and
|
||||
* unused because VM level has no config-space bandit today. Block and
|
||||
* message levels have no Artemis/Hermes compudynamics implementation yet
|
||||
* -- CD_TUNING_BLOCK and CD_TUNING_MESSAGE are deliberately not defined
|
||||
* here rather than filled with invented numbers.
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/* Hard cap on how many recent entity-ID touches cd_classify_ids() will look
|
||||
* at. Callers pass a shorter slice via count; this cap bounds the classifier's
|
||||
* O(depth^2) worst case and the stack scratch space it uses internally. */
|
||||
#define CD_MAX_CLASSIFY_DEPTH 64u
|
||||
|
||||
/* ============================================================================
|
||||
* Deterministic Regime Classification
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
uint32_t diversity_q16; /* distinct IDs in window / window size, Q16 [0,65536] */
|
||||
uint32_t volatility_q16; /* dispersion (coefficient of variation) of per-ID
|
||||
* touch counts within the window, Q16, unclamped
|
||||
* above 65536 is possible and meaningful (high CV) */
|
||||
uint32_t locality_q16; /* fraction of touches that repeat an ID seen within
|
||||
* the preceding locality_lookback touches, Q16 [0,65536] */
|
||||
} CDRegimeMetrics;
|
||||
|
||||
/**
|
||||
* @brief Classify a recent entity-ID touch sequence into diversity/volatility/
|
||||
* locality, purely from execution counts -- no wall-clock input anywhere.
|
||||
*
|
||||
* @param ids Recent entity IDs, oldest first, chronological order
|
||||
* @param count Number of valid entries in ids (clamped internally
|
||||
* to CD_MAX_CLASSIFY_DEPTH)
|
||||
* @param locality_lookback How many preceding touches count as "recent" for
|
||||
* the locality measure (clamped to count)
|
||||
* @return Classified regime metrics; all-zero if count == 0
|
||||
*/
|
||||
CDRegimeMetrics cd_classify_ids(const uint32_t *ids, uint32_t count,
|
||||
uint32_t locality_lookback);
|
||||
|
||||
/* ============================================================================
|
||||
* Per-Level Tuning Knobs
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
/* --- Classifier knobs (used by cd_classify_ids callers) --- */
|
||||
uint32_t id_window_depth; /* touches to classify over, <= CD_MAX_CLASSIFY_DEPTH */
|
||||
uint32_t locality_lookback; /* recurrence lookback within id_window_depth */
|
||||
uint32_t diversity_high_threshold_q16;
|
||||
uint32_t volatility_high_threshold_q16;
|
||||
uint32_t locality_high_threshold_q16;
|
||||
|
||||
/* --- Bandit knobs (used by cd_config_table_*); 0 where the level has
|
||||
* no config-space bandit (e.g. VM level today -- see file header) --- */
|
||||
uint32_t num_configs; /* size of the config space, e.g. 128 */
|
||||
uint32_t num_regimes; /* size of the regime space, e.g. 8 */
|
||||
uint32_t score_max;
|
||||
uint32_t score_min;
|
||||
uint32_t score_decay_factor_q16; /* per-tick exponential decay of benefit_score */
|
||||
uint32_t ucb_k; /* UCB1 exploration coefficient */
|
||||
uint32_t reward_gain; /* max per-tick score delta magnitude */
|
||||
uint32_t joint_weight_q16; /* weight of window/locality joint-convergence
|
||||
* signal in the combined reward, Q16 */
|
||||
uint32_t anova_weight_q16; /* weight of the caller-supplied stability
|
||||
* signal (e.g. ANOVA early-exit) in the
|
||||
* combined reward, Q16 */
|
||||
} CDTuning;
|
||||
|
||||
/* Tuning knobs are returned by value, not exposed as addressable globals.
|
||||
* This freestanding kernel's boot-time loader does not reliably relocate
|
||||
* the address of an extern const aggregate referenced from a different
|
||||
* translation unit (confirmed by direct QEMU/gdb-style fault tracing:
|
||||
* &CD_TUNING_WORD-style access faulted on an unrelocated pointer, while
|
||||
* cross-TU function calls -- including struct-return-by-value, which
|
||||
* uses a caller-local hidden pointer, never a global's address --
|
||||
* worked correctly). Every existing scalar tunable elsewhere in this
|
||||
* codebase already avoids this by being a #define immediate rather than
|
||||
* a referenced global; these accessors are the aggregate-typed
|
||||
* equivalent of that same constraint. */
|
||||
|
||||
/* Word level (ssm_jacquard.c): 128 configs (7-bit L1-L7), 8 regimes
|
||||
* (3-bit diversity/volatility/locality). Values match the constants
|
||||
* ssm_jacquard.c/.h used before this module existed -- this is a lift,
|
||||
* not a retune. */
|
||||
CDTuning cd_tuning_word(void);
|
||||
|
||||
/* VM level (capsule_vm_physics.c): classifier-side parameters only, for a
|
||||
* future migration off Hera's word-level regime proxy. Not consumed by
|
||||
* any call site today -- see file header. Bandit fields are zero: VM
|
||||
* level modulates a continuous multiplier (fleet_transfer_slope_q48)
|
||||
* rather than selecting from a discrete config space. */
|
||||
CDTuning cd_tuning_vm(void);
|
||||
|
||||
/* ============================================================================
|
||||
* Generic UCB1 Config-Space Bandit
|
||||
* ============================================================================
|
||||
*
|
||||
* Stratified by regime: each (regime, config) pair has its own benefit
|
||||
* score and tick count, so the bandit learns "best config per regime"
|
||||
* rather than one global best. One clock only: cd_config_table_tick()
|
||||
* scores the currently-active config and reselects on every call -- no
|
||||
* derived multi-tick batching unit exists anywhere in this mechanism.
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
uint8_t config_bits;
|
||||
uint8_t has_prev; /* 1 once prev_window/prev_locality_q16 hold real data */
|
||||
uint8_t _pad[2];
|
||||
uint32_t benefit_score;
|
||||
uint32_t tick_count;
|
||||
uint32_t prev_window;
|
||||
uint32_t prev_locality_q16;
|
||||
} CDConfigEntry;
|
||||
|
||||
typedef struct {
|
||||
CDConfigEntry *entries; /* num_configs entries, malloc'd */
|
||||
uint32_t *regime_scores; /* num_regimes * num_configs, row-major [regime][config] */
|
||||
uint32_t *regime_ticks; /* same shape as regime_scores */
|
||||
uint32_t *total_regime_ticks; /* num_regimes entries */
|
||||
uint32_t num_configs;
|
||||
uint32_t num_regimes;
|
||||
uint8_t current_config;
|
||||
uint8_t current_regime;
|
||||
} CDConfigTable;
|
||||
|
||||
/**
|
||||
* @brief Allocate a config table sized for tuning->num_configs / num_regimes.
|
||||
* @return 0 on success, -1 on malloc failure (table left zeroed, unusable)
|
||||
*/
|
||||
int cd_config_table_init(CDConfigTable *table, const CDTuning *tuning);
|
||||
|
||||
/**
|
||||
* @brief Free all memory owned by a config table. Safe on a zeroed table.
|
||||
*/
|
||||
void cd_config_table_free(CDConfigTable *table);
|
||||
|
||||
/**
|
||||
* @brief Seed every (regime, config) score with initial_scores[config], and
|
||||
* set the starting active config to initial_config.
|
||||
* @param initial_scores Array of tuning->num_configs initial benefit scores
|
||||
*/
|
||||
void cd_config_table_seed(CDConfigTable *table, const CDTuning *tuning,
|
||||
const uint32_t *initial_scores, uint8_t initial_config);
|
||||
|
||||
/**
|
||||
* @brief Score the currently-active config from this tick's outcome and
|
||||
* UCB1-reselect this tick's config, for the given regime.
|
||||
*
|
||||
* Combined reward = joint_weight * window/locality-convergence-stability
|
||||
* + anova_weight * anova_stability_q16
|
||||
* Joint-convergence is skipped (falls back to ANOVA-only) on a config's
|
||||
* first tick, or when use_joint_convergence is false (the caller's config
|
||||
* bits don't include loops whose convergence this signal is meant to
|
||||
* track -- word level's L5/L6, see ssm_jacquard.c).
|
||||
*
|
||||
* @param table Config table (must be initialised)
|
||||
* @param tuning Tuning knobs matching this table's sizing
|
||||
* @param regime Current regime index, < tuning->num_regimes
|
||||
* @param anova_stability_q16 Caller-supplied stability signal, Q16 [0,65536]
|
||||
* @param use_joint_convergence Whether to include the window/locality signal
|
||||
* @param current_window Caller's current window-width metric
|
||||
* @param current_locality_q16 Caller's current locality_q16 from cd_classify_ids
|
||||
* @return The newly-selected config's config_bits
|
||||
*/
|
||||
uint8_t cd_config_table_tick(CDConfigTable *table, const CDTuning *tuning,
|
||||
uint8_t regime, uint32_t anova_stability_q16,
|
||||
int use_joint_convergence,
|
||||
uint32_t current_window, uint32_t current_locality_q16);
|
||||
|
||||
/**
|
||||
* @brief Force the table onto an externally-chosen config (as if UCB1 had
|
||||
* picked it). One-tick nudge, not a hold -- see ssm_l8_force_config().
|
||||
*/
|
||||
void cd_config_table_force(CDConfigTable *table, uint8_t config_idx);
|
||||
|
||||
#endif /* COMPUDYNAMICS_H */
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
*** StarForth ***
|
||||
|
||||
dictionary_heat_optimization.h - Phase 2: Heat-Aware Dictionary Lookup
|
||||
|
||||
Inference-driven optimizations for dictionary lookup based on execution heat
|
||||
patterns from the Rolling Window of Truth:
|
||||
|
||||
1. Heat percentile thresholds (25th, 50th, 75th)
|
||||
2. Bucket reorganization by co-execution patterns
|
||||
3. Heat-aware lookup strategy (search hot words first)
|
||||
*/
|
||||
|
||||
#ifndef DICTIONARY_HEAT_OPTIMIZATION_H
|
||||
#define DICTIONARY_HEAT_OPTIMIZATION_H
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
/**
|
||||
* @brief Calculate heat percentile thresholds for all words in dictionary
|
||||
*
|
||||
* Computes 25th, 50th, 75th percentiles of execution_heat across all
|
||||
* dictionary entries. Used to partition lookup search order.
|
||||
*/
|
||||
void dict_update_heat_percentiles(VM *vm);
|
||||
|
||||
/**
|
||||
* @brief Reorganize dictionary buckets by inference-driven priorities
|
||||
*
|
||||
* Uses rolling window co-execution patterns to reorder words within buckets.
|
||||
* Hot/frequently co-executing words are placed first for faster lookup.
|
||||
*/
|
||||
void dict_reorganize_buckets_by_heat(VM *vm);
|
||||
|
||||
/**
|
||||
* @brief Find word with heat-aware search strategy
|
||||
*
|
||||
* Instead of newest-first, searches buckets in order:
|
||||
* 1. Top 25% by heat (likely hits)
|
||||
* 2. Middle 50% by heat
|
||||
* 3. Bottom 25% (rarely executed)
|
||||
*/
|
||||
DictEntry *dict_find_word_heat_aware(VM *vm, const char *name, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Periodically reoptimize dictionary based on latest heat patterns
|
||||
*
|
||||
* Called every ~1 second or every N executions to adapt lookup strategy
|
||||
* to current workload patterns from rolling window.
|
||||
*/
|
||||
void dict_adaptive_optimization_pass(VM *vm);
|
||||
|
||||
/* Frequency-class (fc) buckets — shared between dictionary_management.c and
|
||||
dictionary_heat_optimization.c. Declared here once to give LTO a single
|
||||
canonical type definition and avoid -Werror=lto-type-mismatch. */
|
||||
#ifndef SF_FC_BUCKETS
|
||||
#define SF_FC_BUCKETS 256
|
||||
#endif
|
||||
extern DictEntry **sf_fc_list[SF_FC_BUCKETS];
|
||||
extern size_t sf_fc_count[SF_FC_BUCKETS];
|
||||
extern size_t sf_fc_cap[SF_FC_BUCKETS];
|
||||
|
||||
#endif /* DICTIONARY_HEAT_OPTIMIZATION_H */
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
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 DICTIONARY_MANAGEMENT_H
|
||||
#define DICTIONARY_MANAGEMENT_H
|
||||
|
||||
#include "vm.h" /* For VM type and word_func_t */
|
||||
|
||||
/**
|
||||
* @brief Initialize the Forth dictionary
|
||||
*
|
||||
* @param vm Pointer to the virtual machine instance
|
||||
*
|
||||
* Initializes all dictionary structures and prepares them for use.
|
||||
* Must be called before any other dictionary operations.
|
||||
*/
|
||||
void dictionary_init(VM * vm);
|
||||
|
||||
/**
|
||||
* @brief Register a new word in the dictionary
|
||||
*
|
||||
* @param vm Pointer to the virtual machine instance
|
||||
* @param name Name of the word to register
|
||||
* @param func Function pointer to the word's implementation
|
||||
*
|
||||
* Adds a new word to the dictionary with the specified name and implementation.
|
||||
*/
|
||||
void dictionary_register(VM *vm, const char *name, word_func_t func);
|
||||
|
||||
/**
|
||||
* @brief Look up a word in the dictionary by name
|
||||
*
|
||||
* @param vm Pointer to the virtual machine instance
|
||||
* @param name Name of the word to look up
|
||||
* @return word_func_t Function pointer to the word's implementation or NULL if not found
|
||||
*/
|
||||
word_func_t dictionary_lookup(VM *vm, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Clear the entire dictionary
|
||||
*
|
||||
* @param vm Pointer to the virtual machine instance
|
||||
*
|
||||
* Removes all words from the dictionary and frees associated memory.
|
||||
* This is an optional operation that may be used during cleanup.
|
||||
*/
|
||||
void dictionary_clear(VM * vm);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file doe_metrics.h
|
||||
* @brief Design of Experiments (DoE) metrics collection API
|
||||
*
|
||||
* Provides C-level programmatic access to VM metrics for physics-based experiments.
|
||||
* Enables efficient in-process data collection without string parsing overhead.
|
||||
*
|
||||
* Usage:
|
||||
* struct DoeMetrics metrics = metrics_from_vm(vm);
|
||||
* metrics_write_csv_row(stdout, &metrics, run_number);
|
||||
*/
|
||||
|
||||
#ifndef DOE_METRICS_H
|
||||
#define DOE_METRICS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include "vm.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Complete metrics snapshot for a single DoE run
|
||||
*
|
||||
* Represents all 35 metrics collected per test iteration, mirroring the CSV schema.
|
||||
* All values extracted directly from VM structures (no string parsing).
|
||||
*/
|
||||
typedef struct {
|
||||
/* === Metrics === */
|
||||
uint32_t total_lookups; /* Total dictionary lookups during run */
|
||||
|
||||
/* === Cache Statistics === */
|
||||
uint64_t cache_hits; /* Cache hits (if enabled) */
|
||||
double cache_hit_percent; /* Hit percentage (0-100) */
|
||||
uint64_t bucket_hits; /* Bucket hits after cache miss */
|
||||
double bucket_hit_percent; /* Hit percentage (0-100) */
|
||||
|
||||
/* === Latency Metrics === */
|
||||
int64_t cache_hit_latency_ns; /* Average cache hit latency (ns) */
|
||||
int64_t cache_hit_stddev_ns; /* StdDev of cache hit latency */
|
||||
int64_t bucket_search_latency_ns; /* Average bucket search latency (ns) */
|
||||
int64_t bucket_search_stddev_ns; /* StdDev of bucket search latency */
|
||||
|
||||
/* === Pipelining Metrics (Loop #4) === */
|
||||
uint64_t context_predictions_total; /* Total prefetch predictions */
|
||||
uint64_t context_correct; /* Successful predictions */
|
||||
double context_accuracy_percent; /* Accuracy percentage (0-100) */
|
||||
uint64_t cache_promotions; /* Hotwords cache promotions */
|
||||
uint64_t cache_demotions; /* Hotwords cache demotions */
|
||||
|
||||
/* === Rolling Window Metrics (Loop #2) === */
|
||||
double window_diversity_percent; /* Window pattern diversity (0-100) */
|
||||
uint32_t window_final_size_bytes; /* Final rolling window size */
|
||||
uint32_t rolling_window_width; /* Width of rolling window (effective/adaptive) */
|
||||
uint32_t actual_window_size; /* Ring buffer fill level: min(total_executions, ROLLING_WINDOW_SIZE) */
|
||||
uint64_t total_executions; /* Words recorded in window */
|
||||
uint64_t window_variance_q48; /* Pattern variance (Q48.16) */
|
||||
|
||||
/* === Heat Dynamics (Loop #1 & #3) === */
|
||||
double decay_slope; /* Decay rate slope */
|
||||
uint64_t total_heat; /* Aggregate execution heat */
|
||||
uint64_t hot_word_count; /* Words above heat threshold */
|
||||
uint64_t stale_word_count; /* Words with decaying heat */
|
||||
double stale_word_ratio; /* Stale words / total words */
|
||||
double avg_word_heat; /* Average execution heat */
|
||||
|
||||
/* === Heartbeat & Timing (Loop #7) === */
|
||||
uint64_t tick_count; /* Total heartbeat ticks elapsed */
|
||||
uint64_t tick_target_ns; /* Current tick interval (adaptive) */
|
||||
uint64_t inference_run_count; /* Times inference engine invoked */
|
||||
uint64_t early_exit_count; /* ANOVA early-exits (variance stable) */
|
||||
|
||||
/* === Window & Decay Inference (Loop #5 & #6) === */
|
||||
double prefetch_accuracy_percent; /* Speculative prefetch success rate (0-100) */
|
||||
uint64_t prefetch_attempts; /* Total speculative prefetch attempts */
|
||||
uint64_t prefetch_hits; /* Successful prefetch hits */
|
||||
uint64_t window_tuning_checks; /* Number of times window was tuned */
|
||||
uint32_t final_effective_window_size; /* Final window size after tuning */
|
||||
|
||||
/* === Performance === */
|
||||
int64_t vm_workload_duration_ns_q48; /* VM workload duration (Q48.16, nanoseconds) */
|
||||
uint64_t total_runtime_ms; /* Total execution time (ms) */
|
||||
uint64_t words_executed; /* Total word executions */
|
||||
uint64_t dictionary_lookups; /* Dictionary search operations */
|
||||
uint64_t memory_allocated_bytes; /* Total memory allocated */
|
||||
double speedup_vs_baseline; /* Speedup ratio vs baseline */
|
||||
|
||||
/* === Statistical === */
|
||||
double ci_lower_95; /* 95% confidence interval lower */
|
||||
double ci_upper_95; /* 95% confidence interval upper */
|
||||
|
||||
/* === System State Deltas === */
|
||||
int64_t cpu_temp_delta_c_q48; /* CPU temp change during run (Q48.16, °C) */
|
||||
int64_t cpu_freq_delta_mhz_q48; /* CPU freq change during run (Q48.16, MHz) */
|
||||
|
||||
/* === Tuning Knobs === */
|
||||
uint32_t decay_rate_q16; /* Decay rate (Q16 fixed-point) */
|
||||
uint32_t decay_min_interval_ns; /* Min decay interval (ns) */
|
||||
uint32_t rolling_window_size; /* Rolling window size */
|
||||
uint32_t adaptive_shrink_rate; /* Shrink rate percentage */
|
||||
uint32_t heat_cache_demotion_threshold; /* Demotion threshold */
|
||||
|
||||
/* === Loop Enable Flags (2^7 factorial) === */
|
||||
int enable_loop_1_heat_tracking; /* Loop #1: Execution heat tracking */
|
||||
int enable_loop_2_rolling_window; /* Loop #2: Rolling window history */
|
||||
int enable_loop_3_linear_decay; /* Loop #3: Linear decay */
|
||||
int enable_loop_4_pipelining; /* Loop #4: Pipelining metrics */
|
||||
int enable_loop_5_window_inference; /* Loop #5: Window width inference */
|
||||
int enable_loop_6_decay_inference; /* Loop #6: Decay slope inference */
|
||||
int enable_loop_7_adaptive_heartrate; /* Loop #7: Adaptive heartrate */
|
||||
|
||||
/* === Legacy Configuration === */
|
||||
int enable_hotwords_cache; /* 1 if cache enabled, 0 otherwise */
|
||||
int enable_pipelining; /* 1 if pipelining enabled, 0 otherwise */
|
||||
} DoeMetrics;
|
||||
|
||||
/**
|
||||
* @brief Extract complete metrics snapshot from VM
|
||||
*
|
||||
* Collects all metrics from current VM state after a test harness run.
|
||||
* Workload duration and CPU deltas must be supplied by caller.
|
||||
*
|
||||
* @param vm Pointer to Forth VM instance
|
||||
* @param workload_duration_ns Actual VM workload execution time (nanoseconds)
|
||||
* @param cpu_temp_delta_c CPU temperature change during run (°C)
|
||||
* @param cpu_freq_delta_mhz CPU frequency change during run (MHz)
|
||||
* @return Populated metrics structure (timestamp filled in)
|
||||
*/
|
||||
DoeMetrics metrics_from_vm(VM *vm, uint64_t workload_duration_ns,
|
||||
int32_t cpu_temp_delta_c, int32_t cpu_freq_delta_mhz);
|
||||
|
||||
/**
|
||||
* @brief Write CSV header row
|
||||
*
|
||||
* Writes the 35-column CSV header to output stream.
|
||||
*
|
||||
* @param out Output file stream
|
||||
*/
|
||||
void metrics_write_csv_header(FILE *out);
|
||||
|
||||
/**
|
||||
* @brief Write metrics as CSV row
|
||||
*
|
||||
* Writes a single run's metrics as comma-separated values.
|
||||
*
|
||||
* @param out Output file stream
|
||||
* @param metrics Metrics structure to write
|
||||
*/
|
||||
void metrics_write_csv_row(FILE *out, const DoeMetrics *metrics);
|
||||
|
||||
/**
|
||||
* @brief Write reduced James Law CSV row (20 columns)
|
||||
*
|
||||
* Outputs only the subset of metrics needed for the James Law validation experiment.
|
||||
* This matches the header format in run_window_sweep.sh (minus the 5 script-prepended columns).
|
||||
*
|
||||
* @param out Output file stream
|
||||
* @param metrics Metrics structure to write
|
||||
* @param vm VM instance (for heartbeat bucket aggregates and L8 mode)
|
||||
*/
|
||||
void metrics_write_james_law_csv_row(FILE *out, const DoeMetrics *metrics, VM *vm);
|
||||
|
||||
/**
|
||||
* @brief Print metrics as human-readable text
|
||||
*
|
||||
* Outputs metrics in formatted text for debugging/verification.
|
||||
*
|
||||
* @param out Output file stream
|
||||
* @param metrics Metrics structure to print
|
||||
*/
|
||||
void metrics_print_text(FILE *out, const DoeMetrics *metrics);
|
||||
|
||||
/**
|
||||
* @brief Get current CPU temperature (Celsius)
|
||||
*
|
||||
* Reads /sys/class/thermal/thermal_zone0/temp on Linux,
|
||||
* returns 0 if unavailable.
|
||||
*
|
||||
* @return Temperature in °C, or 0 if unable to read
|
||||
*/
|
||||
int32_t metrics_get_cpu_temp_c(void);
|
||||
|
||||
/**
|
||||
* @brief Get current CPU frequency (MHz)
|
||||
*
|
||||
* Tries multiple sources:
|
||||
* 1. /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
|
||||
* 2. /proc/cpuinfo
|
||||
* Returns 0 if unavailable.
|
||||
*
|
||||
* @return Frequency in MHz, or 0 if unable to read
|
||||
*/
|
||||
int32_t metrics_get_cpu_freq_mhz(void);
|
||||
|
||||
/**
|
||||
* @brief Get current timestamp as ISO 8601 string
|
||||
*
|
||||
* Format: "YYYY-MM-DDTHH:MM:SS"
|
||||
*
|
||||
* @param buf Buffer to write timestamp (minimum 32 bytes)
|
||||
* @param bufsize Size of buffer
|
||||
*/
|
||||
void metrics_get_timestamp(char *buf, size_t bufsize);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DOE_METRICS_H */
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
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 INFERENCE_ENGINE_H
|
||||
#define INFERENCE_ENGINE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "vm.h" /* For RollingWindowOfTruth, VM structure */
|
||||
#include "q48_16.h" /* For Q48.16 fixed-point math */
|
||||
|
||||
/* ============================================================================
|
||||
* OpAmp Model: Inference Engine Architecture
|
||||
* ============================================================================
|
||||
*
|
||||
* The inference engine unifies all VM adaptive tuning into a single loop:
|
||||
*
|
||||
* Rolling Window of Truth (execution_heat trajectory, metrics)
|
||||
* ↓
|
||||
* [Inference Engine]
|
||||
* ├─ ANOVA Early-Exit: Check if variance stable
|
||||
* ├─ Window Width Inference: Variance inflection detection
|
||||
* ├─ Decay Slope Inference: Closed-form linear regression
|
||||
* └─ Diagnostics: Fit quality, variance metrics
|
||||
* ↓
|
||||
* Tuning Outputs (adaptive_window_width, adaptive_decay_slope)
|
||||
* ↓
|
||||
* [Apply to VM]
|
||||
* ↓
|
||||
* [System Adapts]
|
||||
* ↓
|
||||
* [Feedback Loop Repeats]
|
||||
*
|
||||
* Key Design Principles:
|
||||
* - Q48.16 math only (no floating-point)
|
||||
* - ANOVA early-exit for efficiency (5% variance threshold)
|
||||
* - Closed-form algorithms (deterministic, fast)
|
||||
* - Observable diagnostics (RStudio dashboard feed)
|
||||
*/
|
||||
|
||||
/* ============================================================================
|
||||
* Input Data Structure: Metrics snapshot + window context
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
VM *vm; /* Owning VM (for dictionary heat snapshots) */
|
||||
/* === Window Context === */
|
||||
RollingWindowOfTruth *window; /* Pointer to window of truth */
|
||||
uint64_t trajectory_length; /* Number of heat entries to analyze */
|
||||
|
||||
/* === Current Metrics Snapshot === */
|
||||
uint64_t prefetch_hits; /* Successful prefetch predictions */
|
||||
uint64_t prefetch_attempts; /* Total prefetch attempts */
|
||||
uint64_t hot_word_count; /* Words above execution_heat threshold */
|
||||
uint64_t stale_word_count; /* Words with execution_heat in low range */
|
||||
uint64_t total_heat; /* Sum of all execution_heat values */
|
||||
uint32_t word_count; /* Total dictionary entries */
|
||||
|
||||
/* === Baseline (For Trending) === */
|
||||
uint64_t last_total_heat; /* Total heat from previous check */
|
||||
uint64_t last_stale_count; /* Stale count from previous check */
|
||||
} InferenceInputs;
|
||||
|
||||
/* ============================================================================
|
||||
* Output Data Structure: Tuning parameters + diagnostics
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
struct InferenceOutputs {
|
||||
/* === Tuning Outputs (Apply to VM) === */
|
||||
uint32_t adaptive_window_width; /* Inferred optimal window size (from variance inflection) */
|
||||
uint64_t adaptive_decay_slope; /* Inferred decay slope in Q48.16 (from regression) */
|
||||
|
||||
/* === Diagnostics (RStudio Dashboard Feed) === */
|
||||
uint64_t window_variance_q48; /* Variance of heat across window in Q48.16 */
|
||||
uint64_t slope_fit_quality_q48; /* R² or residual metric in Q48.16 (0.0 to 1.0) */
|
||||
|
||||
/* === Status Tracking === */
|
||||
uint32_t early_exited; /* 1 if ANOVA check skipped full inference, 0 if full run */
|
||||
uint64_t last_check_tick; /* Timestamp of last full inference (for monitoring) */
|
||||
};
|
||||
|
||||
/* ============================================================================
|
||||
* Main API: Run Inference Engine
|
||||
* ============================================================================
|
||||
*
|
||||
* Single entry point that handles:
|
||||
* 1. ANOVA early-exit (check variance stability)
|
||||
* 2. Full inference (if variance changed significantly)
|
||||
* 3. Update tuning outputs
|
||||
*
|
||||
* Thread-Safety: Safe for synchronous calls from vm_tick()
|
||||
* (No shared state written; outputs struct is caller-allocated)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Run adaptive inference engine on window data
|
||||
*
|
||||
* High-level flow:
|
||||
* 1. ANOVA Check: Is variance delta < 5%?
|
||||
* - YES: Return cached outputs immediately (cost: ~100 cycles)
|
||||
* - NO: Continue to full inference
|
||||
*
|
||||
* 2. Extract trajectory: Fresh execution_heat snapshot from dictionary
|
||||
*
|
||||
* 3. Window Width Inference:
|
||||
* - Compute variance across entire window
|
||||
* - Detect inflection point (where d(variance)/d(size) → 0)
|
||||
* - Return optimal window size
|
||||
*
|
||||
* 4. Decay Slope Inference:
|
||||
* - Transform trajectory to log space (ln(heat[t]))
|
||||
* - Fit linear regression: ln(heat) = a - slope*t
|
||||
* - Extract slope coefficient in Q48.16
|
||||
*
|
||||
* 5. Diagnostics:
|
||||
* - Compute fit quality (R² or residual)
|
||||
* - Store variance metric for next early-exit check
|
||||
*
|
||||
* @param inputs Snapshot of window + metrics (caller-provided)
|
||||
* @param outputs Tuning parameters + diagnostics (caller-provided, will be updated)
|
||||
*
|
||||
* @return void (results_run_01_2025_12_08 in outputs struct)
|
||||
*
|
||||
* Preconditions:
|
||||
* - inputs != NULL
|
||||
* - outputs != NULL
|
||||
* - inputs->window != NULL
|
||||
* - inputs->window->is_warm == 1 (window has enough data)
|
||||
* - inputs->trajectory_length > 1
|
||||
*
|
||||
* Side Effects:
|
||||
* - Modifies outputs struct (caller must preserve if caching)
|
||||
* - Allocates temporary trajectory buffer (freed on return)
|
||||
*/
|
||||
void inference_engine_run(InferenceInputs *inputs, InferenceOutputs *outputs);
|
||||
|
||||
/* ============================================================================
|
||||
* Configuration Constants (In vm.h, but referenced here)
|
||||
* ============================================================================
|
||||
*
|
||||
* User-tunable via Makefile:
|
||||
*
|
||||
* #define HEARTBEAT_INFERENCE_FREQUENCY 5000
|
||||
* - Ticks between full inference runs
|
||||
* - Decreasing = faster response, more CPU
|
||||
* - Increasing = slower response, less CPU
|
||||
*
|
||||
* #define VARIANCE_SIGNIFICANCE_THRESHOLD 5
|
||||
* - Percentage (0-100) at which variance change triggers re-inference
|
||||
* - Default: 5% (skip inference if variance changed < 5%)
|
||||
* - Decreasing = more frequent re-inference (conservative)
|
||||
* - Increasing = less frequent re-inference (aggressive)
|
||||
*
|
||||
* #define ADAPTIVE_MIN_WINDOW_SIZE 256
|
||||
* - Minimum effective_window_size (never shrink below this)
|
||||
* - Ensures minimum statistical validity
|
||||
* - Increasing = higher overhead, more accuracy
|
||||
*/
|
||||
|
||||
/* ============================================================================
|
||||
* Helper Functions (For Testing / Diagnostics)
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* @brief Pretty-print inference outputs for logging
|
||||
*
|
||||
* Format: "window_width=XXX variance_q48=YYYY slope_q48=ZZZZ fit_quality=WWWW"
|
||||
*
|
||||
* @param outputs Inference outputs struct
|
||||
* @return Pointer to static string (valid until next call)
|
||||
*/
|
||||
const char* inference_outputs_to_string(const InferenceOutputs *outputs);
|
||||
|
||||
/*
|
||||
* @brief Validate inference outputs for sanity
|
||||
*
|
||||
* Checks:
|
||||
* - window_width is within [ADAPTIVE_MIN_WINDOW_SIZE, ROLLING_WINDOW_SIZE]
|
||||
* - slope_q48 is reasonable (between 0.001 and 100 in Q48.16)
|
||||
* - variance and fit_quality are between 0.0 and 1.0 in Q48.16
|
||||
*
|
||||
* @param outputs Inference outputs to validate
|
||||
* @return 1 if valid, 0 if suspicious
|
||||
*/
|
||||
int inference_outputs_validate(const InferenceOutputs *outputs);
|
||||
|
||||
/* ============================================================================
|
||||
* Internal Functions (Exposed for Unit Testing)
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* @brief Compute variance of heat data in Q48.16 format
|
||||
*
|
||||
* @param heat_data Array of heat values
|
||||
* @param length Number of elements in array
|
||||
* @return Variance in Q48.16 format
|
||||
*/
|
||||
q48_16_t compute_variance_q48(const uint64_t *heat_data, uint64_t length);
|
||||
|
||||
/*
|
||||
* @brief Find variance inflection point for optimal window width
|
||||
*
|
||||
* @param heat_data Array of heat values
|
||||
* @param trajectory_length Total trajectory length
|
||||
* @param full_variance Variance of full trajectory
|
||||
* @return Optimal window width
|
||||
*/
|
||||
uint32_t find_variance_inflection(const uint64_t *heat_data, uint64_t trajectory_length, q48_16_t full_variance);
|
||||
|
||||
/*
|
||||
* @brief Infer decay slope from heat trajectory using linear regression
|
||||
*
|
||||
* @param heat_data Array of heat values
|
||||
* @param length Number of elements
|
||||
* @return Decay slope in Q48.16 format
|
||||
*/
|
||||
uint64_t infer_decay_slope_q48(const uint64_t *heat_data, uint64_t length);
|
||||
|
||||
#endif /* INFERENCE_ENGINE_H */
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
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 IO_H
|
||||
#define IO_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "vm.h"
|
||||
|
||||
/* BLOCK_SIZE is defined in vm.h */
|
||||
|
||||
/** @brief Total number of blocks available */
|
||||
#define BLOCK_COUNT 1024
|
||||
/** @brief Total memory size in bytes */
|
||||
#define MEMORY_SIZE (BLOCK_SIZE * BLOCK_COUNT)
|
||||
|
||||
/*
|
||||
* @brief Initialize the I/O subsystem
|
||||
* @param vm Pointer to the virtual machine instance
|
||||
*/
|
||||
void io_init(VM * vm);
|
||||
|
||||
/**
|
||||
* @brief Read a block from storage
|
||||
* @param vm Pointer to the virtual machine instance
|
||||
* @param block_num Block number to read
|
||||
* @param buffer Buffer to store the read data
|
||||
* @return 0 on success, non-zero on failure
|
||||
*/
|
||||
int io_read_block(VM *vm, int block_num, unsigned char *buffer);
|
||||
|
||||
/*
|
||||
* @brief Write a block to storage
|
||||
* @param vm Pointer to the virtual machine instance
|
||||
* @param block_num Block number to write
|
||||
* @param buffer Buffer containing data to write
|
||||
* @return 0 on success, non-zero on failure
|
||||
*/
|
||||
int io_write_block(VM *vm, int block_num, const unsigned char *buffer);
|
||||
|
||||
#endif /* IO_H */
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
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 LOG_H
|
||||
#define LOG_H
|
||||
|
||||
/* Maximum log line size for persistent logging */
|
||||
/* Renamed from LOG_LINE_MAX: vm.h owns that name for the persistent
|
||||
* block-log line width (64, unrelated concept). This is the in-memory log
|
||||
* message-formatting line length; keeping a distinct name removes the
|
||||
* include-order collision that forced a fragile "vm.h before log.h"
|
||||
* convention across the kernel (kernel_main.c/shim.c/capsule_birth.c). */
|
||||
#ifndef LOG_MSG_LINE_MAX
|
||||
#define LOG_MSG_LINE_MAX 256
|
||||
#endif
|
||||
|
||||
/* Forward declaration */
|
||||
struct VM;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Logging level enumeration
|
||||
*
|
||||
* Defines the available logging levels in order of increasing verbosity.
|
||||
*/
|
||||
typedef enum {
|
||||
LOG_NONE = -1, /**< Completely disable all logging */
|
||||
LOG_ERROR = 0, /**< Error messages only */
|
||||
LOG_WARN, /**< Warning and error messages */
|
||||
LOG_INFO, /**< Informational, warning and error messages */
|
||||
LOG_TEST, /**< Test results_run_01_2025_12_08 and all previous levels */
|
||||
LOG_DEBUG /**< Debug messages and all previous levels */
|
||||
} LogLevel;
|
||||
|
||||
/**
|
||||
* @brief Test result type enumeration
|
||||
*
|
||||
* Defines the possible outcomes of test execution.
|
||||
*/
|
||||
typedef enum {
|
||||
TEST_PASS = 0, /**< Test completed successfully */
|
||||
TEST_FAIL, /**< Test failed */
|
||||
TEST_SKIP, /**< Test was skipped */
|
||||
TEST_ERROR /**< Test encountered an error during execution */
|
||||
} TestResult;
|
||||
|
||||
/**
|
||||
* @brief Set the global logging level
|
||||
* @param level The new logging level to set
|
||||
* @note Default level is LOG_INFO
|
||||
*/
|
||||
void log_set_level(LogLevel level);
|
||||
|
||||
/**
|
||||
* @brief Get the current logging level
|
||||
* @return The current logging level
|
||||
*/
|
||||
LogLevel log_get_level(void);
|
||||
|
||||
/**
|
||||
* @brief Log a formatted message at the specified level
|
||||
* @param level The logging level for this message
|
||||
* @param fmt Printf-style format string
|
||||
* @param ... Variable arguments for format string
|
||||
*/
|
||||
void log_message(LogLevel level, const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* @brief Log a test result with colored output
|
||||
* @param word_name Name of the word being tested
|
||||
* @param result Result of the test execution
|
||||
*/
|
||||
void log_test_result(const char *word_name, TestResult result);
|
||||
|
||||
/**
|
||||
* @brief Set the VM instance for persistent logging
|
||||
* @param vm VM instance to use for persistent logging (NULL to disable)
|
||||
*/
|
||||
void log_set_vm(struct VM *vm);
|
||||
|
||||
#endif /* LOG_H */
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* math_portable.h
|
||||
*
|
||||
* Portable C99 math functions in Q48.16 64-bit fixed-point format.
|
||||
* Suitable for embedded environments with no libm
|
||||
* (HISTORICAL: also L4Re, through mid-2026; removed as an active target).
|
||||
* All arithmetic is integer-only for maximum performance.
|
||||
*
|
||||
* Q48.16 Format:
|
||||
* - 48-bit signed integer + 16-bit fractional part
|
||||
* - Precision: 2^-16 ≈ 0.0000153 (nanoseconds)
|
||||
* - Range: ±140 trillion ns (≈ 4.4 years)
|
||||
*
|
||||
* Functions:
|
||||
* sqrt_q48(x) - Square root of Q48.16 value via Newton-Raphson
|
||||
* erf_q48(x) - Error function of Q48.16 value (dimensionless)
|
||||
*/
|
||||
|
||||
#ifndef MATH_PORTABLE_H
|
||||
#define MATH_PORTABLE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define Q48_SCALE (1LL << 16) /* 65536 */
|
||||
|
||||
/**
|
||||
* Square root in Q48.16 fixed-point
|
||||
* Input: x in Q48.16 format
|
||||
* Output: sqrt(x) in Q48.16 format
|
||||
* Uses Newton-Raphson: x_{n+1} = (x_n + a/x_n) / 2
|
||||
* Converges in ~10-15 iterations
|
||||
* Returns 0 for negative inputs (guard against domain error)
|
||||
*/
|
||||
int64_t sqrt_q48(int64_t x);
|
||||
|
||||
/**
|
||||
* Error function in Q48.16 fixed-point
|
||||
* Input: x in Q48.16 format (dimensionless Z-score)
|
||||
* Output: erf(x) in Q48.16 format (range: -Q48_SCALE to +Q48_SCALE)
|
||||
* Uses Abramowitz & Stegun approximation (7.1.26)
|
||||
* Maximum error: 1.5e-7 relative to floating-point erf
|
||||
* Properties:
|
||||
* - erf(0) = 0
|
||||
* - erf(-x) = -erf(x) (odd function)
|
||||
* - erf(x) approaches ±Q48_SCALE for large |x|
|
||||
*/
|
||||
int64_t erf_q48(int64_t x);
|
||||
|
||||
#endif /* MATH_PORTABLE_H */
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file memory_management.h
|
||||
* @brief Memory management functions for the StarForth virtual machine
|
||||
*
|
||||
* This file contains functions for managing memory allocation and alignment
|
||||
* in the StarForth virtual machine implementation.
|
||||
*/
|
||||
|
||||
#ifndef VM_MEMORY_H
|
||||
#define VM_MEMORY_H
|
||||
#include "vm.h"
|
||||
|
||||
/*
|
||||
* @brief Allocates memory in the virtual machine's memory space
|
||||
* @param vm Pointer to the virtual machine instance
|
||||
* @param bytes Number of bytes to allocate
|
||||
* @return Pointer to the allocated memory or NULL if allocation fails
|
||||
*/
|
||||
void *vm_allot(VM *vm, size_t bytes);
|
||||
|
||||
/*
|
||||
* @brief Aligns the virtual machine's memory pointer to the next word boundary
|
||||
* @param vm Pointer to the virtual machine instance
|
||||
*/
|
||||
void vm_align(VM * vm);
|
||||
#endif
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
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 STARFORTH_PHYSICS_EXECUTION_HOOKS_H
|
||||
#define STARFORTH_PHYSICS_EXECUTION_HOOKS_H
|
||||
|
||||
/*
|
||||
* physics_execution_hooks.h - Clean abstraction for physics-driven execution hooks
|
||||
*
|
||||
* Purpose: Hide ALL physics machinery behind two simple function calls.
|
||||
* The VM execution loop calls these; it doesn't know or care about:
|
||||
* - Heat tracking
|
||||
* - Decay models
|
||||
* - Rolling window
|
||||
* - Pipelining metrics
|
||||
* - Hotwords cache
|
||||
* - Profiling
|
||||
*
|
||||
* This keeps vm.c clean and focused on execution semantics.
|
||||
*/
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
/*
|
||||
* Call BEFORE executing a word.
|
||||
* Handles: decay, heat increment, rolling window, pipelining speculation check.
|
||||
*
|
||||
* @param vm The VM instance
|
||||
* @param word The word about to execute
|
||||
* @param prev The previously executed word (NULL if first in sequence)
|
||||
*/
|
||||
void physics_pre_execute(VM* vm, DictEntry* word, DictEntry* prev);
|
||||
|
||||
/*
|
||||
* Call AFTER executing a word.
|
||||
* Handles: physics touch, profiler exit, DoE counter, heartbeat check.
|
||||
*
|
||||
* @param vm The VM instance
|
||||
* @param word The word that just executed
|
||||
*/
|
||||
void physics_post_execute(VM* vm, DictEntry* word);
|
||||
|
||||
/*
|
||||
* Call when looking up a word (outer interpreter).
|
||||
* Handles: decay + heat for lookup path (not inner loop).
|
||||
*
|
||||
* @param vm The VM instance
|
||||
* @param entry The word found
|
||||
* @param canon Canonical dictionary entry (may differ from entry)
|
||||
*/
|
||||
void physics_on_lookup(VM* vm, DictEntry* entry, DictEntry* canon);
|
||||
|
||||
#endif /* STARFORTH_PHYSICS_EXECUTION_HOOKS_H */
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* physics_hotwords_cache.h
|
||||
*
|
||||
* Hot-words cache optimization: frequency-driven dictionary acceleration
|
||||
*
|
||||
* Compile-time switch to enable/disable the hot-words cache variant.
|
||||
* Allows measuring impact before/after on different build profiles.
|
||||
*
|
||||
* Usage:
|
||||
* make clean && make ENABLE_HOTWORDS_CACHE=1 # With cache
|
||||
* make clean && make ENABLE_HOTWORDS_CACHE=0 # Without cache
|
||||
*
|
||||
* Measure performance:
|
||||
* BENCH-DICT-LOOKUP (show baseline + stats)
|
||||
* PHYSICS-SHOW-CACHE-STATS (show hit rate, evictions, etc)
|
||||
*/
|
||||
|
||||
#ifndef PHYSICS_HOTWORDS_CACHE_H
|
||||
#define PHYSICS_HOTWORDS_CACHE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "vm.h"
|
||||
|
||||
/* ============================================================================
|
||||
* Feature Switch
|
||||
* ============================================================================
|
||||
*
|
||||
* Set via -DENABLE_HOTWORDS_CACHE=1 during compilation
|
||||
* Default: enabled (can be disabled for baseline comparison)
|
||||
*/
|
||||
|
||||
#ifndef ENABLE_HOTWORDS_CACHE
|
||||
#define ENABLE_HOTWORDS_CACHE 1
|
||||
#endif
|
||||
|
||||
/* ============================================================================
|
||||
* Hot Words Cache Configuration
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/** Maximum number of hot words to cache (typically 16-32) */
|
||||
#define HOTWORDS_CACHE_SIZE 32
|
||||
|
||||
/** Execution heat threshold to consider a word "hot" (execution count) */
|
||||
#define HOTWORDS_EXECUTION_HEAT_THRESHOLD 50
|
||||
|
||||
/** Reorder bucket when execution heat delta exceeds this (avoid thrashing) */
|
||||
#define HOTWORDS_EXECUTION_HEAT_DELTA_THRESHOLD 100
|
||||
|
||||
/* ============================================================================
|
||||
* Statistics Structure
|
||||
* ============================================================================
|
||||
*
|
||||
* Tracks cache performance for measurement and diagnostics
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
/** Total lookups performed */
|
||||
uint64_t total_lookups;
|
||||
|
||||
/** Lookups that hit the hot-words cache */
|
||||
uint64_t cache_hits;
|
||||
|
||||
/** Lookups that missed cache but found in bucket */
|
||||
uint64_t bucket_hits;
|
||||
|
||||
/** Lookups that failed (word not found) */
|
||||
uint64_t misses;
|
||||
|
||||
/** Words evicted from cache (LRU) */
|
||||
uint64_t evictions;
|
||||
|
||||
/** Times a word was promoted to hot-words */
|
||||
uint64_t promotions;
|
||||
|
||||
/** Times bucket was reordered */
|
||||
uint64_t bucket_reorders;
|
||||
|
||||
/** === 64-bit Fixed-Point Statistics (Q48.16 format) ===
|
||||
* Q48.16 = 48-bit integer + 16-bit fractional part
|
||||
* Precision: 2^-16 ns ≈ 0.0000153 ns
|
||||
* Range: ±140 trillion ns (≈ 4.4 years)
|
||||
* Allows: Bayesian inference, precise averaging, statistical computation
|
||||
*/
|
||||
|
||||
/** Sum of cache hit latencies (64-bit fixed-point, Q48.16) */
|
||||
int64_t cache_hit_total_ns_q48;
|
||||
|
||||
/** Sum of bucket search latencies (64-bit fixed-point, Q48.16) */
|
||||
int64_t bucket_search_total_ns_q48;
|
||||
|
||||
/** Number of cache hit samples (for averaging) */
|
||||
uint64_t cache_hit_samples;
|
||||
|
||||
/** Number of bucket search samples (for averaging) */
|
||||
uint64_t bucket_search_samples;
|
||||
|
||||
/** Minimum cache hit latency (nanoseconds) */
|
||||
int64_t min_cache_hit_ns;
|
||||
|
||||
/** Maximum cache hit latency (nanoseconds) */
|
||||
int64_t max_cache_hit_ns;
|
||||
|
||||
/** Minimum bucket search latency (nanoseconds) */
|
||||
int64_t min_bucket_search_ns;
|
||||
|
||||
/** Maximum bucket search latency (nanoseconds) */
|
||||
int64_t max_bucket_search_ns;
|
||||
|
||||
/** Sum of squared cache hit latencies (for variance/stddev) */
|
||||
int64_t cache_hit_variance_sum_q48;
|
||||
|
||||
/** Sum of squared bucket search latencies (for variance/stddev) */
|
||||
int64_t bucket_search_variance_sum_q48;
|
||||
} HotwordsStats;
|
||||
|
||||
/* ============================================================================
|
||||
* Hot Words Cache State
|
||||
* ============================================================================
|
||||
*
|
||||
* Embedded in VM for global state
|
||||
*/
|
||||
|
||||
typedef struct HotwordsCache_s {
|
||||
/** Cache entries (pointers to DictEntry) */
|
||||
DictEntry *cache[HOTWORDS_CACHE_SIZE];
|
||||
|
||||
/** Number of valid entries in cache */
|
||||
uint32_t cache_count;
|
||||
|
||||
/** LRU: last-used index for eviction */
|
||||
uint32_t lru_index;
|
||||
|
||||
/** Performance statistics */
|
||||
HotwordsStats stats;
|
||||
|
||||
/** Enabled/disabled flag (runtime toggle) */
|
||||
int enabled;
|
||||
} HotwordsCache;
|
||||
|
||||
/* ============================================================================
|
||||
* Public API
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize hot-words cache
|
||||
* Should be called during VM initialization
|
||||
*/
|
||||
void hotwords_cache_init(HotwordsCache *cache);
|
||||
|
||||
/**
|
||||
* Cleanup hot-words cache
|
||||
*/
|
||||
void hotwords_cache_cleanup(HotwordsCache *cache);
|
||||
|
||||
/**
|
||||
* Lookup word in cache first, then fall back to bucket search
|
||||
* Returns the found word, or NULL if not found
|
||||
*
|
||||
* This is the main function that replaces vm_find_word() logic.
|
||||
* Takes the VM so the bucket fallback can resolve same-named entries by
|
||||
* definition age (vm_dict_resolve_in_bucket) — bucket order alone is not
|
||||
* authoritative once dict_reorganize_buckets_by_heat() has run.
|
||||
*/
|
||||
DictEntry *hotwords_cache_lookup(VM *vm,
|
||||
HotwordsCache *cache,
|
||||
DictEntry **bucket,
|
||||
size_t bucket_count,
|
||||
const char *name,
|
||||
size_t len);
|
||||
|
||||
/**
|
||||
* Promote a word to the hot-words cache
|
||||
* Called when a word's execution heat exceeds threshold
|
||||
*/
|
||||
void hotwords_cache_promote(HotwordsCache *cache, DictEntry *word);
|
||||
|
||||
/**
|
||||
* Evict every cache slot holding this exact entry.
|
||||
* Must be called before an entry is freed (FORGET) — a stale pointer in the
|
||||
* ring is a use-after-free.
|
||||
*/
|
||||
void hotwords_cache_evict_entry(HotwordsCache *cache, const DictEntry *word);
|
||||
|
||||
/**
|
||||
* Evict every cache slot whose entry matches this name.
|
||||
* Called whenever name resolution may change: word creation (shadowing),
|
||||
* smudge/unsmudge, hide. The next lookup re-resolves through the bucket.
|
||||
*/
|
||||
void hotwords_cache_evict_name(HotwordsCache *cache, const char *name, size_t len);
|
||||
|
||||
/**
|
||||
* Reorder a bucket by execution heat (move hot words forward)
|
||||
* Called when execution heat delta exceeds threshold
|
||||
*/
|
||||
void hotwords_bucket_reorder(DictEntry **bucket, size_t count);
|
||||
|
||||
/**
|
||||
* Reset statistics (for before/after comparison)
|
||||
*/
|
||||
void hotwords_stats_reset(HotwordsStats *stats);
|
||||
|
||||
/**
|
||||
* Print statistics to stdout
|
||||
*/
|
||||
void hotwords_stats_print(const HotwordsStats *stats);
|
||||
|
||||
/**
|
||||
* Enable/disable cache at runtime (for A/B testing)
|
||||
*/
|
||||
void hotwords_cache_set_enabled(HotwordsCache *cache, int enabled);
|
||||
|
||||
/* ============================================================================
|
||||
* Bayesian Inference Statistics
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Bayesian statistics structure for inference and credible intervals
|
||||
* All values stored in Q48.16 fixed-point (64-bit signed integer)
|
||||
* representing nanoseconds with 2^-16 ≈ 0.0000153 ns precision
|
||||
*/
|
||||
typedef struct {
|
||||
int64_t mean_ns_q48; /* Mean latency (Q48.16) */
|
||||
int64_t stddev_ns_q48; /* Standard deviation (Q48.16) */
|
||||
int64_t median_ns_q48; /* Median (p50) (Q48.16) */
|
||||
int64_t credible_lower_95; /* Lower bound of 95% credible interval (Q48.16) */
|
||||
int64_t credible_upper_95; /* Upper bound of 95% credible interval (Q48.16) */
|
||||
int64_t credible_lower_99; /* Lower bound of 99% credible interval (Q48.16) */
|
||||
int64_t credible_upper_99; /* Upper bound of 99% credible interval (Q48.16) */
|
||||
uint64_t sample_count; /* Number of samples in posterior */
|
||||
} BayesianLatencyPosterior;
|
||||
|
||||
/**
|
||||
* Calculate posterior distribution for cache hits
|
||||
* Uses accumulated samples to compute credible intervals
|
||||
*/
|
||||
BayesianLatencyPosterior hotwords_posterior_cache_hits(const HotwordsStats *stats);
|
||||
|
||||
/**
|
||||
* Calculate posterior distribution for bucket searches
|
||||
* Uses accumulated samples to compute credible intervals
|
||||
*/
|
||||
BayesianLatencyPosterior hotwords_posterior_bucket_searches(const HotwordsStats *stats);
|
||||
|
||||
/**
|
||||
* Calculate speedup estimate with credible interval
|
||||
* All values in Q48.16 fixed-point format (64-bit signed integer)
|
||||
* Probabilities are in range [0, Q48_SCALE] representing [0%, 100%]
|
||||
*/
|
||||
struct SpeedupEstimate {
|
||||
int64_t speedup_factor_q48; /* Bucket latency / Cache latency (Q48.16) */
|
||||
int64_t credible_lower_95_q48; /* Lower bound (95% credible) (Q48.16) */
|
||||
int64_t credible_upper_95_q48; /* Upper bound (95% credible) (Q48.16) */
|
||||
int64_t probability_gt_10pct_q48; /* P(speedup > 1.1) in Q48.16, [0, 65536] = [0%, 100%] */
|
||||
int64_t probability_gt_double_q48;/* P(speedup > 2.0) in Q48.16, [0, 65536] = [0%, 100%] */
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate speedup estimate with uncertainty
|
||||
*/
|
||||
struct SpeedupEstimate hotwords_speedup_estimate(const HotwordsStats *stats);
|
||||
|
||||
/**
|
||||
* Generate detailed Bayesian inference report
|
||||
* Compares cache vs bucket performance with confidence measures
|
||||
*/
|
||||
void hotwords_bayesian_report(const HotwordsStats *stats_with_cache,
|
||||
const HotwordsStats *stats_without_cache);
|
||||
|
||||
#endif /* PHYSICS_HOTWORDS_CACHE_H */
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
*** StarForth ***
|
||||
|
||||
physics_metadata.h - Physics metadata helpers for dictionary entries
|
||||
|
||||
Phase 1 implements lightweight thermodynamic signals that inform scheduling
|
||||
and storage placement while keeping the runtime overhead minimal.
|
||||
*/
|
||||
|
||||
#ifndef STARFORTH_PHYSICS_METADATA_H
|
||||
#define STARFORTH_PHYSICS_METADATA_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "vm.h"
|
||||
|
||||
/* L8 FINAL INTEGRATION: L1 heat tracking always-on */
|
||||
|
||||
/* ============================================================================
|
||||
* INTENT: Atomic heat increment for FL1 feedback loop
|
||||
* FL1: Heat accumulation - called on every word execution by inner interpreter
|
||||
* WHY: Atomic operations prevent race conditions when multiple execution paths
|
||||
* (e.g., heartbeat thread + main interpreter) touch execution_heat
|
||||
* RELAXED memory order is sufficient (no ordering dependencies)
|
||||
* ============================================================================ */
|
||||
static inline void physics_execution_heat_increment(DictEntry *entry)
|
||||
{
|
||||
if (!entry) return;
|
||||
#if defined(__GNUC__)
|
||||
__atomic_fetch_add(&entry->execution_heat, 1, __ATOMIC_RELAXED);
|
||||
#else
|
||||
entry->execution_heat++;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline cell_t physics_execution_heat_load(const DictEntry *entry)
|
||||
{
|
||||
if (!entry) return 0;
|
||||
#if defined(__GNUC__)
|
||||
return __atomic_load_n(&entry->execution_heat, __ATOMIC_RELAXED);
|
||||
#else
|
||||
return entry->execution_heat;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint64_t physics_decay_slope_load(const VM *vm)
|
||||
{
|
||||
#if defined(__GNUC__)
|
||||
return __atomic_load_n(&vm->decay_slope_q48, __ATOMIC_RELAXED);
|
||||
#else
|
||||
return vm->decay_slope_q48;
|
||||
#endif
|
||||
}
|
||||
|
||||
void physics_metadata_init(DictEntry *entry, uint32_t header_bytes);
|
||||
|
||||
void physics_metadata_set_mass(DictEntry *entry, uint32_t total_bytes);
|
||||
|
||||
void physics_metadata_touch(DictEntry *entry, cell_t execution_heat, uint64_t now_ns);
|
||||
|
||||
void physics_metadata_refresh_state(DictEntry * entry);
|
||||
|
||||
void physics_metadata_record_latency(DictEntry *entry, uint64_t sample_ns);
|
||||
|
||||
void physics_metadata_apply_seed(DictEntry * entry);
|
||||
|
||||
/* L8 FINAL INTEGRATION: L3 linear decay always-on */
|
||||
void physics_metadata_apply_linear_decay(DictEntry *entry, uint64_t elapsed_ticks, VM *vm);
|
||||
|
||||
#endif /* STARFORTH_PHYSICS_METADATA_H */
|
||||
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
*** StarForth ***
|
||||
|
||||
physics_pipelining_metrics.h - Word transition tracking for speculative execution
|
||||
|
||||
Phase 1: Instrumentation for pipelining/speculative execution
|
||||
|
||||
This module tracks word-to-word transitions (frequency of which words follow
|
||||
which other words) to support speculative prefetching and pipelining decisions.
|
||||
|
||||
Terminology:
|
||||
- transition_heat[i]: How many times word[i] has been executed immediately
|
||||
after this word (forms prediction basis)
|
||||
- total_transitions: Sum of all transition_heat entries
|
||||
- prefetch_*: Metrics for speculative execution success/failure
|
||||
|
||||
*/
|
||||
|
||||
#ifndef PHYSICS_PIPELINING_METRICS_H
|
||||
#define PHYSICS_PIPELINING_METRICS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "starforth_config.h"
|
||||
|
||||
/* ============================================================================
|
||||
* Pipelining Feature Switch (disabled by default during Phase 1)
|
||||
* ============================================================================
|
||||
* Default lives in starforth_config.h, the single source of truth for VM
|
||||
* build-time defaults; overridable via -D exactly as before.
|
||||
*/
|
||||
|
||||
/* ============================================================================
|
||||
* Tuning Knobs (adjustable at compile-time, future: runtime)
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* SPECULATION_THRESHOLD_Q48: Minimum probability (Q48.16) to speculate on
|
||||
* a word transition.
|
||||
* Range: 0.10 (very aggressive) to 0.95 (very conservative)
|
||||
* Default: see starforth_config.h (STARFORTH_CONFIG_SPECULATION_THRESHOLD_Q48_DEFAULT)
|
||||
* Q48.16 encoding: 0.50 = 0x8000LL (1LL << 15)
|
||||
*/
|
||||
|
||||
/**
|
||||
* SPECULATION_DEPTH: How many words ahead to prefetch speculatively.
|
||||
* Range: 1 (next word only) to 4 (very aggressive)
|
||||
* Default: see starforth_config.h (STARFORTH_CONFIG_SPECULATION_DEPTH_DEFAULT)
|
||||
*/
|
||||
|
||||
/**
|
||||
* MIN_SAMPLES_FOR_SPECULATION: Minimum number of transitions observed
|
||||
* before making speculation decisions.
|
||||
* Range: 1 (immediate) to 100 (very conservative)
|
||||
* Default: see starforth_config.h (STARFORTH_CONFIG_MIN_SAMPLES_FOR_SPECULATION_DEFAULT)
|
||||
*/
|
||||
|
||||
/**
|
||||
* MISPREDICTION_COST_Q48: Estimated cost (in nanoseconds, Q48.16 format)
|
||||
* of recovering from wrong speculation.
|
||||
* This is calibrated to the actual recovery time on target hardware.
|
||||
* Range: 0 to 100 ns
|
||||
* Default: see starforth_config.h (STARFORTH_CONFIG_MISPREDICTION_COST_Q48_DEFAULT)
|
||||
* Q48.16 encoding: 25 ns = 25 << 16 = 0x190000LL
|
||||
*/
|
||||
|
||||
/**
|
||||
* MINIMUM_PREFETCH_ROI: Minimum expected Return-On-Investment ratio
|
||||
* for speculation to be worthwhile.
|
||||
* Speculation succeeds only if:
|
||||
* (prefetch_latency_saved / total_attempts) > MINIMUM_PREFETCH_ROI
|
||||
* Range: 1.0 (always speculate) to 2.0 (very selective)
|
||||
* Default: see starforth_config.h (STARFORTH_CONFIG_MINIMUM_PREFETCH_ROI_DEFAULT)
|
||||
* Q48.16 encoding: 1.10 = 1.10 * (1 << 16) = 0x1199AL
|
||||
*/
|
||||
|
||||
/**
|
||||
* TRANSITION_WINDOW_SIZE: Knob #6 - Execution context depth for prediction
|
||||
*
|
||||
* How many previous words to remember when predicting next word.
|
||||
* Larger window captures deeper patterns but uses more memory.
|
||||
*
|
||||
* Window=1: Only immediate predecessor (A→B predictions)
|
||||
* Window=2: Previous 2 words (A,B→C predictions) - good balance
|
||||
* Window=4: Previous 4 words (captures multi-level patterns)
|
||||
* Window=8: Very deep, expensive -- see starforth_config.h for the actual
|
||||
* shipped default
|
||||
*
|
||||
* Binary Chop Search Space (Phase 2):
|
||||
* Iteration 1: Try window=2 (baseline)
|
||||
* Iteration 2: Try window=1 and window=4
|
||||
* Iteration 3: Narrow to optimal (1, 2, or 4)
|
||||
* Converge: Pick size with highest prediction accuracy
|
||||
*
|
||||
* Range: 1 to 8
|
||||
* Default: see starforth_config.h (STARFORTH_CONFIG_TRANSITION_WINDOW_SIZE_DEFAULT)
|
||||
* Trade-off: Pattern depth vs. memory usage & complexity
|
||||
*
|
||||
* Tuning: make TRANSITION_WINDOW_SIZE=1 (or 2, 4, 8)
|
||||
*/
|
||||
|
||||
/* ============================================================================
|
||||
* Word Transition Metrics
|
||||
* ============================================================================
|
||||
*
|
||||
* Allocated per dictionary entry to track which words follow this word.
|
||||
* This is the basis for speculative prefetching decisions.
|
||||
*/
|
||||
|
||||
typedef struct WordTransitionMetrics {
|
||||
/**
|
||||
* transition_heat[i]: Number of times word with index i has been
|
||||
* executed immediately after this word.
|
||||
*
|
||||
* Dynamically allocated array of size DICTIONARY_SIZE.
|
||||
* Initialized to NULL; allocated on first use to save memory
|
||||
* for words that are never executed.
|
||||
*/
|
||||
uint64_t *transition_heat;
|
||||
|
||||
/** Total number of transitions observed from this word */
|
||||
uint64_t total_transitions;
|
||||
|
||||
/** Number of speculative prefetch attempts made for this word */
|
||||
uint64_t prefetch_attempts;
|
||||
|
||||
/** Number of successful prefetch predictions (hits) */
|
||||
uint64_t prefetch_hits;
|
||||
|
||||
/** Number of failed prefetch predictions (misses) */
|
||||
uint64_t prefetch_misses;
|
||||
|
||||
/**
|
||||
* Total latency saved through successful prefetching (Q48.16 nanoseconds).
|
||||
* Positive value indicates net benefit from speculation.
|
||||
*/
|
||||
int64_t prefetch_latency_saved_q48;
|
||||
|
||||
/**
|
||||
* Total latency cost from failed prefetch predictions (Q48.16 nanoseconds).
|
||||
* Negative value (cost) to be subtracted from savings.
|
||||
*/
|
||||
int64_t misprediction_cost_q48;
|
||||
|
||||
/**
|
||||
* Cached probability of most likely next word (for quick access).
|
||||
* Updated periodically to avoid recomputation.
|
||||
* Q48.16 format (0.0 to 1.0 == 0LL to 1LL<<16)
|
||||
*/
|
||||
int64_t max_transition_probability_q48;
|
||||
|
||||
/**
|
||||
* Index (word id) of most likely next word.
|
||||
* Used for fast prefetch without scanning transition_heat array.
|
||||
*/
|
||||
uint32_t most_likely_next_word_id;
|
||||
|
||||
/* ========== Context-Aware Transitions (Phase 1 Extension) ========== */
|
||||
|
||||
/**
|
||||
* Circular buffer of previous word IDs for context tracking.
|
||||
* Size = TRANSITION_WINDOW_SIZE (default 2)
|
||||
* Used to build multi-word context patterns (e.g., A,B→C)
|
||||
*
|
||||
* Example with WINDOW=2:
|
||||
* context_window[0] = older word (2 steps back)
|
||||
* context_window[1] = recent word (1 step back)
|
||||
* Next word completes the pattern: (context_window[0], context_window[1]) → next
|
||||
*/
|
||||
uint32_t *context_window;
|
||||
|
||||
/**
|
||||
* Current position in context_window circular buffer.
|
||||
* Points to slot where next word ID will be stored.
|
||||
*/
|
||||
uint32_t context_window_pos;
|
||||
|
||||
/**
|
||||
* Sparse hash table for context-based transitions.
|
||||
* Maps: hash(context_window) → counts_array[DICTIONARY_SIZE]
|
||||
*
|
||||
* Phase 1: Collect only (simple reference implementation)
|
||||
* Phase 2: Analyze prediction accuracy by context depth
|
||||
* Phase 3: Use for adaptive prefetching decisions
|
||||
*
|
||||
* Opaque pointer - implementation is in transition_metrics.c
|
||||
*/
|
||||
void *context_transitions;
|
||||
|
||||
/**
|
||||
* Total context-based transitions observed.
|
||||
* Counter for Phase 2 analysis (accuracy measurement)
|
||||
*/
|
||||
uint64_t total_context_transitions;
|
||||
|
||||
/**
|
||||
* Actual window size for this word's metrics.
|
||||
* May differ from TRANSITION_WINDOW_SIZE if memory allocation failed.
|
||||
* Phase 2 uses this to validate context data reliability.
|
||||
*/
|
||||
uint32_t actual_window_size;
|
||||
|
||||
} WordTransitionMetrics;
|
||||
|
||||
/* ============================================================================
|
||||
* Transition Metrics API
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Initialize word transition metrics for a dictionary entry.
|
||||
* Called once per word during dictionary entry creation.
|
||||
*
|
||||
* @param metrics Pointer to uninitialized WordTransitionMetrics
|
||||
* @return 0 on success, -1 on memory allocation failure
|
||||
*/
|
||||
int transition_metrics_init(WordTransitionMetrics *metrics);
|
||||
|
||||
/*
|
||||
* Record a word transition (word_from -> word_to).
|
||||
* Called in the inner interpreter after each word execution.
|
||||
*
|
||||
* @param metrics Metrics for the current word
|
||||
* @param next_word_id Index of word that executed next
|
||||
* @param dict_size Total number of words in dictionary (for array bounds)
|
||||
* @return 0 on success, -1 on invalid word_id
|
||||
*/
|
||||
int transition_metrics_record(WordTransitionMetrics *metrics, uint32_t next_word_id, uint32_t dict_size);
|
||||
|
||||
/*
|
||||
* Get the probability (Q48.16 format) of a specific word following this word.
|
||||
*
|
||||
* Returns: (transition_heat[target] / total_transitions) in Q48.16 format
|
||||
* or 0 if total_transitions == 0
|
||||
*
|
||||
* @param metrics Metrics for the current word
|
||||
* @param target_word_id Index of potential next word
|
||||
* @return Probability in Q48.16 format (0.0 to 1.0)
|
||||
*/
|
||||
int64_t transition_metrics_get_probability_q48(const WordTransitionMetrics *metrics, uint32_t target_word_id);
|
||||
|
||||
/*
|
||||
* Update cached most-likely-next-word information.
|
||||
* Should be called periodically (e.g., after every 100 transitions)
|
||||
* to avoid stale cache.
|
||||
*
|
||||
* @param metrics Metrics for the current word
|
||||
* @param dict_size Total number of words in dictionary
|
||||
*/
|
||||
void transition_metrics_update_cache(WordTransitionMetrics *metrics, uint32_t dict_size);
|
||||
|
||||
/*
|
||||
* Record a successful prefetch prediction.
|
||||
* Called when speculation correctly predicted the next word.
|
||||
*
|
||||
* @param metrics Metrics for the current word
|
||||
* @param latency_saved_ns Nanoseconds saved (will be Q48.16 encoded)
|
||||
*/
|
||||
void transition_metrics_record_prefetch_hit(WordTransitionMetrics *metrics, int64_t latency_saved_ns);
|
||||
|
||||
/*
|
||||
* Record a failed prefetch prediction.
|
||||
* Called when speculation incorrectly predicted the next word.
|
||||
*
|
||||
* @param metrics Metrics for the current word
|
||||
* @param recovery_cost_ns Nanoseconds lost to misprediction recovery
|
||||
*/
|
||||
void transition_metrics_record_prefetch_miss(WordTransitionMetrics *metrics, int64_t recovery_cost_ns);
|
||||
|
||||
/*
|
||||
* Check if speculation should be attempted for a given target word.
|
||||
* Applies threshold logic and ROI analysis.
|
||||
*
|
||||
* Returns: 1 if speculation should be attempted, 0 otherwise
|
||||
*
|
||||
* @param metrics Metrics for the current word
|
||||
* @param target_word_id Index of potential next word
|
||||
* @return 1 if probability > SPECULATION_THRESHOLD and enough samples, 0 otherwise
|
||||
*/
|
||||
int transition_metrics_should_speculate(const WordTransitionMetrics *metrics, uint32_t target_word_id);
|
||||
|
||||
/*
|
||||
* Get detailed statistics string for diagnostics.
|
||||
* Caller must free the returned string.
|
||||
*
|
||||
* @param metrics Metrics to report on
|
||||
* @return Formatted statistics string (must be freed by caller)
|
||||
*/
|
||||
char *transition_metrics_stats_string(const WordTransitionMetrics *metrics);
|
||||
|
||||
/*
|
||||
* Reset all transition metrics (clear counts, free transition_heat array).
|
||||
* Called on VM reset or explicit metrics reset.
|
||||
*
|
||||
* @param metrics Metrics to reset
|
||||
*/
|
||||
void transition_metrics_reset(WordTransitionMetrics *metrics);
|
||||
|
||||
/*
|
||||
* Free all allocated memory for transition metrics.
|
||||
* Called during dictionary cleanup.
|
||||
*
|
||||
* @param metrics Metrics to clean up
|
||||
*/
|
||||
void transition_metrics_cleanup(WordTransitionMetrics *metrics);
|
||||
|
||||
/* ============================================================================
|
||||
* Context-Aware Transition API (Phase 1 Extension)
|
||||
* ============================================================================
|
||||
*
|
||||
* These functions handle multi-word pattern tracking for Phase 2 analysis.
|
||||
* Phase 1: Collect context window data (this session)
|
||||
* Phase 2: Analyze accuracy by context depth (next session)
|
||||
* Phase 3: Use for adaptive prediction (future)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Record a context-based transition (multi-word pattern).
|
||||
* Called after each word execution with the execution window.
|
||||
*
|
||||
* Example with WINDOW=2:
|
||||
* context[0] = word_A, context[1] = word_B, next_word = word_C
|
||||
* Records: (A,B) → C transition in sparse hash table
|
||||
*
|
||||
* Phase 1: Just collects data, no analysis
|
||||
* Phase 2: Will measure: "Do patterns with window=2 predict better than window=1?"
|
||||
*
|
||||
* @param metrics Metrics for the current word
|
||||
* @param context Array of word IDs forming the context (size = window_size)
|
||||
* @param window_size Length of context array
|
||||
* @param next_word_id Word that follows the context
|
||||
* @param dict_size Total number of words in dictionary
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int transition_metrics_record_context(WordTransitionMetrics *metrics,
|
||||
const uint32_t *context,
|
||||
uint32_t window_size,
|
||||
uint32_t next_word_id,
|
||||
uint32_t dict_size);
|
||||
|
||||
/*
|
||||
* Update the execution context window with a new word.
|
||||
* Called after each word execution to slide the window forward.
|
||||
*
|
||||
* Example with WINDOW=2:
|
||||
* Before: [A, B] (two previous words)
|
||||
* After calling with C: [B, C] (shift left, add C on right)
|
||||
*
|
||||
* @param metrics Metrics to update
|
||||
* @param word_id New word ID to add to window
|
||||
* @return 0 on success, -1 if context tracking not initialized
|
||||
*/
|
||||
int transition_metrics_update_context_window(WordTransitionMetrics *metrics, uint32_t word_id);
|
||||
|
||||
/*
|
||||
* Get context-based prediction accuracy statistics (Phase 2).
|
||||
* Compares prediction success rates across different window sizes.
|
||||
*
|
||||
* Phase 2 will use this to run binary chop tuning:
|
||||
* Measure accuracy(window=1), accuracy(window=2), accuracy(window=4)
|
||||
* Binary search converges to optimal window size
|
||||
*
|
||||
* @param metrics Metrics to analyze
|
||||
* @return Formatted string with accuracy statistics (caller must free)
|
||||
*/
|
||||
char *transition_metrics_context_accuracy_string(const WordTransitionMetrics *metrics);
|
||||
|
||||
/*
|
||||
* Get recommended window size based on collected data.
|
||||
* Phase 2 will call this as part of binary chop tuning algorithm.
|
||||
*
|
||||
* Algorithm:
|
||||
* 1. Collect data with current window size
|
||||
* 2. Measure prediction accuracy
|
||||
* 3. If accuracy improves, try larger window
|
||||
* 4. If accuracy declines, try smaller window
|
||||
* 5. Converge to optimal size
|
||||
*
|
||||
* @param metrics Metrics to analyze
|
||||
* @param current_window Current window size being tested
|
||||
* @param accuracy_at_current Measured accuracy with current window
|
||||
* @return Suggested next window size to try (1, 2, 4, or 8)
|
||||
*/
|
||||
uint32_t transition_metrics_binary_chop_suggest_window(const WordTransitionMetrics *metrics,
|
||||
uint32_t current_window,
|
||||
double accuracy_at_current);
|
||||
|
||||
#endif /* PHYSICS_PIPELINING_METRICS_H */
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
*** StarForth ***
|
||||
|
||||
physics_runtime.h - Phase 1 physics runtime services
|
||||
|
||||
Provides host snapshot shims (POSIX + L4Re) and analytics heap scaffolding
|
||||
for physics-aware scheduling. The interface is intentionally lightweight so
|
||||
future governance-approved modules can extend it without ABI churn.
|
||||
|
||||
============================================================================
|
||||
IMPORTANT SYNC NOTE (projects using git hooks / governance mirror):
|
||||
• If you change HOLA protocol fields, magic, or version numbers below, also
|
||||
update the governance export docs so the L4Re mirror stays aligned.
|
||||
• Daemons touching this header can throw a warning when HOLA_PROTOCOL_* or
|
||||
HOLA_SHARED_MAGIC change to catch accidental ABI drifts early.
|
||||
• Reference: docs/src/internal/GOVERNANCE_EXPORT_NOTES.adoc
|
||||
============================================================================
|
||||
*/
|
||||
|
||||
#ifndef STARFORTH_PHYSICS_RUNTIME_H
|
||||
#define STARFORTH_PHYSICS_RUNTIME_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Default analytics heap size (10 MiB) */
|
||||
#define PHYSICS_ANALYTICS_DEFAULT_HEAP_BYTES (10u * 1024u * 1024u)
|
||||
|
||||
/** Magic tag for HOLA analytics heap headers ('HOLA') */
|
||||
#define HOLA_SHARED_MAGIC 0x484f4c41u
|
||||
|
||||
/** HOLA protocol version (major.minor) */
|
||||
#define HOLA_PROTOCOL_MAJOR 0x0001u
|
||||
#define HOLA_PROTOCOL_MINOR 0x0000u
|
||||
|
||||
/** Host backend identifier */
|
||||
typedef enum physics_host_backend {
|
||||
PHYSICS_HOST_BACKEND_POSIX = 0,
|
||||
PHYSICS_HOST_BACKEND_L4RE = 1, /* HISTORICAL: no backend sets this anymore; L4Re removed as an active target. Value kept stable for the HOLA analytics data format. */
|
||||
PHYSICS_HOST_BACKEND_FAKE = 2
|
||||
} physics_host_backend_t;
|
||||
|
||||
/** Analytics channel ids */
|
||||
#define PHYSICS_ANALYTICS_CHANNEL_HOST_SNAPSHOT 0x00000002u
|
||||
|
||||
/** Host snapshot flag bits */
|
||||
#define PHYSICS_HOST_FLAG_PSI 0x0001u
|
||||
#define PHYSICS_HOST_FLAG_CGROUP 0x0002u
|
||||
#define PHYSICS_HOST_FLAG_CPU_STAT 0x0004u
|
||||
|
||||
/**
|
||||
* @brief Snapshot of scheduler-adjacent host signals.
|
||||
*
|
||||
* Fields are intentionally fixed width to maximise portability and allow the
|
||||
* governance repository to consume binary dumps without per-platform quirks.
|
||||
*/
|
||||
typedef struct physics_host_snapshot {
|
||||
physics_host_backend_t backend; /**< Selected backend (POSIX/L4Re/other) */
|
||||
uint32_t scheduler_policy; /**< OS-specific scheduler policy identifier */
|
||||
uint32_t scheduler_priority; /**< Nominal thread priority */
|
||||
uint32_t scheduler_quantum_ns; /**< Reported quantum in nanoseconds (0 if unknown) */
|
||||
uint32_t runnable_threads; /**< Runnable entities on this CPU set (best effort) */
|
||||
uint32_t cpu_count; /**< Online logical CPU count */
|
||||
uint32_t load_avg_milli; /**< Smoothed load average (×1000) */
|
||||
uint32_t flags; /**< Backend-specific flags */
|
||||
uint32_t psi_cpu_some_avg10_milli; /**< CPU PSI some avg10 ×1000 */
|
||||
uint32_t psi_cpu_some_avg60_milli; /**< CPU PSI some avg60 ×1000 */
|
||||
uint32_t psi_cpu_some_avg300_milli; /**< CPU PSI some avg300 ×1000 */
|
||||
uint32_t psi_cpu_full_avg10_milli; /**< CPU PSI full avg10 ×1000 */
|
||||
uint32_t psi_cpu_full_avg60_milli; /**< CPU PSI full avg60 ×1000 */
|
||||
uint32_t psi_cpu_full_avg300_milli; /**< CPU PSI full avg300 ×1000 */
|
||||
uint32_t psi_io_some_avg10_milli; /**< IO PSI some avg10 ×1000 */
|
||||
uint32_t psi_io_some_avg60_milli; /**< IO PSI some avg60 ×1000 */
|
||||
uint32_t psi_io_some_avg300_milli; /**< IO PSI some avg300 ×1000 */
|
||||
uint32_t psi_io_full_avg10_milli; /**< IO PSI full avg10 ×1000 */
|
||||
uint32_t psi_io_full_avg60_milli; /**< IO PSI full avg60 ×1000 */
|
||||
uint32_t psi_io_full_avg300_milli; /**< IO PSI full avg300 ×1000 */
|
||||
uint32_t psi_mem_some_avg10_milli; /**< Memory PSI some avg10 ×1000 */
|
||||
uint32_t psi_mem_some_avg60_milli; /**< Memory PSI some avg60 ×1000 */
|
||||
uint32_t psi_mem_some_avg300_milli; /**< Memory PSI some avg300 ×1000 */
|
||||
uint64_t monotonic_time_ns; /**< Monotonic clock reading */
|
||||
uint64_t realtime_ns; /**< Wall-clock timestamp (0 if unavailable) */
|
||||
uint64_t backend_seq; /**< Backend-provided sequence counter */
|
||||
uint64_t cpu_total_jiffies; /**< /proc/stat total jiffies snapshot */
|
||||
uint64_t cpu_idle_jiffies; /**< /proc/stat idle jiffies snapshot */
|
||||
uint64_t cgroup_cpu_usage_us; /**< cgroup v2 cpu.stat usage_usec (0 if unavailable) */
|
||||
uint64_t cgroup_memory_current_bytes; /**< cgroup v2 memory.current (0 if unavailable) */
|
||||
} physics_host_snapshot_t;
|
||||
|
||||
/** Event header written into the analytics ring buffer. */
|
||||
typedef struct physics_analytics_event_header {
|
||||
uint32_t channel; /**< Logical channel identifier */
|
||||
uint16_t payload_bytes; /**< Size of event payload */
|
||||
uint16_t reserved; /**< Reserved for alignment */
|
||||
uint64_t timestamp_ns; /**< Producer timestamp */
|
||||
} physics_analytics_event_header_t;
|
||||
|
||||
/** Analytics heap header seen by both producer (VM) and consumer (HOLA). */
|
||||
typedef struct physics_analytics_header {
|
||||
uint32_t magic; /**< HOLA_SHARED_MAGIC */
|
||||
uint16_t version_major; /**< Protocol major version */
|
||||
uint16_t version_minor; /**< Protocol minor version */
|
||||
uint32_t heap_bytes; /**< Total heap size */
|
||||
uint32_t ring_offset; /**< Offset to event ring */
|
||||
uint32_t ring_bytes; /**< Event ring length */
|
||||
uint32_t summary_offset; /**< Offset to summary/scratch region */
|
||||
uint32_t summary_bytes; /**< Summary region length */
|
||||
uint32_t scratch_offset; /**< Offset to reserved scratch area */
|
||||
uint32_t scratch_bytes; /**< Scratch area length */
|
||||
uint64_t produce_seq; /**< Monotonic sequence for producers */
|
||||
uint64_t consume_seq; /**< Consumer-visible sequence */
|
||||
uint32_t write_offset; /**< Producer write cursor */
|
||||
uint32_t read_offset; /**< Consumer read cursor */
|
||||
uint32_t dropped_events; /**< Count of dropped events */
|
||||
uint32_t flags; /**< Heap flags (see docs) */
|
||||
} physics_analytics_header_t;
|
||||
|
||||
/** Simple view onto the analytics heap layout. */
|
||||
typedef struct physics_analytics_heap {
|
||||
uint8_t *base; /**< Base pointer (owned by runtime) */
|
||||
size_t bytes; /**< Total bytes reserved */
|
||||
physics_analytics_header_t *header; /**< Shared header */
|
||||
uint8_t *ring; /**< Event ring base */
|
||||
size_t ring_bytes; /**< Event ring span */
|
||||
uint8_t *summary; /**< Summary/statistics region */
|
||||
size_t summary_bytes; /**< Summary region span */
|
||||
uint8_t *scratch; /**< Scratch/reserved region */
|
||||
size_t scratch_bytes; /**< Scratch region span */
|
||||
} physics_analytics_heap_t;
|
||||
|
||||
/*
|
||||
* @brief Initialise physics runtime services.
|
||||
* @param analytics_heap_bytes Desired heap size (0 -> default 10 MiB)
|
||||
* @return 0 on success, -1 on failure
|
||||
*/
|
||||
int physics_runtime_init(size_t analytics_heap_bytes);
|
||||
|
||||
/**
|
||||
* @brief Tear down physics runtime services and release heap storage.
|
||||
*/
|
||||
void physics_runtime_shutdown(void);
|
||||
|
||||
/*
|
||||
* @brief Capture a host snapshot.
|
||||
* @param out Output buffer
|
||||
* @return 0 on success, -1 on failure
|
||||
*/
|
||||
int physics_host_snapshot(physics_host_snapshot_t *out);
|
||||
|
||||
/*
|
||||
* @brief Publish an event into the analytics ring buffer.
|
||||
* @param channel Logical event channel
|
||||
* @param payload Pointer to payload bytes
|
||||
* @param payload_bytes Payload length (<= ring size)
|
||||
* @return 0 on success, -1 if the payload could not be enqueued
|
||||
*/
|
||||
int physics_analytics_publish_event(uint32_t channel, const void *payload, uint16_t payload_bytes);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the analytics heap descriptor.
|
||||
* @return Pointer to heap descriptor (NULL if runtime not initialised)
|
||||
*/
|
||||
const physics_analytics_heap_t *physics_analytics_heap_info(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* STARFORTH_PHYSICS_RUNTIME_H */
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
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 STARFORTH_PLATFORM_ALLOC_H
|
||||
#define STARFORTH_PLATFORM_ALLOC_H
|
||||
|
||||
/*
|
||||
* platform_alloc.h - Portable memory allocation abstraction
|
||||
*
|
||||
* Host builds: Uses standard malloc/free
|
||||
* Kernel builds: Uses static arena with bump allocator
|
||||
*
|
||||
* Usage:
|
||||
* void* p = sf_malloc(size);
|
||||
* sf_free(p);
|
||||
* void* p2 = sf_calloc(count, size); // zero-initialized
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
* Allocate memory block of given size.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
void* sf_malloc(size_t size);
|
||||
|
||||
/*
|
||||
* Allocate zero-initialized memory for array.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
void* sf_calloc(size_t count, size_t size);
|
||||
|
||||
/*
|
||||
* Reallocate memory block to new size.
|
||||
* Returns NULL on failure (original block unchanged).
|
||||
*/
|
||||
void* sf_realloc(void* ptr, size_t new_size);
|
||||
|
||||
/*
|
||||
* Free previously allocated memory.
|
||||
* Passing NULL is safe (no-op).
|
||||
*/
|
||||
void sf_free(void* ptr);
|
||||
|
||||
/*
|
||||
* Initialize the allocator (required for kernel builds).
|
||||
* Host builds: no-op.
|
||||
* Kernel builds: sets up the static arena.
|
||||
*
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
int sf_alloc_init(void);
|
||||
|
||||
/*
|
||||
* Get allocator statistics (for diagnostics).
|
||||
*/
|
||||
typedef struct sf_alloc_stats {
|
||||
size_t total_bytes; /* Total arena size */
|
||||
size_t used_bytes; /* Currently allocated */
|
||||
size_t peak_bytes; /* High water mark */
|
||||
size_t alloc_count; /* Number of allocations */
|
||||
size_t free_count; /* Number of frees */
|
||||
} sf_alloc_stats_t;
|
||||
|
||||
void sf_alloc_get_stats(sf_alloc_stats_t* stats);
|
||||
|
||||
#endif /* STARFORTH_PLATFORM_ALLOC_H */
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* platform_io.h - Portable terminal-input-readiness check for StarForth
|
||||
*
|
||||
* One function, same shape as platform_lock.h/platform_time.h: a single
|
||||
* portable declaration, implemented once per platform (POSIX select() on
|
||||
* fd 0 for the hosted build, src/platform/linux/io.c; the kernel's own
|
||||
* console/keyboard-event bridge for the freestanding build, shim.c).
|
||||
* Backs the standard dictionary's ?TERMINAL word (io_words.c).
|
||||
*/
|
||||
|
||||
#ifndef STARFORTH_PLATFORM_IO_H
|
||||
#define STARFORTH_PLATFORM_IO_H
|
||||
|
||||
/**
|
||||
* @brief Non-blocking check for whether a key/character is available to read.
|
||||
*
|
||||
* Must not block and must not consume the byte if one is found (a
|
||||
* following KEY/getchar() must still return that same byte).
|
||||
*
|
||||
* @return 1 if input is ready, 0 otherwise
|
||||
*/
|
||||
int sf_terminal_ready(void);
|
||||
|
||||
#endif /* STARFORTH_PLATFORM_IO_H */
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* platform_lock.h - Portable mutex abstraction for StarForth
|
||||
*/
|
||||
|
||||
#ifndef STARFORTH_PLATFORM_LOCK_H
|
||||
#define STARFORTH_PLATFORM_LOCK_H
|
||||
|
||||
/* HISTORICAL: this condition also included `|| defined(L4RE_TARGET)` --
|
||||
* L4Re had no pthreads either, so it shared the spinlock path with
|
||||
* STARFORTH_MINIMAL. L4Re support has been removed as an active target. */
|
||||
#if defined(STARFORTH_MINIMAL)
|
||||
typedef struct sf_mutex
|
||||
{
|
||||
volatile unsigned int state;
|
||||
} sf_mutex_t;
|
||||
#else
|
||||
#include <pthread.h>
|
||||
typedef struct sf_mutex
|
||||
{
|
||||
pthread_mutex_t handle;
|
||||
} sf_mutex_t;
|
||||
#endif
|
||||
|
||||
int sf_mutex_init(sf_mutex_t *mutex);
|
||||
void sf_mutex_destroy(sf_mutex_t *mutex);
|
||||
void sf_mutex_lock(sf_mutex_t *mutex);
|
||||
void sf_mutex_unlock(sf_mutex_t *mutex);
|
||||
|
||||
#endif /* STARFORTH_PLATFORM_LOCK_H */
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
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 STARFORTH_PLATFORM_TIME_H
|
||||
#define STARFORTH_PLATFORM_TIME_H
|
||||
|
||||
/*
|
||||
* StarForth Platform Time Abstraction
|
||||
*
|
||||
* Provides portable timing interface for POSIX builds. (HISTORICAL: also
|
||||
* supported L4Re/StarshipOS through mid-2026; removed as an active target.)
|
||||
* Uses vtable pattern similar to blkio subsystem.
|
||||
*
|
||||
* License: See LICENSE file
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Time representation (nanoseconds since epoch) */
|
||||
typedef uint64_t sf_time_ns_t;
|
||||
|
||||
/* Timestamp string buffer size */
|
||||
#define SF_TIME_STAMP_SIZE 32
|
||||
|
||||
/* Platform time backend vtable */
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Get monotonic time (for performance measurement)
|
||||
* @return Nanoseconds since system boot (never decreases)
|
||||
*/
|
||||
sf_time_ns_t (*get_monotonic_ns)(void);
|
||||
|
||||
/**
|
||||
* @brief Get wall-clock time
|
||||
* @return Nanoseconds since Unix epoch (1970-01-01 00:00:00 UTC)
|
||||
* Returns 0 if RTC unavailable
|
||||
*/
|
||||
sf_time_ns_t (*get_realtime_ns)(void);
|
||||
|
||||
/**
|
||||
* @brief Set wall-clock time (may require privileges)
|
||||
* @param ns_since_epoch Nanoseconds since Unix epoch
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int (*set_realtime_ns)(sf_time_ns_t ns_since_epoch);
|
||||
|
||||
/**
|
||||
* @brief Format timestamp as human-readable string
|
||||
* @param ns_since_epoch Nanoseconds since Unix epoch
|
||||
* @param buf Output buffer (SF_TIME_STAMP_SIZE bytes)
|
||||
* @param format_24h Use 24-hour format (1) or 12-hour (0)
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int (*format_timestamp)(sf_time_ns_t ns_since_epoch, char *buf, int format_24h);
|
||||
|
||||
/**
|
||||
* @brief Check if real-time clock is available
|
||||
* @return 1 if RTC available, 0 if not
|
||||
*/
|
||||
int (*has_rtc)(void);
|
||||
} sf_time_backend_t;
|
||||
|
||||
/* Global backend pointer (set at initialization) */
|
||||
extern const sf_time_backend_t *sf_time_backend;
|
||||
|
||||
/* Platform-specific backend registration */
|
||||
extern const sf_time_backend_t sf_time_backend_posix; /* POSIX implementation */
|
||||
extern const sf_time_backend_t sf_time_backend_l4re; /* HISTORICAL: L4Re/StarshipOS implementation, no longer wired into any build (see src/platform/l4re/time.c) */
|
||||
|
||||
/* Convenience wrappers (inline for zero overhead).
|
||||
* Define PLATFORM_TIME_NO_INLINE before including this header to suppress
|
||||
* these inline definitions (e.g. when providing concrete implementations).
|
||||
* When suppressed, non-inline prototypes are declared so callers can link
|
||||
* to a concrete implementation (e.g. shim.c in the kernel). */
|
||||
#ifndef PLATFORM_TIME_NO_INLINE
|
||||
|
||||
static inline sf_time_ns_t sf_monotonic_ns(void) {
|
||||
return sf_time_backend->get_monotonic_ns();
|
||||
}
|
||||
|
||||
static inline sf_time_ns_t sf_realtime_ns(void) {
|
||||
return sf_time_backend->get_realtime_ns();
|
||||
}
|
||||
|
||||
static inline int sf_set_realtime_ns(sf_time_ns_t ns) {
|
||||
return sf_time_backend->set_realtime_ns(ns);
|
||||
}
|
||||
|
||||
static inline int sf_format_timestamp(sf_time_ns_t ns, char *buf, int format_24h) {
|
||||
return sf_time_backend->format_timestamp(ns, buf, format_24h);
|
||||
}
|
||||
|
||||
static inline int sf_has_rtc(void) {
|
||||
return sf_time_backend->has_rtc();
|
||||
}
|
||||
|
||||
#else /* PLATFORM_TIME_NO_INLINE */
|
||||
|
||||
/* Non-inline prototypes — provided by the platform's concrete implementation. */
|
||||
sf_time_ns_t sf_monotonic_ns(void);
|
||||
sf_time_ns_t sf_realtime_ns(void);
|
||||
int sf_set_realtime_ns(sf_time_ns_t ns);
|
||||
int sf_format_timestamp(sf_time_ns_t ns, char *buf, int format_24h);
|
||||
int sf_has_rtc(void);
|
||||
|
||||
#endif /* PLATFORM_TIME_NO_INLINE */
|
||||
|
||||
/* Time conversion helpers */
|
||||
|
||||
static inline sf_time_ns_t sf_seconds_to_ns(uint64_t seconds) {
|
||||
return seconds * 1000000000ULL;
|
||||
}
|
||||
|
||||
static inline uint64_t sf_ns_to_seconds(sf_time_ns_t ns) {
|
||||
return ns / 1000000000ULL;
|
||||
}
|
||||
|
||||
static inline uint64_t sf_ns_to_ms(sf_time_ns_t ns) {
|
||||
return ns / 1000000ULL;
|
||||
}
|
||||
|
||||
static inline uint64_t sf_ns_to_us(sf_time_ns_t ns) {
|
||||
return ns / 1000ULL;
|
||||
}
|
||||
|
||||
/* Initialize platform time backend (call once at startup) */
|
||||
void sf_time_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* STARFORTH_PLATFORM_TIME_H */
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
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 STARFORTH_PROFILER_H
|
||||
#define STARFORTH_PROFILER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Forward declarations */
|
||||
typedef struct VM VM;
|
||||
typedef struct DictEntry DictEntry;
|
||||
|
||||
/**
|
||||
* @brief Profiling detail levels
|
||||
*
|
||||
* Controls the amount of profiling information collected during execution.
|
||||
* Higher levels include all features from lower levels.
|
||||
*/
|
||||
typedef enum {
|
||||
PROFILE_DISABLED = 0, /**< Profiling disabled (zero overhead) */
|
||||
PROFILE_BASIC = 1, /**< Word execution frequency tracking (minimal overhead) */
|
||||
PROFILE_DETAILED = 2, /**< Basic + word execution timing (5-10% overhead) */
|
||||
PROFILE_VERBOSE = 3, /**< Detailed + stack/memory access patterns (15-20% overhead) */
|
||||
PROFILE_FULL = 4 /**< Reserved for future use (full instrumentation) */
|
||||
} ProfileLevel;
|
||||
|
||||
/**
|
||||
* @brief Profiling measurement categories
|
||||
*
|
||||
* Different aspects of VM operation that can be profiled
|
||||
*/
|
||||
typedef enum {
|
||||
PROF_VM_INIT = 0, /**< VM initialization */
|
||||
PROF_VM_EXECUTE, /**< VM execution loop */
|
||||
PROF_VM_COMPILE, /**< Word compilation */
|
||||
PROF_DICT_LOOKUP, /**< Dictionary searches */
|
||||
PROF_DICT_ADD, /**< Dictionary additions */
|
||||
PROF_STACK_OPS, /**< Stack operations */
|
||||
PROF_MEMORY_ACCESS, /**< Memory access operations */
|
||||
PROF_WORD_EXEC, /**< Word execution */
|
||||
PROF_IO_OPS, /**< I/O operations */
|
||||
PROF_CONTROL_FLOW, /**< Control flow operations */
|
||||
PROF_CATEGORY_COUNT /**< Total number of categories */
|
||||
} ProfileCategory;
|
||||
|
||||
/**
|
||||
* @brief Timer for measuring execution duration
|
||||
*/
|
||||
typedef struct {
|
||||
uint64_t start_time; /**< Start timestamp */
|
||||
uint64_t total_time; /**< Accumulated execution time */
|
||||
uint64_t call_count; /**< Number of measurements */
|
||||
uint64_t min_time; /**< Minimum recorded time */
|
||||
uint64_t max_time; /**< Maximum recorded time */
|
||||
const char *name; /**< Timer identifier */
|
||||
} ProfileTimer;
|
||||
|
||||
/**
|
||||
* @brief Execution statistics for a single word
|
||||
*/
|
||||
typedef struct {
|
||||
const char *word_name; /**< Name of the profiled word */
|
||||
uint64_t total_time; /**< Total execution time */
|
||||
uint64_t call_count; /**< Number of executions */
|
||||
uint64_t avg_time; /**< Average execution time */
|
||||
uint64_t min_time; /**< Minimum execution time */
|
||||
uint64_t max_time; /**< Maximum execution time */
|
||||
double percentage; /**< Percentage of total runtime */
|
||||
} WordProfile;
|
||||
|
||||
/* Memory access pattern tracking */
|
||||
typedef struct {
|
||||
uint64_t reads;
|
||||
uint64_t writes;
|
||||
uint64_t cache_hits;
|
||||
uint64_t cache_misses;
|
||||
uint64_t total_bytes_read;
|
||||
uint64_t total_bytes_written;
|
||||
} MemoryProfile;
|
||||
|
||||
/* Main profiler state */
|
||||
typedef struct {
|
||||
ProfileLevel level;
|
||||
uint64_t profiling_overhead;
|
||||
uint64_t total_execution_time;
|
||||
|
||||
/* Category timers */
|
||||
ProfileTimer category_timers[PROF_CATEGORY_COUNT];
|
||||
|
||||
/* Word execution tracking */
|
||||
WordProfile *word_profiles;
|
||||
size_t word_profile_count;
|
||||
size_t word_profile_capacity;
|
||||
|
||||
/* Memory access tracking */
|
||||
MemoryProfile memory_stats;
|
||||
|
||||
/* Hot path detection */
|
||||
uint64_t instruction_count;
|
||||
uint64_t branch_count;
|
||||
uint64_t loop_iterations;
|
||||
|
||||
/* Performance counters */
|
||||
struct {
|
||||
uint64_t vm_cycles;
|
||||
uint64_t dict_lookups;
|
||||
uint64_t stack_operations;
|
||||
uint64_t memory_allocations;
|
||||
uint64_t compile_operations;
|
||||
} counters;
|
||||
} Profiler;
|
||||
|
||||
/* Global profiler instance */
|
||||
extern void *g_profiler;
|
||||
|
||||
/* Profiler lifecycle */
|
||||
/**
|
||||
* @brief Initialize the profiling system
|
||||
* @param level Initial profiling detail level
|
||||
* @return 0 on success, non-zero on failure
|
||||
*/
|
||||
int profiler_init(ProfileLevel level);
|
||||
|
||||
/**
|
||||
* @brief Shutdown and cleanup the profiling system
|
||||
*/
|
||||
void profiler_shutdown(void);
|
||||
|
||||
/**
|
||||
* @brief Reset all profiling statistics
|
||||
*/
|
||||
void profiler_reset(void);
|
||||
|
||||
/**
|
||||
* @brief Change the current profiling detail level
|
||||
* @param level New profiling level to set
|
||||
*/
|
||||
|
||||
void profiler_set_level(ProfileLevel level);
|
||||
|
||||
/* Timing functions */
|
||||
uint64_t profiler_get_time_ns(void);
|
||||
|
||||
void profiler_start_timer(ProfileCategory category);
|
||||
|
||||
void profiler_end_timer(ProfileCategory category);
|
||||
|
||||
/* Word profiling */
|
||||
/**
|
||||
* @brief Start timing a word execution (PROFILE_DETAILED+)
|
||||
* @param entry Dictionary entry of the word being executed
|
||||
*/
|
||||
void profiler_word_enter(const DictEntry *entry);
|
||||
|
||||
/**
|
||||
* @brief End timing a word execution (PROFILE_DETAILED+)
|
||||
* @param entry Dictionary entry of the word being executed
|
||||
*/
|
||||
void profiler_word_exit(const DictEntry *entry);
|
||||
|
||||
/**
|
||||
* @brief Track word execution frequency without timing (PROFILE_BASIC+)
|
||||
* @param entry Dictionary entry of the word being executed
|
||||
*
|
||||
* This is a lightweight alternative to profiler_word_enter/exit that only
|
||||
* increments call counters without timing overhead. Ideal for identifying
|
||||
* hot words (frequently executed) for optimization.
|
||||
*/
|
||||
void profiler_word_count(const DictEntry *entry);
|
||||
|
||||
/* Memory profiling */
|
||||
void profiler_memory_read(size_t bytes);
|
||||
|
||||
void profiler_memory_write(size_t bytes);
|
||||
|
||||
void profiler_memory_alloc(size_t bytes);
|
||||
|
||||
/* Counter increments */
|
||||
void profiler_inc_vm_cycles(uint64_t cycles);
|
||||
|
||||
void profiler_inc_dict_lookup(void);
|
||||
|
||||
void profiler_inc_stack_op(void);
|
||||
|
||||
void profiler_inc_branch(void);
|
||||
|
||||
void profiler_inc_loop_iter(void);
|
||||
|
||||
/* Analysis and reporting */
|
||||
void profiler_generate_report(void);
|
||||
|
||||
void profiler_print_summary(void);
|
||||
|
||||
void profiler_print_word_analysis(void);
|
||||
|
||||
void profiler_print_memory_analysis(void);
|
||||
|
||||
void profiler_print_hotspots(void);
|
||||
|
||||
/* Performance optimization suggestions */
|
||||
void profiler_analyze_performance(VM *vm);
|
||||
|
||||
void profiler_suggest_optimizations(void);
|
||||
|
||||
/* Profiler macros for easy integration */
|
||||
#define PROFILE_ENABLED() (0)
|
||||
|
||||
#define PROFILE_START(category) ((void)0)
|
||||
#define PROFILE_END(category) ((void)0)
|
||||
#define PROFILE_WORD_ENTER(name) ((void)0)
|
||||
#define PROFILE_WORD_EXIT(name) ((void)0)
|
||||
#define PROFILE_MEMORY_READ(bytes) ((void)0)
|
||||
#define PROFILE_MEMORY_WRITE(bytes) ((void)0)
|
||||
#define PROFILE_INC_CYCLES(n) ((void)0)
|
||||
#define PROFILE_INC_DICT_LOOKUP() ((void)0)
|
||||
#define PROFILE_INC_STACK_OP() ((void)0)
|
||||
|
||||
/* Scoped profiling helper */
|
||||
typedef struct {
|
||||
ProfileCategory category;
|
||||
const char *word_name;
|
||||
} ProfileScope;
|
||||
|
||||
#endif /* STARFORTH_PROFILER_H */
|
||||
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
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 Q48_16_H
|
||||
#define Q48_16_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ============================================================================
|
||||
* Q48.16 Fixed-Point Arithmetic
|
||||
* ============================================================================
|
||||
*
|
||||
* Format: uint64_t with fixed decimal point after bit 15
|
||||
* - Bits 0-15: Fractional part (1/65536 resolution)
|
||||
* - Bits 16-63: Integer part (up to 2^48-1)
|
||||
*
|
||||
* Example: 0x00010000 = 1.0, 0x00018000 = 1.5, 0x00010001 = 1 + 1/65536
|
||||
*
|
||||
* All operations are integer-only: no floating-point, no transcendental functions.
|
||||
* Used for inference engine calculations (variance, regression, diagnostics).
|
||||
*
|
||||
* Why Q48.16?
|
||||
* - 48-bit integer range: 0 to 281,474,976,710,655 (enough for VM metrics)
|
||||
* - 16-bit fractional: ~0.0000152 resolution (precise enough for statistical calculations)
|
||||
* - Perfect fit for 64-bit uint64_t: efficient on modern CPUs
|
||||
*/
|
||||
|
||||
typedef uint64_t q48_16_t;
|
||||
|
||||
/* ============================================================================
|
||||
* Core Arithmetic Operations (All Q48.16 ↔ Q48.16)
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* @brief Multiply two Q48.16 values, keeping result in Q48.16
|
||||
*
|
||||
* Math: (a / 2^16) * (b / 2^16) * 2^16 = (a * b) / 2^16
|
||||
* Returns: (a * b) >> 16
|
||||
*
|
||||
* @param a First Q48.16 operand
|
||||
* @param b Second Q48.16 operand
|
||||
* @return (a * b) >> 16 in Q48.16 format
|
||||
*/
|
||||
q48_16_t q48_mul(q48_16_t a, q48_16_t b);
|
||||
|
||||
/*
|
||||
* @brief Divide two Q48.16 values, keeping result in Q48.16
|
||||
*
|
||||
* Math: (a / 2^16) / (b / 2^16) * 2^16 = (a << 16) / b
|
||||
* Returns: (a << 16) / b
|
||||
*
|
||||
* @param a Dividend in Q48.16
|
||||
* @param b Divisor in Q48.16 (non-zero)
|
||||
* @return (a << 16) / b in Q48.16 format
|
||||
*/
|
||||
q48_16_t q48_div(q48_16_t a, q48_16_t b);
|
||||
|
||||
/* Guard: skip static inlines already provided by include/starkernel/q48_16.h
|
||||
* (both headers share the same definitions; STARKERNEL_Q48_16_H is defined
|
||||
* by the kernel version so we avoid -Werror redefinition in mixed builds). */
|
||||
#ifndef STARKERNEL_Q48_16_H
|
||||
|
||||
/**
|
||||
* @brief Add two Q48.16 values
|
||||
*
|
||||
* Math: (a / 2^16) + (b / 2^16) = (a + b) / 2^16
|
||||
* Returns: a + b (no shift needed, both have same scaling)
|
||||
*
|
||||
* @param a First Q48.16 operand
|
||||
* @param b Second Q48.16 operand
|
||||
* @return a + b in Q48.16 format
|
||||
*/
|
||||
static inline q48_16_t q48_add(q48_16_t a, q48_16_t b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subtract two Q48.16 values
|
||||
*
|
||||
* Math: (a / 2^16) - (b / 2^16) = (a - b) / 2^16
|
||||
* Returns: a - b (no shift needed)
|
||||
*
|
||||
* @param a Minuend in Q48.16
|
||||
* @param b Subtrahend in Q48.16
|
||||
* @return a - b in Q48.16 format
|
||||
*/
|
||||
static inline q48_16_t q48_sub(q48_16_t a, q48_16_t b) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Absolute value of Q48.16
|
||||
*
|
||||
* @param a Q48.16 value
|
||||
* @return |a| in Q48.16 format
|
||||
*/
|
||||
static inline q48_16_t q48_abs(q48_16_t a) {
|
||||
return (a < 0x8000000000000000ULL) ? a : (0ULL - a);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* Conversion Operations (u64 ↔ Q48.16)
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Convert unsigned 64-bit integer to Q48.16
|
||||
*
|
||||
* Math: u * 2^16 (shift left by 16 bits)
|
||||
* Example: q48_from_u64(1) = 0x10000 (1.0 in Q48.16)
|
||||
*
|
||||
* @param u Unsigned 64-bit integer (max 2^48-1 to avoid overflow)
|
||||
* @return u << 16 in Q48.16 format
|
||||
*/
|
||||
static inline q48_16_t q48_from_u64(uint64_t u) {
|
||||
return u << 16;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert Q48.16 to a 64-bit integer (truncate fractional)
|
||||
*
|
||||
* Math: q / 2^16 (arithmetic shift right by 16 bits)
|
||||
* Example: q48_to_u64(0x10000) = 1, q48_to_u64(-0x10000) = -1
|
||||
*
|
||||
* q48_16_t values are two's-complement signed under the hood (q48_neg/
|
||||
* q48_abs already treat them that way); a plain unsigned (logical) shift
|
||||
* would corrupt negative inputs instead of sign-extending them, so this
|
||||
* shifts via a signed intermediate. Bit-identical to the old behavior for
|
||||
* non-negative q (the only case this function's other caller, the
|
||||
* inference engine's sum-of-squares accumulation, ever produces).
|
||||
*
|
||||
* @param q Q48.16 value
|
||||
* @return q >> 16, sign-extended, reinterpreted as uint64_t
|
||||
*/
|
||||
static inline uint64_t q48_to_u64(q48_16_t q) {
|
||||
return (uint64_t)(((int64_t)q) >> 16);
|
||||
}
|
||||
|
||||
#endif /* STARKERNEL_Q48_16_H */
|
||||
|
||||
/*
|
||||
* @brief Convert double to Q48.16 (for testing/initialization only)
|
||||
*
|
||||
* NOT used in inference engine (inference is integer-only).
|
||||
* Provided for testing and diagnostic tools only.
|
||||
*
|
||||
* @param d Double-precision float
|
||||
* @return d * 2^16 as Q48.16
|
||||
*/
|
||||
q48_16_t q48_from_double(double d);
|
||||
|
||||
/*
|
||||
* @brief Convert Q48.16 to double (for testing/diagnostics only)
|
||||
*
|
||||
* NOT used in inference engine (inference is integer-only).
|
||||
* Provided for logging and RStudio dashboard output.
|
||||
*
|
||||
* @param q Q48.16 value
|
||||
* @return q / 2^16 as double
|
||||
*/
|
||||
double q48_to_double(q48_16_t q);
|
||||
|
||||
/* ============================================================================
|
||||
* Approximation Operations (Integer-Only)
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* @brief Approximate natural logarithm in Q48.16 (integer-only)
|
||||
*
|
||||
* Purpose: Required for decay slope inference via exponential fitting
|
||||
* Model: ln(heat[t]) = ln(h0) - slope*t
|
||||
*
|
||||
* Method: Piecewise linear approximation or Newton-Raphson iteration
|
||||
* - Input x must be > 0 (assertion check)
|
||||
* - Output is ln(x) in Q48.16 format
|
||||
* - Precision: ~5% error acceptable (tuning parameter)
|
||||
* - No floating-point operations
|
||||
*
|
||||
* Implementation Strategy:
|
||||
* 1. Use bit position (floor(log2(x))) as coarse estimate
|
||||
* 2. Refine with Newton iteration (3-4 cycles for Q48.16 precision)
|
||||
* 3. Keep all arithmetic in integer space
|
||||
*
|
||||
* Example: q48_log_approx(65536) = log(1.0) in Q48.16 ≈ 0
|
||||
* q48_log_approx(131072) = log(2.0) in Q48.16 ≈ 0x0000B172
|
||||
*
|
||||
* @param x Value to take logarithm of (x > 0)
|
||||
* @return ln(x) in Q48.16 format
|
||||
*/
|
||||
q48_16_t q48_log_approx(uint64_t x);
|
||||
|
||||
/*
|
||||
* @brief Approximate exponential e^x in Q48.16 (integer-only, future use)
|
||||
*
|
||||
* Purpose: Might be needed for inverse operations or model validation
|
||||
* Status: Defined but not required for Phase 1 inference engine
|
||||
* Method: Similar to q48_log_approx (piecewise + Newton)
|
||||
*
|
||||
* @param q Q48.16 exponent
|
||||
* @return e^q in Q48.16 format
|
||||
*/
|
||||
q48_16_t q48_exp_approx(q48_16_t q);
|
||||
|
||||
/*
|
||||
* @brief Approximate square root in Q48.16 (integer-only)
|
||||
*
|
||||
* Purpose: Compute R² (fit quality) for regression diagnostics
|
||||
* Method: Newton-Raphson for integer-only sqrt
|
||||
*
|
||||
* @param q Q48.16 value
|
||||
* @return sqrt(q) in Q48.16 format
|
||||
*/
|
||||
q48_16_t q48_sqrt_approx(q48_16_t q);
|
||||
|
||||
/*
|
||||
* @brief Approximate sin(q) in Q48.16 (integer-only, Taylor series)
|
||||
*
|
||||
* Purpose: Console drawing fabric's CIRCLE/ARC/ELLIPSE (FABRIC-0.md item 4.3.3b)
|
||||
* Method: Range-reduce into [-pi, pi], then Taylor series
|
||||
*
|
||||
* @param q Angle in Q48.16 format (radians, any magnitude)
|
||||
* @return sin(q) in Q48.16 format
|
||||
*/
|
||||
q48_16_t q48_sin_approx(q48_16_t q);
|
||||
|
||||
/*
|
||||
* @brief Approximate cos(q) in Q48.16 (integer-only, Taylor series)
|
||||
*
|
||||
* Purpose: Console drawing fabric's CIRCLE/ARC/ELLIPSE (FABRIC-0.md item 4.3.3b)
|
||||
* Method: Range-reduce into [-pi, pi], then Taylor series
|
||||
*
|
||||
* @param q Angle in Q48.16 format (radians, any magnitude)
|
||||
* @return cos(q) in Q48.16 format
|
||||
*/
|
||||
q48_16_t q48_cos_approx(q48_16_t q);
|
||||
|
||||
/* ============================================================================
|
||||
* Diagnostic / Testing Utilities
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* @brief Pretty-print Q48.16 value to string for debugging
|
||||
*
|
||||
* Format: "integer.fractional" (e.g., "1.50000")
|
||||
* Uses internal static buffer - NOT thread-safe
|
||||
*
|
||||
* @param q Q48.16 value
|
||||
* @return Pointer to static string (valid until next call)
|
||||
*/
|
||||
const char* q48_to_string(q48_16_t q);
|
||||
|
||||
/*
|
||||
* @brief Validate Q48.16 value is within safe range
|
||||
*
|
||||
* Checks: Does not overflow on mul/div operations
|
||||
* Returns: 1 if valid, 0 if risky
|
||||
*
|
||||
* @param q Q48.16 value
|
||||
* @return 1 if safe, 0 if risky
|
||||
*/
|
||||
int q48_is_valid(q48_16_t q);
|
||||
|
||||
#endif /* Q48_16_H */
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
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 REPL_H
|
||||
#define REPL_H
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
/*
|
||||
* @brief Starts the REPL (Read-Eval-Print Loop) for the Forth virtual machine
|
||||
* @param vm Pointer to the VM instance to run the REPL on
|
||||
* @param script_mode If 1, suppress prompts and "ok" output (for piped input)
|
||||
*/
|
||||
void vm_repl(VM *vm, int script_mode);
|
||||
|
||||
#endif /* REPL_H */
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
*** StarForth ***
|
||||
|
||||
rolling_window_knobs.h - Tunable Adaptive Shrinking Control
|
||||
|
||||
Knobs that control how the rolling window automatically learns and adapts
|
||||
its size during execution. These are NOT the initial window size - they
|
||||
control the self-tuning behavior.
|
||||
|
||||
All knobs are tunable via Makefile:
|
||||
make ADAPTIVE_SHRINK_RATE=30 (shrink by 30% instead of default)
|
||||
make ADAPTIVE_MIN_WINDOW_SIZE=512 (never shrink below 512)
|
||||
etc.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ROLLING_WINDOW_KNOBS_H
|
||||
#define ROLLING_WINDOW_KNOBS_H
|
||||
|
||||
#include "starforth_config.h"
|
||||
|
||||
/* ============================================================================
|
||||
* Knob #8: ADAPTIVE_SHRINK_RATE - How aggressively to shrink the window
|
||||
* ============================================================================
|
||||
*
|
||||
* When pattern diversity plateaus, the window shrinks by this percentage.
|
||||
*
|
||||
* Value: 0-99 (percentage to retain, not discard)
|
||||
* ADAPTIVE_SHRINK_RATE=75 → shrink to 75% of current size (discard 25%)
|
||||
* ADAPTIVE_SHRINK_RATE=50 → shrink to 50% of current size (discard 50%)
|
||||
* ADAPTIVE_SHRINK_RATE=90 → shrink to 90% of current size (discard 10%)
|
||||
*
|
||||
* Rationale:
|
||||
* Higher (90+): Conservative, slow learning, larger final window
|
||||
* Default (75): Balanced - learns in 3-4 shrink cycles
|
||||
* Lower (50): Aggressive, fast learning, minimal final window
|
||||
*
|
||||
* Trade-off:
|
||||
* Higher = safer (more pattern capture margin, slower adaptation)
|
||||
* Lower = leaner (less memory overhead, faster optimization)
|
||||
*
|
||||
* Default: see starforth_config.h (STARFORTH_CONFIG_ADAPTIVE_SHRINK_RATE_DEFAULT)
|
||||
* Range: 50-95 (below 50 is too aggressive, above 95 barely shrinks)
|
||||
*
|
||||
* Tuning: make ADAPTIVE_SHRINK_RATE=80
|
||||
*/
|
||||
|
||||
/* ============================================================================
|
||||
* Knob #9: ADAPTIVE_MIN_WINDOW_SIZE - Minimum window size (safety floor)
|
||||
* ============================================================================
|
||||
*
|
||||
* The window will never shrink below this size, even if diversity plateaus.
|
||||
* Prevents over-optimization that might miss emerging patterns.
|
||||
*
|
||||
* Value: 64-2048 (in word IDs, not bytes)
|
||||
* ADAPTIVE_MIN_WINDOW_SIZE=256 → don't shrink below 256
|
||||
* ADAPTIVE_MIN_WINDOW_SIZE=128 → don't shrink below 128
|
||||
* ADAPTIVE_MIN_WINDOW_SIZE=512 → don't shrink below 512
|
||||
*
|
||||
* Rationale:
|
||||
* Smaller (128): Lean optimization, accept lower pattern capture
|
||||
* Default (256): Balanced - typical workload needs 200-250 patterns
|
||||
* Larger (512): Conservative, handles complex workloads
|
||||
*
|
||||
* Trade-off:
|
||||
* Smaller = less memory, risk of missing edge-case patterns
|
||||
* Larger = more memory, safer for unpredictable workloads
|
||||
*
|
||||
* Default: see starforth_config.h (STARFORTH_CONFIG_ADAPTIVE_MIN_WINDOW_SIZE_DEFAULT)
|
||||
* Range: 64-1024
|
||||
*
|
||||
* Tuning: make ADAPTIVE_MIN_WINDOW_SIZE=512
|
||||
*/
|
||||
|
||||
/* ============================================================================
|
||||
* Knob #10: ADAPTIVE_CHECK_FREQUENCY - How often to check diversity
|
||||
* ============================================================================
|
||||
*
|
||||
* The system checks pattern diversity every N executions.
|
||||
* More frequent checks = faster response to saturation, more overhead.
|
||||
*
|
||||
* Value: 32-1024 (execution count between checks)
|
||||
* ADAPTIVE_CHECK_FREQUENCY=128 → check after every 128 executions
|
||||
* ADAPTIVE_CHECK_FREQUENCY=256 → check after every 256 executions
|
||||
* ADAPTIVE_CHECK_FREQUENCY=512 → check after every 512 executions
|
||||
*
|
||||
* Rationale:
|
||||
* More frequent (128): Responsive learning, detects saturation quickly
|
||||
* Default (256): Balanced - good responsiveness, minimal overhead
|
||||
* Less frequent (512): Lazy learning, smaller monitoring overhead
|
||||
*
|
||||
* Trade-off:
|
||||
* More = faster adaptation, slightly more computation
|
||||
* Less = slower adaptation, minimal overhead
|
||||
*
|
||||
* Default: see starforth_config.h (STARFORTH_CONFIG_ADAPTIVE_CHECK_FREQUENCY_DEFAULT)
|
||||
* Range: 32-1024
|
||||
*
|
||||
* Tuning: make ADAPTIVE_CHECK_FREQUENCY=128
|
||||
*/
|
||||
|
||||
/* ============================================================================
|
||||
* Knob #11: ADAPTIVE_GROWTH_THRESHOLD - Pattern growth rate that signals saturation
|
||||
* ============================================================================
|
||||
*
|
||||
* When pattern diversity growth drops below this rate, window can shrink.
|
||||
* Expressed as percentage (0-100).
|
||||
*
|
||||
* Value: 0-10 (as percentage, 0=0%, 10=10%)
|
||||
* ADAPTIVE_GROWTH_THRESHOLD=1 → shrink when growth < 1% (aggressive)
|
||||
* ADAPTIVE_GROWTH_THRESHOLD=2 → shrink when growth < 2% (default, balanced)
|
||||
* ADAPTIVE_GROWTH_THRESHOLD=5 → shrink when growth < 5% (conservative)
|
||||
*
|
||||
* Rationale:
|
||||
* Lower (0.5%): Very aggressive, assumes patterns found early
|
||||
* Default (1%): Balanced - empirically tested, works well
|
||||
* Higher (5%): Conservative, waits for stronger saturation signal
|
||||
*
|
||||
* Trade-off:
|
||||
* Lower = eager shrinking, risk of missing late-arriving patterns
|
||||
* Higher = cautious shrinking, longer time to final window size
|
||||
*
|
||||
* Default: see starforth_config.h (STARFORTH_CONFIG_ADAPTIVE_GROWTH_THRESHOLD_DEFAULT)
|
||||
* Range: 0-10
|
||||
*
|
||||
* Note: Internally, this is applied as: growth_rate < (threshold / 100.0)
|
||||
* E.g., ADAPTIVE_GROWTH_THRESHOLD=1 means growth_rate < 0.01
|
||||
*
|
||||
* Tuning: make ADAPTIVE_GROWTH_THRESHOLD=2
|
||||
*/
|
||||
|
||||
/* ============================================================================
|
||||
* Summary Table: How Knobs Interact
|
||||
* ============================================================================
|
||||
*
|
||||
* Scenario 1: "Fast Learning, Lean Final Size"
|
||||
* ADAPTIVE_SHRINK_RATE=50
|
||||
* ADAPTIVE_MIN_WINDOW_SIZE=128
|
||||
* ADAPTIVE_CHECK_FREQUENCY=128
|
||||
* ADAPTIVE_GROWTH_THRESHOLD=0
|
||||
* → Shrinks aggressively, frequently, to minimal size
|
||||
* → Best for: Memory-constrained systems, simple workloads
|
||||
*
|
||||
* Scenario 2: "Balanced (Default)" -- see starforth_config.h for the exact
|
||||
* shipped default of each knob; illustrative values below.
|
||||
* ADAPTIVE_SHRINK_RATE=50
|
||||
* ADAPTIVE_MIN_WINDOW_SIZE=256
|
||||
* ADAPTIVE_CHECK_FREQUENCY=512
|
||||
* ADAPTIVE_GROWTH_THRESHOLD=5
|
||||
* → Good balance, typical FORTH programs
|
||||
* → Best for: General-purpose systems
|
||||
*
|
||||
* Scenario 3: "Conservative, Safe"
|
||||
* ADAPTIVE_SHRINK_RATE=90
|
||||
* ADAPTIVE_MIN_WINDOW_SIZE=512
|
||||
* ADAPTIVE_CHECK_FREQUENCY=512
|
||||
* ADAPTIVE_GROWTH_THRESHOLD=5
|
||||
* → Slow, cautious shrinking, large safety margin
|
||||
* → Best for: Complex workloads, unpredictable patterns
|
||||
*
|
||||
*/
|
||||
|
||||
#endif /* ROLLING_WINDOW_KNOBS_H */
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
*** StarForth ***
|
||||
|
||||
rolling_window_of_truth.h - Execution history for deterministic metrics seeding
|
||||
|
||||
The "Rolling Window of Truth" captures actual execution sequences to:
|
||||
1. Seed initial metrics after POST (hot-words cache, pipelining context)
|
||||
2. Provide deterministic record of all word executions
|
||||
3. Enable reproducible, provable optimization decisions
|
||||
4. Form foundation for formal verification (Isabelle/HOL)
|
||||
|
||||
Vision: Every decision is observable, traceable, and mathematically provable.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ROLLING_WINDOW_OF_TRUTH_H
|
||||
#define ROLLING_WINDOW_OF_TRUTH_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "vm.h"
|
||||
|
||||
/* ============================================================================
|
||||
* Rolling Window of Truth
|
||||
* ============================================================================
|
||||
*
|
||||
* Circular buffer of word IDs representing recent execution sequence.
|
||||
* Used to:
|
||||
* 1. Seed metrics after POST completes
|
||||
* 2. Provide execution context for pipelining decisions
|
||||
* 3. Enable reproducibility and formal verification
|
||||
*
|
||||
* NOTE: Structure is defined in vm.h to avoid circular includes.
|
||||
* See vm.h for structure definition and ROLLING_WINDOW_SIZE constant.
|
||||
*/
|
||||
|
||||
/* ============================================================================
|
||||
* Rolling Window API
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Initialize rolling window of truth.
|
||||
* Called during VM initialization.
|
||||
*
|
||||
* @param window Pointer to uninitialized RollingWindowOfTruth
|
||||
* @return 0 on success, -1 on malloc failure
|
||||
*/
|
||||
int rolling_window_init(RollingWindowOfTruth * window);
|
||||
|
||||
/*
|
||||
* Record a word execution in the rolling window.
|
||||
* Called after each word executes (in inner interpreter).
|
||||
*
|
||||
* THREAD SAFETY: This function is NOT thread-safe. Caller MUST hold
|
||||
* vm->tuning_lock when calling from multiple threads (main VM + heartbeat).
|
||||
*
|
||||
* Called from:
|
||||
* - Main VM thread (inner interpreter loop, src/vm.c:1162)
|
||||
* - Heartbeat worker thread (through rolling_window_service, src/vm.c:743)
|
||||
*
|
||||
* MUST ALWAYS be protected by:
|
||||
* sf_mutex_lock(&vm->tuning_lock);
|
||||
* rolling_window_record_execution(&vm->rolling_window, word_id);
|
||||
* sf_mutex_unlock(&vm->tuning_lock);
|
||||
*
|
||||
* Extremely lightweight (constant time):
|
||||
* - Write to circular buffer
|
||||
* - Increment counters
|
||||
* - 2-3 CPU cycles (plus lock overhead ~100 cycles uncontended)
|
||||
*
|
||||
* @param window Rolling window to update
|
||||
* @param word_id Word that just executed
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int rolling_window_record_execution(RollingWindowOfTruth* window, uint32_t word_id);
|
||||
|
||||
/*
|
||||
* Get recent execution sequence for context.
|
||||
* Returns pointer to last N words (or fewer if not enough executions yet).
|
||||
*
|
||||
* @param window Rolling window
|
||||
* @param depth How many previous words to return (1 to ROLLING_WINDOW_SIZE)
|
||||
* @param out_sequence Output array (caller allocates, must be >= depth)
|
||||
* @return Number of words written to out_sequence (may be < depth if cold start)
|
||||
*/
|
||||
uint32_t rolling_window_get_recent_sequence(const RollingWindowOfTruth* window,
|
||||
uint32_t depth,
|
||||
uint32_t* out_sequence);
|
||||
|
||||
/*
|
||||
* Get most frequent word in rolling window.
|
||||
* Useful for identifying hot words immediately after POST.
|
||||
*
|
||||
* @param window Rolling window
|
||||
* @param dict_size Total number of words in dictionary (for bounds check)
|
||||
* @return Word ID of most frequent execution, or 0 if window not warm
|
||||
*/
|
||||
uint32_t rolling_window_find_hottest_word(const RollingWindowOfTruth* window,
|
||||
uint32_t dict_size);
|
||||
|
||||
/*
|
||||
* Get transition frequency: how often word_b follows word_a in window.
|
||||
*
|
||||
* Useful for seeding pipelining context predictions.
|
||||
*
|
||||
* @param window Rolling window
|
||||
* @param word_a First word in sequence
|
||||
* @param word_b Second word in sequence
|
||||
* @return Count of (word_a → word_b) transitions in window
|
||||
*/
|
||||
uint64_t rolling_window_count_transition(const RollingWindowOfTruth* window,
|
||||
uint32_t word_a,
|
||||
uint32_t word_b);
|
||||
|
||||
/*
|
||||
* Check if window is warm (contains representative data).
|
||||
* Seeding functions should check this before using window data.
|
||||
*
|
||||
* @param window Rolling window
|
||||
* @return 1 if window is warm (>= ROLLING_WINDOW_SIZE executions), 0 otherwise
|
||||
*/
|
||||
int rolling_window_is_warm(const RollingWindowOfTruth* window);
|
||||
|
||||
/*
|
||||
* Get detailed statistics string for diagnostics.
|
||||
*
|
||||
* @param window Rolling window
|
||||
* @return Formatted string (caller must free)
|
||||
*/
|
||||
char* rolling_window_stats_string(const RollingWindowOfTruth* window);
|
||||
|
||||
/*
|
||||
* Reset rolling window (clear all data).
|
||||
*
|
||||
* @param window Rolling window to reset
|
||||
*/
|
||||
void rolling_window_reset(RollingWindowOfTruth * window);
|
||||
|
||||
/*
|
||||
* Free all memory allocated for rolling window.
|
||||
* Called during VM cleanup.
|
||||
*
|
||||
* @param window Rolling window to clean up
|
||||
*/
|
||||
void rolling_window_cleanup(RollingWindowOfTruth * window);
|
||||
|
||||
/* ============================================================================
|
||||
* Bootstrap Data Analysis API
|
||||
* ============================================================================
|
||||
*
|
||||
* Used to analyze rolling window data and determine statistically significant
|
||||
* window sizes for cold-start systems (un-physics VM).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Export full execution history for external analysis.
|
||||
* Used by bootstrap analyzer to determine optimal window size.
|
||||
*
|
||||
* Returns the complete circular buffer linearized in chronological order.
|
||||
* Caller allocates output buffer; function fills it with execution sequence.
|
||||
*
|
||||
* @param window Rolling window to export
|
||||
* @param out_sequence Output buffer (caller allocates, must be >= total_executions)
|
||||
* @param max_count Maximum entries to write
|
||||
* @return Number of word IDs written to out_sequence
|
||||
*/
|
||||
uint64_t rolling_window_export_execution_history(const RollingWindowOfTruth* window,
|
||||
uint32_t* out_sequence,
|
||||
uint64_t max_count);
|
||||
|
||||
/*
|
||||
* Analyze pattern capture rate for a given window size.
|
||||
* Measures what percentage of unique transitions are captured.
|
||||
*
|
||||
* Algorithm:
|
||||
* 1. Scan execution history with sliding window of given size
|
||||
* 2. Count unique (context, next_word) patterns observed
|
||||
* 3. Return: (patterns_captured / total_unique_patterns) * 100
|
||||
*
|
||||
* Used for offline analysis to determine reasonable starting window size.
|
||||
*
|
||||
* @param window Rolling window with execution data
|
||||
* @param test_window_size Window size to test (e.g., 256, 512, 1024)
|
||||
* @return Pattern capture rate (0-100%)
|
||||
*/
|
||||
double rolling_window_pattern_capture_rate(const RollingWindowOfTruth* window,
|
||||
uint32_t test_window_size);
|
||||
|
||||
/* ============================================================================
|
||||
* Adaptive Window Sizing API
|
||||
* ============================================================================
|
||||
*
|
||||
* Automatic continuous self-tuning of window size during execution.
|
||||
* Called automatically during normal operation - no user intervention needed.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Measure pattern diversity in rolling window.
|
||||
* Counts unique adjacent transitions (word_a → word_b) to indicate workload variability.
|
||||
*
|
||||
* Used by dictionary heat optimization to decide lookup strategy:
|
||||
* - High diversity (>70) = use heat-aware lookup (patterns are varied)
|
||||
* - Low diversity (<30) = use naive lookup (patterns are stable)
|
||||
*
|
||||
* @param window Rolling window to analyze
|
||||
* @return Count of unique transitions in window
|
||||
*/
|
||||
uint64_t rolling_window_measure_diversity(const RollingWindowOfTruth* window);
|
||||
|
||||
/*
|
||||
* Check and apply adaptive window shrinking.
|
||||
* Called periodically by rolling_window_record_execution.
|
||||
* Measures pattern diversity and shrinks window if diminishing returns detected.
|
||||
*
|
||||
* This function is called automatically - users never invoke it directly.
|
||||
* It runs every 256 executions after window becomes warm.
|
||||
*
|
||||
* @param window Rolling window (modified in-place if shrinking occurs)
|
||||
*/
|
||||
void rolling_window_check_adaptive_shrink(RollingWindowOfTruth* window);
|
||||
void rolling_window_service(RollingWindowOfTruth* window);
|
||||
|
||||
#endif /* ROLLING_WINDOW_OF_TRUTH_H */
|
||||
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
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 SSM_JACQUARD_H
|
||||
#define SSM_JACQUARD_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "compudynamics.h"
|
||||
#include "starforth_config.h"
|
||||
|
||||
/* ============================================================================
|
||||
* SSM L8: Jacquard Mode Selector (Data-Driven Architecture)
|
||||
* ============================================================================
|
||||
*
|
||||
* Based on 2^7 DoE with 300 reps (128 configurations, 38,400 total runs).
|
||||
* Top 5% analysis reveals optimal loop combinations for speed + stability.
|
||||
*
|
||||
* Architecture (Experimentally Validated):
|
||||
* - L1 (heat_tracking): DISABLED (harmful in 86% of top configs)
|
||||
* - L4 (pipelining_metrics): DISABLED (harmful in 100% of top configs)
|
||||
* - L7 (adaptive_heartrate): ALWAYS ON (beneficial in 71% of top configs)
|
||||
* - L2, L3, L5, L6: Runtime-controlled by L8 (workload-dependent)
|
||||
* - L8 (Jacquard): 4-bit selector (16 modes) controlling L2/L3/L5/L6
|
||||
*
|
||||
* L8 operates as a multi-dimensional classifier:
|
||||
* L2 (window): ON if entropy > 0.75 (diversity tracking)
|
||||
* L3 (decay): ON if temporal_decay > 0.5 (temporal locality)
|
||||
* L5 (window_inf): ON if cv > 0.15 (variance adaptation)
|
||||
* L6 (decay_inf): ON if cv > 0.15 AND temporal_decay > 0.3
|
||||
*
|
||||
* Top 5% Validated Modes:
|
||||
* C4 (0100): L2=0, L3=1, L5=0, L6=0 - Temporal locality
|
||||
* C7 (0111): L2=0, L3=1, L5=1, L6=1 - Full inference
|
||||
* C9 (1001): L2=1, L3=0, L5=0, L6=1 - Diverse + decay_inf
|
||||
* C11 (1011): L2=1, L3=0, L5=1, L6=1 - Diverse + inference
|
||||
* C12 (1100): L2=1, L3=1, L5=0, L6=0 - Diverse + temporal
|
||||
*/
|
||||
|
||||
/* ============================================================================
|
||||
* L8 Mode Definitions (4-bit: L2/L3/L5/L6)
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
/* Bits: L2 L3 L5 L6 */
|
||||
SSM_MODE_C0 = 0x0, /* 0000: Minimal (stable/predictable workloads) */
|
||||
SSM_MODE_C1 = 0x1, /* 0001: L6 only (decay inference) */
|
||||
SSM_MODE_C2 = 0x2, /* 0010: L5 only (window inference) */
|
||||
SSM_MODE_C3 = 0x3, /* 0011: L5+L6 (volatile workloads) */
|
||||
SSM_MODE_C4 = 0x4, /* 0100: L3 only (temporal locality) ✅ TOP 5% */
|
||||
SSM_MODE_C5 = 0x5, /* 0101: L3+L6 (temporal + decay_inf) */
|
||||
SSM_MODE_C6 = 0x6, /* 0110: L3+L5 (temporal + window_inf) */
|
||||
SSM_MODE_C7 = 0x7, /* 0111: L3+L5+L6 (full inference) ✅ TOP 5% */
|
||||
SSM_MODE_C8 = 0x8, /* 1000: L2 only (high diversity) */
|
||||
SSM_MODE_C9 = 0x9, /* 1001: L2+L6 (diverse + decay_inf) ✅ TOP 5% */
|
||||
SSM_MODE_C10 = 0xA, /* 1010: L2+L5 (diverse + window_inf) */
|
||||
SSM_MODE_C11 = 0xB, /* 1011: L2+L5+L6 (diverse + inference) ✅ TOP 5% */
|
||||
SSM_MODE_C12 = 0xC, /* 1100: L2+L3 (diverse + temporal) ✅ TOP 5% */
|
||||
SSM_MODE_C13 = 0xD, /* 1101: L2+L3+L6 (complex workload) */
|
||||
SSM_MODE_C14 = 0xE, /* 1110: L2+L3+L5 (full adaptive, no decay_inf) */
|
||||
SSM_MODE_C15 = 0xF /* 1111: L2+L3+L5+L6 (full adaptive, all on) */
|
||||
} ssm_l8_mode_t;
|
||||
|
||||
/* ============================================================================
|
||||
* L8 Metrics (Input to Mode Selection)
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
double entropy; /* Rolling-window entropy/diversity (0.0-1.0) */
|
||||
double cv; /* Coefficient of variation (short-term volatility) */
|
||||
double temporal_decay; /* Temporal locality strength (0.0-1.0) */
|
||||
double stability_score; /* Combined stability metric for hysteresis */
|
||||
int inference_ran_this_tick; /* 1 if inference engine ran this tick */
|
||||
int inference_early_exited; /* 1 if inference early-exited (ANOVA stable) */
|
||||
} ssm_l8_metrics_t;
|
||||
|
||||
/* ============================================================================
|
||||
* SSM Configuration (L1-L7 Mode Bits)
|
||||
* ============================================================================
|
||||
*
|
||||
* L1/L4/L7 were runtime-inert until this field expansion: the adaptive
|
||||
* table (SsmConfigTable, below) always selected across the full 128-config
|
||||
* (7-bit) space, but ssm_apply_mode_from_table() and its per-tick scoring
|
||||
* counterpart only ever wrote 4 of those 7 bits out here, so L1/L4/L7 were computed by
|
||||
* the bandit's UCB selection and then silently discarded before reaching
|
||||
* any real gate -- those three loops were governed purely by compile-time
|
||||
* macros (ENABLE_HOTWORDS_CACHE, ENABLE_PIPELINING, HEARTBEAT_THREAD_ENABLED)
|
||||
* for the whole process lifetime. Adding these fields, and wiring them at
|
||||
* their four call sites alongside (not instead of) the compile-time macros,
|
||||
* makes the bandit's full selection actually take effect. */
|
||||
typedef struct {
|
||||
int L1_heat_tracking; /* 1 = hotwords cache active, 0 = off */
|
||||
int L2_rolling_window; /* 1 = window tracking active, 0 = off */
|
||||
int L3_linear_decay; /* 1 = decay active, 0 = off */
|
||||
int L4_pipelining; /* 1 = pipelining metrics active, 0 = off */
|
||||
int L5_window_inference; /* 1 = window inference active, 0 = off */
|
||||
int L6_decay_inference; /* 1 = decay inference active, 0 = off */
|
||||
int L7_adaptive_heartrate; /* 1 = heartbeat thread active, 0 = off */
|
||||
} ssm_config_t;
|
||||
|
||||
/* ============================================================================
|
||||
* L8 Configuration Thresholds (Data-Driven from DoE)
|
||||
* ============================================================================
|
||||
* Defaults live in starforth_config.h (included above), the single source
|
||||
* of truth for VM build-time defaults; overridable via -D exactly as before.
|
||||
*/
|
||||
|
||||
/* ============================================================================
|
||||
* Adaptive Table Constants (L8 Heat-Ranked 128-Config Selector)
|
||||
* One clock only: the heartbeat tick (rev r). Scoring and UCB reselection
|
||||
* both run every tick -- no derived multi-tick batching unit exists
|
||||
* anywhere in this mechanism. The score/UCB/reward/weight constants live
|
||||
* in cd_tuning_word() (compudynamics.h/.c) now, not here -- this is the
|
||||
* word-level instantiation of the generic compudynamics module (rev s).
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/* ============================================================================
|
||||
* 7-bit Config Bit Positions (b6=L1, b5=L2, b4=L3, b3=L4, b2=L5, b1=L6, b0=L7)
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
#define SSM_CFG_L1 0x40u
|
||||
#define SSM_CFG_L2 0x20u
|
||||
#define SSM_CFG_L3 0x10u
|
||||
#define SSM_CFG_L4 0x08u
|
||||
#define SSM_CFG_L5 0x04u
|
||||
#define SSM_CFG_L6 0x02u
|
||||
#define SSM_CFG_L7 0x01u
|
||||
|
||||
/* ============================================================================
|
||||
* L8 State (extended with adaptive table pointer)
|
||||
* ============================================================================
|
||||
*
|
||||
* The adaptive table itself (CDConfigEntry/CDConfigTable) is the generic
|
||||
* compudynamics bandit (compudynamics.h), sized to 128 configs x 8 regimes
|
||||
* via cd_tuning_word() -- see ssm_l8_init_table().
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
ssm_l8_mode_t current_mode; /* Mirrored from table for diagnostics */
|
||||
uint32_t hysteresis_counter; /* Legacy threshold mode hysteresis */
|
||||
ssm_l8_mode_t pending_mode; /* Legacy threshold mode pending */
|
||||
CDConfigTable *table; /* NULL = legacy threshold; non-NULL = adaptive */
|
||||
} ssm_l8_state_t;
|
||||
|
||||
/* ============================================================================
|
||||
* L8 API
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialize L8 state (legacy fields only; call ssm_l8_init_table separately)
|
||||
*/
|
||||
void ssm_l8_init(ssm_l8_state_t *state, ssm_l8_mode_t initial_mode);
|
||||
|
||||
/**
|
||||
* @brief Allocate and seed the adaptive config table
|
||||
* Sets state->table; must be called after ssm_l8_init().
|
||||
* On allocation failure, state->table remains NULL (legacy mode is used).
|
||||
*/
|
||||
void ssm_l8_init_table(ssm_l8_state_t *state);
|
||||
|
||||
/**
|
||||
* @brief Free the adaptive config table
|
||||
*/
|
||||
void ssm_l8_free_table(ssm_l8_state_t *state);
|
||||
|
||||
/*
|
||||
* @brief Per-tick update for the adaptive table path
|
||||
*
|
||||
* Classifies the current regime, scores the current config from this
|
||||
* tick's outcome, and UCB-reselects -- every call, no batching -- and
|
||||
* applies the selected config to ssm_config_t.
|
||||
*
|
||||
* The locality component of the regime classification (and of the
|
||||
* joint-convergence reward signal) is computed here from recent_word_ids
|
||||
* via cd_classify_ids() -- purely execution-count-derived, no wall-clock
|
||||
* input. This replaced a decay-slope-derived temporal_decay signal (rev
|
||||
* r) that made L8's regime classification wall-clock-tainted once it ran
|
||||
* every tick instead of once per ~2000 ticks, breaking cross-architecture
|
||||
* dict_hash determinism under QEMU/TCG's differing real emulation speed
|
||||
* per ISA (rev s).
|
||||
*
|
||||
* @param state L8 state (must have state->table != NULL)
|
||||
* @param metrics Current runtime metrics (entropy, cv, inference status)
|
||||
* @param config SSM config updated this tick
|
||||
* @param current_window VM's effective_window_size this tick
|
||||
* @param recent_word_ids Recent word-execution-ID history, oldest first
|
||||
* (e.g. from rolling_window_get_recent_sequence())
|
||||
* @param recent_word_ids_count Number of valid entries in recent_word_ids
|
||||
*/
|
||||
void ssm_l8_update_table(ssm_l8_state_t *state, const ssm_l8_metrics_t *metrics,
|
||||
ssm_config_t *config,
|
||||
uint32_t current_window,
|
||||
const uint32_t *recent_word_ids, uint32_t recent_word_ids_count);
|
||||
|
||||
/**
|
||||
* @brief Apply the current table config to ssm_config_t
|
||||
* Used on first tick to set the initial DoE-seeded config immediately.
|
||||
*/
|
||||
void ssm_apply_mode_from_table(const ssm_l8_state_t *state, ssm_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Force the adaptive table onto an externally-chosen config
|
||||
* (e.g. a DoE campaign driving a specific tick's config rather than
|
||||
* letting the bandit's own UCB selection choose it). See definition for
|
||||
* details on why this stays coherent with the bandit's own reward loop.
|
||||
*/
|
||||
void ssm_l8_force_config(ssm_l8_state_t *state, ssm_config_t *config, uint8_t config_idx);
|
||||
|
||||
/**
|
||||
* @brief Update L8 state based on current metrics (legacy threshold path)
|
||||
*/
|
||||
void ssm_l8_update(const ssm_l8_metrics_t *metrics, ssm_l8_state_t *state);
|
||||
|
||||
/**
|
||||
* @brief Apply L8 mode to SSM configuration (legacy threshold path)
|
||||
*/
|
||||
void ssm_apply_mode(const ssm_l8_state_t *state, ssm_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Get human-readable mode name
|
||||
*/
|
||||
const char* ssm_l8_mode_name(ssm_l8_mode_t mode);
|
||||
|
||||
#endif /* SSM_JACQUARD_H */
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* StarForth VM Feature Configuration
|
||||
*
|
||||
* This header is the single source of truth for VM build-time defaults.
|
||||
* Build systems may override any option via -D<FLAG>=<value>, but every
|
||||
* translation unit sees these defaults even if no -D is provided.
|
||||
*
|
||||
* Hosted + kernel builds both force-include this header (see Makefiles),
|
||||
* so new toggles belong here with a documented owner.
|
||||
*/
|
||||
|
||||
#ifndef STARFORTH_CONFIG_H
|
||||
#define STARFORTH_CONFIG_H
|
||||
|
||||
/* Base defaults (match 2025-12-26 hosted baseline) */
|
||||
#define STARFORTH_CONFIG_STRICT_PTR_DEFAULT 1
|
||||
#define STARFORTH_CONFIG_ENABLE_HOTWORDS_CACHE_DEFAULT 0
|
||||
#define STARFORTH_CONFIG_ENABLE_PIPELINING_DEFAULT 0
|
||||
#define STARFORTH_CONFIG_ROLLING_WINDOW_SIZE_DEFAULT 4096
|
||||
#define STARFORTH_CONFIG_TRANSITION_WINDOW_SIZE_DEFAULT 8
|
||||
#define STARFORTH_CONFIG_ADAPTIVE_SHRINK_RATE_DEFAULT 50
|
||||
#define STARFORTH_CONFIG_ADAPTIVE_MIN_WINDOW_SIZE_DEFAULT 256
|
||||
#define STARFORTH_CONFIG_ADAPTIVE_CHECK_FREQUENCY_DEFAULT 512
|
||||
#define STARFORTH_CONFIG_ADAPTIVE_GROWTH_THRESHOLD_DEFAULT 5
|
||||
#define STARFORTH_CONFIG_INITIAL_DECAY_SLOPE_Q48_DEFAULT 21845
|
||||
#define STARFORTH_CONFIG_DECAY_MIN_INTERVAL_DEFAULT 1000ULL
|
||||
#define STARFORTH_CONFIG_DECAY_RATE_PER_US_Q16_DEFAULT 1
|
||||
#define STARFORTH_CONFIG_HEARTBEAT_THREAD_ENABLED_DEFAULT 1
|
||||
#define STARFORTH_CONFIG_HEARTBEAT_TICK_NS_DEFAULT 10000ULL
|
||||
#define STARFORTH_CONFIG_HEARTBEAT_INFERENCE_FREQUENCY_DEFAULT 1000
|
||||
#define STARFORTH_CONFIG_STADIUM_VM_MEMORY_PERCENT_DEFAULT 50
|
||||
#define STARFORTH_CONFIG_STADIUM_CONTAINS_DEPTH_MAX_DEFAULT 5
|
||||
#define STARFORTH_CONFIG_STADIUM_CAPACITY_TICK_DEFAULT 4000
|
||||
#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_STADIUM_BLOCK_HEAT_QUANTUM_DEFAULT 2048
|
||||
#define STARFORTH_CONFIG_STADIUM_BLOCK_COOL_RATE_Q48_DEFAULT 21845
|
||||
#define STARFORTH_CONFIG_STADIUM_BLOCK_TRACK_CAP_MULT_DEFAULT 2
|
||||
#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
|
||||
#define STARFORTH_CONFIG_SSM_ENTROPY_HIGH_THRESHOLD_DEFAULT 0.75
|
||||
#define STARFORTH_CONFIG_SSM_CV_HIGH_THRESHOLD_DEFAULT 0.15
|
||||
#define STARFORTH_CONFIG_SSM_TEMPORAL_DECAY_THRESHOLD_DEFAULT 0.5
|
||||
#define STARFORTH_CONFIG_SSM_TEMPORAL_DECAY_LOW_THRESHOLD_DEFAULT 0.3
|
||||
#define STARFORTH_CONFIG_SSM_HYSTERESIS_TICKS_DEFAULT 5
|
||||
#define STARFORTH_CONFIG_SPECULATION_THRESHOLD_Q48_DEFAULT (0x8000LL)
|
||||
#define STARFORTH_CONFIG_SPECULATION_DEPTH_DEFAULT 1
|
||||
#define STARFORTH_CONFIG_MIN_SAMPLES_FOR_SPECULATION_DEFAULT 10
|
||||
#define STARFORTH_CONFIG_MISPREDICTION_COST_Q48_DEFAULT (25LL << 16)
|
||||
/* 1.10 in Q48.16 (1.10 * 65536 = 72089.6, rounds to 0x1199A). Was shipped
|
||||
* for years as 0x11999AL (an extra hex digit, ~17.6 in Q48.16, an order of
|
||||
* magnitude off from the documented 1.10 intent) -- found while migrating
|
||||
* this constant to Kconfig (see rev x in
|
||||
* docs/working/architecture/VM-FLEET-ATTRACTOR-DESIGN-20260705.md) and
|
||||
* fixed on Bob's explicit instruction (rev y). */
|
||||
#define STARFORTH_CONFIG_MINIMUM_PREFETCH_ROI_DEFAULT (0x1199AL)
|
||||
|
||||
/* Public macros (overridable via -D) */
|
||||
#ifndef STRICT_PTR
|
||||
#define STRICT_PTR STARFORTH_CONFIG_STRICT_PTR_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef VM_STRICT_PTR
|
||||
#define VM_STRICT_PTR STRICT_PTR
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_HOTWORDS_CACHE
|
||||
#define ENABLE_HOTWORDS_CACHE STARFORTH_CONFIG_ENABLE_HOTWORDS_CACHE_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_PIPELINING
|
||||
#define ENABLE_PIPELINING STARFORTH_CONFIG_ENABLE_PIPELINING_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef ROLLING_WINDOW_SIZE
|
||||
#define ROLLING_WINDOW_SIZE STARFORTH_CONFIG_ROLLING_WINDOW_SIZE_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef TRANSITION_WINDOW_SIZE
|
||||
#define TRANSITION_WINDOW_SIZE STARFORTH_CONFIG_TRANSITION_WINDOW_SIZE_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef ADAPTIVE_SHRINK_RATE
|
||||
#define ADAPTIVE_SHRINK_RATE STARFORTH_CONFIG_ADAPTIVE_SHRINK_RATE_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef ADAPTIVE_MIN_WINDOW_SIZE
|
||||
#define ADAPTIVE_MIN_WINDOW_SIZE STARFORTH_CONFIG_ADAPTIVE_MIN_WINDOW_SIZE_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef ADAPTIVE_CHECK_FREQUENCY
|
||||
#define ADAPTIVE_CHECK_FREQUENCY STARFORTH_CONFIG_ADAPTIVE_CHECK_FREQUENCY_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef ADAPTIVE_GROWTH_THRESHOLD
|
||||
#define ADAPTIVE_GROWTH_THRESHOLD STARFORTH_CONFIG_ADAPTIVE_GROWTH_THRESHOLD_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef INITIAL_DECAY_SLOPE_Q48
|
||||
#define INITIAL_DECAY_SLOPE_Q48 STARFORTH_CONFIG_INITIAL_DECAY_SLOPE_Q48_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef DECAY_MIN_INTERVAL
|
||||
#define DECAY_MIN_INTERVAL STARFORTH_CONFIG_DECAY_MIN_INTERVAL_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef DECAY_RATE_PER_US_Q16
|
||||
#define DECAY_RATE_PER_US_Q16 STARFORTH_CONFIG_DECAY_RATE_PER_US_Q16_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef HEARTBEAT_THREAD_ENABLED
|
||||
#define HEARTBEAT_THREAD_ENABLED STARFORTH_CONFIG_HEARTBEAT_THREAD_ENABLED_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef HEARTBEAT_TICK_NS
|
||||
#define HEARTBEAT_TICK_NS STARFORTH_CONFIG_HEARTBEAT_TICK_NS_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef HEARTBEAT_INFERENCE_FREQUENCY
|
||||
#define HEARTBEAT_INFERENCE_FREQUENCY STARFORTH_CONFIG_HEARTBEAT_INFERENCE_FREQUENCY_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef STADIUM_VM_MEMORY_PERCENT
|
||||
#define STADIUM_VM_MEMORY_PERCENT STARFORTH_CONFIG_STADIUM_VM_MEMORY_PERCENT_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef STADIUM_CONTAINS_DEPTH_MAX
|
||||
#define STADIUM_CONTAINS_DEPTH_MAX STARFORTH_CONFIG_STADIUM_CONTAINS_DEPTH_MAX_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef STADIUM_CAPACITY_TICK
|
||||
#define STADIUM_CAPACITY_TICK STARFORTH_CONFIG_STADIUM_CAPACITY_TICK_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef STADIUM_MEMORY_PERCENT
|
||||
#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 STADIUM_BLOCK_HEAT_QUANTUM
|
||||
#define STADIUM_BLOCK_HEAT_QUANTUM STARFORTH_CONFIG_STADIUM_BLOCK_HEAT_QUANTUM_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef STADIUM_BLOCK_COOL_RATE_Q48
|
||||
#define STADIUM_BLOCK_COOL_RATE_Q48 STARFORTH_CONFIG_STADIUM_BLOCK_COOL_RATE_Q48_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef STADIUM_BLOCK_TRACK_CAP_MULT
|
||||
#define STADIUM_BLOCK_TRACK_CAP_MULT STARFORTH_CONFIG_STADIUM_BLOCK_TRACK_CAP_MULT_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef HEARTBEAT_CHECK_FREQUENCY
|
||||
#define HEARTBEAT_CHECK_FREQUENCY STARFORTH_CONFIG_HEARTBEAT_CHECK_FREQUENCY_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef HEARTBEAT_WINDOW_TUNING_FREQUENCY
|
||||
#define HEARTBEAT_WINDOW_TUNING_FREQUENCY STARFORTH_CONFIG_HEARTBEAT_WINDOW_TUNING_FREQUENCY_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef HEARTBEAT_SLOPE_VALIDATION_FREQUENCY
|
||||
#define HEARTBEAT_SLOPE_VALIDATION_FREQUENCY STARFORTH_CONFIG_HEARTBEAT_SLOPE_VALIDATION_FREQUENCY_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef SSM_ENTROPY_HIGH_THRESHOLD
|
||||
#define SSM_ENTROPY_HIGH_THRESHOLD STARFORTH_CONFIG_SSM_ENTROPY_HIGH_THRESHOLD_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef SSM_CV_HIGH_THRESHOLD
|
||||
#define SSM_CV_HIGH_THRESHOLD STARFORTH_CONFIG_SSM_CV_HIGH_THRESHOLD_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef SSM_TEMPORAL_DECAY_THRESHOLD
|
||||
#define SSM_TEMPORAL_DECAY_THRESHOLD STARFORTH_CONFIG_SSM_TEMPORAL_DECAY_THRESHOLD_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef SSM_TEMPORAL_DECAY_LOW_THRESHOLD
|
||||
#define SSM_TEMPORAL_DECAY_LOW_THRESHOLD STARFORTH_CONFIG_SSM_TEMPORAL_DECAY_LOW_THRESHOLD_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef SSM_HYSTERESIS_TICKS
|
||||
#define SSM_HYSTERESIS_TICKS STARFORTH_CONFIG_SSM_HYSTERESIS_TICKS_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef SPECULATION_THRESHOLD_Q48
|
||||
#define SPECULATION_THRESHOLD_Q48 STARFORTH_CONFIG_SPECULATION_THRESHOLD_Q48_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef SPECULATION_DEPTH
|
||||
#define SPECULATION_DEPTH STARFORTH_CONFIG_SPECULATION_DEPTH_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef MIN_SAMPLES_FOR_SPECULATION
|
||||
#define MIN_SAMPLES_FOR_SPECULATION STARFORTH_CONFIG_MIN_SAMPLES_FOR_SPECULATION_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef MISPREDICTION_COST_Q48
|
||||
#define MISPREDICTION_COST_Q48 STARFORTH_CONFIG_MISPREDICTION_COST_Q48_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef MINIMUM_PREFETCH_ROI
|
||||
#define MINIMUM_PREFETCH_ROI STARFORTH_CONFIG_MINIMUM_PREFETCH_ROI_DEFAULT
|
||||
#endif
|
||||
|
||||
#endif /* STARFORTH_CONFIG_H */
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
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 STARFORTH_STARTUP_H
|
||||
#define STARFORTH_STARTUP_H
|
||||
|
||||
#include "vm.h"
|
||||
#include "cli.h"
|
||||
|
||||
/**
|
||||
* @brief Initialize block I/O device from CLI configuration
|
||||
*
|
||||
* Opens disk image or allocates RAM disk based on CLIConfig.
|
||||
* Must be called before vm_init().
|
||||
*
|
||||
* @param config CLI configuration
|
||||
* @return 0 on success, 1 on error (logs messages internally)
|
||||
*/
|
||||
int startup_blkio_init(const CLIConfig* config);
|
||||
|
||||
/**
|
||||
* @brief Clean up block I/O device
|
||||
*
|
||||
* Flushes and closes block device, frees RAM disk if allocated.
|
||||
* Called automatically via atexit().
|
||||
*/
|
||||
void startup_blkio_cleanup(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize VM with defaults
|
||||
*
|
||||
* Initializes the VM state after block device is ready.
|
||||
* @param vm Pointer to VM to initialize
|
||||
* @return 0 on success, 1 on error
|
||||
*/
|
||||
int startup_vm_init(VM * vm);
|
||||
|
||||
/**
|
||||
* @brief Initialize block subsystem
|
||||
*
|
||||
* Sets up block buffering and attaches blkio device.
|
||||
* @param vm Pointer to initialized VM
|
||||
* @return 0 on success, 1 on error
|
||||
*/
|
||||
int startup_block_subsys_init(VM * vm);
|
||||
|
||||
#endif /* STARFORTH_STARTUP_H */
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef STARFORTH_VERSION_H
|
||||
#define STARFORTH_VERSION_H
|
||||
|
||||
#define STARFORTH_VERSION "3.2.0"
|
||||
#define STARFORTH_ARCH "x86_64"
|
||||
#define STARFORTH_TARGET "standard"
|
||||
#define STARFORTH_TIMESTAMP "2026-10-01T15:21:25-04:00"
|
||||
#define STARFORTH_VERSION_FULL "StarForth v3.2.0 x86_64 standard 2026-10-01T15:21:25-04:00"
|
||||
|
||||
#endif /* STARFORTH_VERSION_H */
|
||||
+887
@@ -0,0 +1,887 @@
|
||||
/*
|
||||
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);
|
||||
|
||||
/* Numeric BASE accessors: keep the FORTH-visible BASE cell (vm->base_addr)
|
||||
* and the host-mirror field (vm->base, read by number-output words via
|
||||
* current_base()) in sync. Always use these instead of writing base_addr
|
||||
* directly. */
|
||||
unsigned vm_get_base(const struct VM* vm);
|
||||
void vm_set_base(struct VM* vm, unsigned b);
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Per-VM vocabulary (VOCABULARY/DEFINITIONS/CONTEXT/CURRENT/FORTH) state.
|
||||
* Was a set of file-scope statics in vocabulary_words.c, shared by every VM
|
||||
* in the process -- one VM's VOCABULARY/DEFINITIONS/FORTH silently changed
|
||||
* where every other VM looked up and defined words (proof/FINDINGS.md #1,
|
||||
* item #5, "highest severity in the sweep"). Moved onto struct VM so each
|
||||
* VM in the fleet keeps its own vocabulary chains, search order, and VM-cell
|
||||
* addresses -- same shape as hold_addr/hold_pos below. */
|
||||
#define VOCAB_FC_BUCKETS 256
|
||||
typedef struct {
|
||||
int initialized;
|
||||
|
||||
/* Host-side vocabulary chain heads */
|
||||
DictEntry *forth_vocab; /* FORTH vocabulary head (root of system) */
|
||||
DictEntry *context_vocab; /* CONTEXT vocabulary head */
|
||||
DictEntry *current_vocab; /* CURRENT vocabulary head */
|
||||
|
||||
/* VM-visible variables: cell addresses in this VM's own memory */
|
||||
vaddr_t context_var_addr; /* cell containing (DictEntry*) CONTEXT */
|
||||
vaddr_t current_var_addr; /* cell containing (DictEntry*) CURRENT */
|
||||
|
||||
/* First-character search index per chain, lazily rebuilt when that
|
||||
* chain's head changes (see vocab_find_word in vocabulary_words.c) */
|
||||
DictEntry **ctx_fc[VOCAB_FC_BUCKETS];
|
||||
size_t ctx_n[VOCAB_FC_BUCKETS];
|
||||
DictEntry *ctx_cached_head;
|
||||
|
||||
DictEntry **forth_fc[VOCAB_FC_BUCKETS];
|
||||
size_t forth_n[VOCAB_FC_BUCKETS];
|
||||
DictEntry *forth_cached_head;
|
||||
} VocabularyState;
|
||||
|
||||
/* Compile-time control-flow bookkeeping for IF/ELSE/THEN, BEGIN/WHILE/REPEAT/
|
||||
* UNTIL/AGAIN, DO/?DO/LOOP/+LOOP/LEAVE, and CASE/OF/ENDOF/ENDCASE.
|
||||
* Was a set of file-scope statics in control_words.c, shared by every VM in
|
||||
* the process -- two VMs compiling control structures at overlapping times
|
||||
* would corrupt each other's nesting state (proof/FINDINGS.md #1, item #1,
|
||||
* "the two that would actually corrupt VM behavior in the live Tripod fleet
|
||||
* today"). Moved onto struct VM so each VM keeps its own compile-time stacks. */
|
||||
#define CF_STACK_MAX 64
|
||||
|
||||
typedef enum {
|
||||
CF_BEGIN, /* Address of BEGIN target */
|
||||
CF_IF, /* Address of IF's 0BRANCH literal */
|
||||
CF_ELSE, /* Address of ELSE's BRANCH literal */
|
||||
CF_WHILE, /* Address of WHILE's 0BRANCH literal (paired with prior BEGIN) */
|
||||
CF_DO, /* Address of loop body start (back target for LOOP/+LOOP) */
|
||||
CF_CASE, /* Marker for CASE statement start */
|
||||
CF_OF /* Address of OF's 0BRANCH literal */
|
||||
} cf_tag_t;
|
||||
|
||||
typedef struct {
|
||||
size_t addr; /* byte offset in vm->memory used for patching/back edges */
|
||||
cf_tag_t tag;
|
||||
} cf_item_t;
|
||||
|
||||
typedef struct {
|
||||
cf_item_t cf_stack[CF_STACK_MAX];
|
||||
int cf_sp;
|
||||
int cf_last_mode; /* reset cf_stack on mode transitions (INTERPRET/COMPILE) */
|
||||
|
||||
/* LEAVE patch sites: one entry per LEAVE, collected until LOOP/+LOOP */
|
||||
size_t leave_addrs[CF_STACK_MAX];
|
||||
int leave_sp;
|
||||
/* One mark per DO nesting: leave_sp at DO/?DO entry, restored at LOOP/+LOOP */
|
||||
int leave_mark_stack[CF_STACK_MAX];
|
||||
int leave_mark_sp;
|
||||
|
||||
/* ENDOF patch sites: one entry per ENDOF, collected until ENDCASE */
|
||||
size_t endof_addrs[CF_STACK_MAX];
|
||||
int endof_sp;
|
||||
/* One mark per CASE nesting: endof_sp at CASE entry, restored at ENDCASE */
|
||||
int endof_mark_stack[CF_STACK_MAX];
|
||||
int endof_mark_sp;
|
||||
} ControlFlowState;
|
||||
|
||||
#ifdef __STARKERNEL__
|
||||
#include "starkernel/vm_uuid.h" /* VMUuid -- item 4.2, VM.stadium_vm_id */
|
||||
#include "starkernel/vm_identity.h" /* VMIdentity -- FABRIC-2.md §F.2/§F.16 */
|
||||
|
||||
/* 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) */
|
||||
uint8_t zuse_cert_installed; /**< 1 = zuse_cert_seed/pubkey hold a real minted cert (one-time fuse) */
|
||||
uint8_t zuse_cert_seed[32]; /**< Ed25519 seed, this instance's Zuse identity. C-only write via
|
||||
* vm_zuse_cert_install(); no FORTH word can read or write it --
|
||||
* the seed is the private key, never exposed past this struct. */
|
||||
uint8_t zuse_cert_pubkey[32]; /**< Ed25519 public key derived from the seed at mint time, cached
|
||||
* here so callers don't need to re-derive it. No FORTH word can
|
||||
* write it -- deliberately kept out of the dictionary so ACL-PIN's
|
||||
* redefinition-only guarantee can't be bypassed via >BODY on a
|
||||
* CONSTANT (see FABRIC-2.md). Read-only FORTH access via
|
||||
* ZUSE-PUBKEY@. */
|
||||
uint8_t zuse_root_pubkey_known; /**< 1 = zuse_root_pubkey holds a real value, loaded from the
|
||||
* persistent genesis-marker fence (zuse_genesis_marker_t,
|
||||
* capsule_zuse_boot_load_root_pubkey()) independently of whether
|
||||
* Zuse's own thumbdrive is attached this boot. Deliberately
|
||||
* separate from zuse_cert_installed/zuse_cert_seed above -- that
|
||||
* pair is a one-way fuse gating MINT (needs her live private
|
||||
* seed); this pair gates WIREBIND cert *verification* only (needs
|
||||
* nothing but her already-public key), so a WIREBIND identity can
|
||||
* be verified and attach without requiring Zuse's own live session
|
||||
* this boot. Bob, 2026-09-09: "zuse is zuse, [each identity] is
|
||||
* [itself]" -- one identity's attach must not depend on another's
|
||||
* live presence. */
|
||||
uint8_t zuse_root_pubkey[32]; /**< Ed25519 public key, loaded from the genesis-marker fence.
|
||||
* Same value zuse_cert_pubkey ends up holding once Zuse herself
|
||||
* attaches -- this is just available earlier and unconditionally. */
|
||||
/** @} */
|
||||
|
||||
/** @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) */
|
||||
|
||||
VocabularyState vocab; /* Per-VM VOCABULARY/DEFINITIONS/CONTEXT/CURRENT state */
|
||||
ControlFlowState cf; /* Per-VM compile-time control-flow bookkeeping */
|
||||
|
||||
/* 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 */
|
||||
uint64_t blk_vm_epoch; /* blk_subsys_epoch() as of last cache validation --
|
||||
* see block_words.c's blk_vm_find() */
|
||||
|
||||
/** @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 Native execution stack (FABRIC-3.md §XXVIII, Stage 1, 2026-09-13)
|
||||
* @{
|
||||
*/
|
||||
uint64_t native_stack_paddr; /**< Physical base (for teardown); 0 = not allocated */
|
||||
uint64_t native_stack_guard_vaddr; /**< Virtual base of the whole guarded region */
|
||||
uint64_t native_stack_top; /**< Top of the allocated region (fixed, for the
|
||||
* initial frame construction); 0 = not allocated. */
|
||||
uint64_t native_stack_saved_sp; /**< Stage 2 (FABRIC-3.md §XXVIII, 2026-09-13): the
|
||||
* parked SP of a live, switched-out register
|
||||
* context on this VM's own native stack. 0 = never
|
||||
* entered (no context parked yet); non-zero only
|
||||
* while VMRegistryEntry.state ==
|
||||
* VM_STATE_SWITCHED_OUT for this VM. */
|
||||
struct VM *native_switch_back_to; /**< Stage 3 correction (FABRIC-3.md §XXVIII,
|
||||
* 2026-09-13): who most recently switched INTO
|
||||
* this VM. Updated by sk_vm_context_switch() on
|
||||
* every switch-in (first-ever or resumed), so the
|
||||
* placeholder trampoline (sk_vm_switch_entry())
|
||||
* always yields back to whoever actually switched
|
||||
* in, not a stale value captured once at this
|
||||
* VM's very first entry -- a single global there
|
||||
* let a later switch-in from a different VM get
|
||||
* silently bounced to the original caller instead
|
||||
* of itself. */
|
||||
/** @} */
|
||||
|
||||
/** @name Stadium Identity (item 4.2, FABRIC-0.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(). */
|
||||
/** @} */
|
||||
|
||||
/** @name Owner identity + ACL capabilities (FABRIC-2.md §F.2/§F.16)
|
||||
* @{
|
||||
*/
|
||||
VMIdentity identity; /**< This VM's owning identity. installed=0 (the
|
||||
* zero-init default) means no lock -- every
|
||||
* VM today, until D.5's per-VM-identity work
|
||||
* and CERTVERIFY/MINT actually populate it. */
|
||||
/** @} */
|
||||
#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);
|
||||
|
||||
/* Validate that `candidate` is a live, currently-registered dictionary entry
|
||||
* for this VM (walks vm->latest under dict_lock; never dereferences an
|
||||
* unverified pointer). Use before executing/inspecting through any xt/addr
|
||||
* 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);
|
||||
|
||||
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);
|
||||
|
||||
/* Zuse cert one-time install (blows the fuse). seed and pubkey are each 32
|
||||
* bytes, copied into the VM struct. Returns 0 on success, -1 if already
|
||||
* installed -- a second call is a caller bug, not a runtime error to
|
||||
* recover from silently. C-only: no FORTH word wraps this. */
|
||||
int vm_zuse_cert_install(VM* vm, const uint8_t seed[32], const uint8_t pubkey[32]);
|
||||
|
||||
/* 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 */
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file vm_api.h
|
||||
* @brief StarForth Virtual Machine Public API
|
||||
*
|
||||
* This header provides the public interface for interacting with the StarForth
|
||||
* Virtual Machine, particularly focusing on input handling and memory management.
|
||||
*/
|
||||
|
||||
#ifndef VM_API_H
|
||||
#define VM_API_H
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup vm_input VM Input Operations
|
||||
* @{
|
||||
* Public API for managing VM input operations including Terminal Input Buffer (TIB),
|
||||
* input pointer (>IN), and input span (SPAN) handling.
|
||||
*/
|
||||
/*
|
||||
* @brief Ensures input buffer initialization
|
||||
* @param vm Pointer to VM instance
|
||||
* @return 0 on success, non-zero on failure
|
||||
*
|
||||
* Allocates and initializes input buffer if not already done.
|
||||
*/
|
||||
int vm_input_ensure(VM * vm);
|
||||
|
||||
/*
|
||||
* @brief Retrieves Terminal Input Buffer address
|
||||
* @param vm Pointer to VM instance
|
||||
* @return Pointer to TIB buffer
|
||||
*/
|
||||
unsigned char *vm_input_tib(VM * vm);
|
||||
|
||||
/*
|
||||
* @brief Retrieves input pointer address
|
||||
* @param vm Pointer to VM instance
|
||||
* @return Pointer to >IN cell
|
||||
*/
|
||||
cell_t *vm_input_in(VM * vm);
|
||||
|
||||
/*
|
||||
* @brief Retrieves span cell address
|
||||
* @param vm Pointer to VM instance
|
||||
* @return Pointer to SPAN cell
|
||||
*/
|
||||
cell_t *vm_input_span(VM * vm);
|
||||
|
||||
/*
|
||||
* @brief Loads a line into TIB
|
||||
* @param vm Pointer to VM instance
|
||||
* @param src Source string to copy
|
||||
* @param n Number of bytes to copy
|
||||
*
|
||||
* Copies data into TIB, updates SPAN, and resets >IN.
|
||||
*/
|
||||
void vm_input_load_line(VM *vm, const char *src, size_t n);
|
||||
|
||||
/*
|
||||
* @brief Gets current input source
|
||||
* @param vm Pointer to VM instance
|
||||
* @param out_addr Output parameter for source address
|
||||
* @param out_len Output parameter for source length
|
||||
*
|
||||
* Returns SOURCE pair (address, length) for current input.
|
||||
*/
|
||||
void vm_input_source(VM * vm, cell_t * out_addr, cell_t * out_len);
|
||||
|
||||
/*
|
||||
* @brief Converts host pointer to Forth address
|
||||
* @param vm Pointer to VM instance
|
||||
* @param p Host memory pointer
|
||||
* @return Forth memory address (offset into vm->memory)
|
||||
*/
|
||||
cell_t vm_addr_from_ptr(VM *vm, void *p);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VM_API_H */
|
||||
@@ -0,0 +1,606 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
*** StarForth ***
|
||||
vm_asm_opt.h - x86_64 Inline Assembly Optimizations
|
||||
Compatible with: Linux, bare metal x86_64
|
||||
|
||||
HISTORICAL: the "L4Re Compatibility" notes throughout this file describe
|
||||
a target that was supported through mid-2026. L4Re support has since
|
||||
been removed as an active target; these notes are retained as historical
|
||||
design rationale (pure computational/unprivileged code has no reason to
|
||||
become L4Re-incompatible, so the analysis still holds, it's just no
|
||||
longer relevant to any build).
|
||||
|
||||
This work is released under license terms in LICENSE file.
|
||||
No warranty. Use at your own risk.
|
||||
*/
|
||||
|
||||
#ifndef VM_ASM_OPT_H
|
||||
#define VM_ASM_OPT_H
|
||||
|
||||
#include "vm.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* Configuration: Define USE_ASM_OPT=1 to enable assembly optimizations
|
||||
* Define USE_ASM_OPT=0 or leave undefined to use standard C implementations
|
||||
*/
|
||||
#ifndef USE_ASM_OPT
|
||||
#define USE_ASM_OPT 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup stack_ops Stack Operations
|
||||
* @{
|
||||
*
|
||||
* @brief Optimized stack manipulation operations
|
||||
* @details Critical optimizations for vm_push, vm_pop, vm_rpush, vm_rpop
|
||||
*
|
||||
* Impact: EXTREME - These are called on nearly every Forth word execution
|
||||
*
|
||||
* Benefits:
|
||||
* - Eliminates function call overhead (inline)
|
||||
* - Reduces branch mispredictions with conditional moves
|
||||
* - Keeps frequently-used values in registers
|
||||
* - Uses lea for pointer arithmetic (no flags affected)
|
||||
*
|
||||
* L4Re Compatibility: YES - Pure computational code, no syscalls
|
||||
*/
|
||||
|
||||
#if USE_ASM_OPT
|
||||
|
||||
/* vm_push optimized - Data stack push */
|
||||
/**
|
||||
* @brief Optimized data stack push operation
|
||||
* @param vm Pointer to VM context
|
||||
* @param value Value to push onto stack
|
||||
* @return void
|
||||
* @note Sets vm->error on stack overflow
|
||||
*/
|
||||
static inline void vm_push_asm(VM *vm, cell_t value) {
|
||||
int error = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
/* Load current dsp into register with sign extension */
|
||||
"movslq %[dsp], %%rax\n\t"
|
||||
|
||||
/* Check for overflow: dsp >= STACK_SIZE-1 (1023) */
|
||||
"cmpq $1022, %%rax\n\t"
|
||||
"jg 1f\n\t" /* Jump if overflow */
|
||||
|
||||
/* No overflow: increment dsp */
|
||||
"addq $1, %%rax\n\t" /* rax = dsp + 1 */
|
||||
"movl %%eax, %[dsp]\n\t" /* Save new dsp (32-bit) */
|
||||
|
||||
/* Store value: vm->data_stack[dsp] = value (dsp already incremented) */
|
||||
"movq %[val], (%[stack], %%rax, 8)\n\t"
|
||||
|
||||
/* Success: set error = 0 */
|
||||
"xorl %[err], %[err]\n\t"
|
||||
"jmp 2f\n\t"
|
||||
|
||||
/* Overflow path */
|
||||
"1:\n\t"
|
||||
"movl $1, %[err]\n\t"
|
||||
|
||||
"2:\n\t"
|
||||
: [dsp]"+m"(vm->dsp),
|
||||
[err]"=&r"(error)
|
||||
: [val]"r"(value),
|
||||
[stack]"r"(vm->data_stack)
|
||||
: "rax", "cc", "memory"
|
||||
);
|
||||
|
||||
if (__builtin_expect(error, 0)) {
|
||||
vm->error = 1;
|
||||
/* Note: Logging omitted for performance - enable in debug builds only */
|
||||
}
|
||||
}
|
||||
|
||||
/* vm_pop optimized - Data stack pop */
|
||||
/**
|
||||
* @brief Optimized data stack pop operation
|
||||
* @param vm Pointer to VM context
|
||||
* @return Top value from stack, 0 on underflow
|
||||
* @note Sets vm->error on stack underflow
|
||||
*/
|
||||
static inline cell_t vm_pop_asm(VM *vm) {
|
||||
cell_t value = 0;
|
||||
int error = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
/* Load current dsp with sign extension */
|
||||
"movslq %[dsp], %%rax\n\t"
|
||||
|
||||
/* Check for underflow: dsp < 0 */
|
||||
"testq %%rax, %%rax\n\t"
|
||||
"js 1f\n\t" /* Jump if negative (underflow) */
|
||||
|
||||
/* No underflow: load value */
|
||||
"movq (%[stack], %%rax, 8), %[val]\n\t"
|
||||
|
||||
/* Decrement dsp */
|
||||
"subq $1, %%rax\n\t"
|
||||
"movl %%eax, %[dsp]\n\t"
|
||||
|
||||
/* Success: set error = 0 */
|
||||
"xorl %[err], %[err]\n\t"
|
||||
"jmp 2f\n\t"
|
||||
|
||||
/* Underflow path */
|
||||
"1:\n\t"
|
||||
"movl $1, %[err]\n\t"
|
||||
"xorq %%rax, %%rax\n\t" /* Return 0 on error */
|
||||
"movq %%rax, %[val]\n\t"
|
||||
|
||||
"2:\n\t"
|
||||
: [dsp]"+m"(vm->dsp),
|
||||
[val]"=&r"(value),
|
||||
[err]"=&r"(error)
|
||||
: [stack]"r"(vm->data_stack)
|
||||
: "rax", "cc", "memory"
|
||||
);
|
||||
|
||||
if (__builtin_expect(error, 0)) {
|
||||
vm->error = 1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* vm_rpush optimized - Return stack push */
|
||||
/**
|
||||
* @brief Optimized return stack push operation
|
||||
* @param vm Pointer to VM context
|
||||
* @param value Value to push onto return stack
|
||||
* @return void
|
||||
* @note Sets vm->error on stack overflow
|
||||
*/
|
||||
static inline void vm_rpush_asm(VM *vm, cell_t value) {
|
||||
int error = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"movslq %[rsp], %%rax\n\t"
|
||||
"cmpq $1022, %%rax\n\t"
|
||||
"jg 1f\n\t"
|
||||
"addq $1, %%rax\n\t"
|
||||
"movl %%eax, %[rsp]\n\t"
|
||||
"movq %[val], (%[stack], %%rax, 8)\n\t"
|
||||
"xorl %[err], %[err]\n\t"
|
||||
"jmp 2f\n\t"
|
||||
"1:\n\t"
|
||||
"movl $1, %[err]\n\t"
|
||||
"2:\n\t"
|
||||
: [rsp]"+m"(vm->rsp),
|
||||
[err]"=&r"(error)
|
||||
: [val]"r"(value),
|
||||
[stack]"r"(vm->return_stack)
|
||||
: "rax", "cc", "memory"
|
||||
);
|
||||
|
||||
if (__builtin_expect(error, 0)) {
|
||||
vm->error = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* vm_rpop optimized - Return stack pop */
|
||||
/**
|
||||
* @brief Optimized return stack pop operation
|
||||
* @param vm Pointer to VM context
|
||||
* @return Top value from return stack, 0 on underflow
|
||||
* @note Sets vm->error on stack underflow
|
||||
*/
|
||||
static inline cell_t vm_rpop_asm(VM *vm) {
|
||||
cell_t value = 0;
|
||||
int error = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"movslq %[rsp], %%rax\n\t"
|
||||
"testq %%rax, %%rax\n\t"
|
||||
"js 1f\n\t"
|
||||
"movq (%[stack], %%rax, 8), %[val]\n\t"
|
||||
"subq $1, %%rax\n\t"
|
||||
"movl %%eax, %[rsp]\n\t"
|
||||
"xorl %[err], %[err]\n\t"
|
||||
"jmp 2f\n\t"
|
||||
"1:\n\t"
|
||||
"movl $1, %[err]\n\t"
|
||||
"xorq %%rax, %%rax\n\t"
|
||||
"movq %%rax, %[val]\n\t"
|
||||
"2:\n\t"
|
||||
: [rsp]"+m"(vm->rsp),
|
||||
[val]"=&r"(value),
|
||||
[err]"=&r"(error)
|
||||
: [stack]"r"(vm->return_stack)
|
||||
: "rax", "cc", "memory"
|
||||
);
|
||||
|
||||
if (__builtin_expect(error, 0)) {
|
||||
vm->error = 1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 2: Fast Arithmetic with Overflow Detection
|
||||
* ============================================================================
|
||||
*
|
||||
* IMPACT: MEDIUM-HIGH
|
||||
*
|
||||
* Benefits:
|
||||
* - Uses CPU overflow flag instead of manual checks
|
||||
* - Eliminates branches in common (no overflow) case
|
||||
* - Single instruction for addition/subtraction with flag check
|
||||
*
|
||||
* L4Re Compatibility: YES
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/* Fast add with overflow detection */
|
||||
/**
|
||||
* @brief Performs addition with hardware overflow detection
|
||||
* @param a First operand
|
||||
* @param b Second operand
|
||||
* @param result Pointer to store result
|
||||
* @return 1 if overflow occurred, 0 otherwise
|
||||
*/
|
||||
static inline int vm_add_check_overflow(cell_t a, cell_t b, cell_t *result) {
|
||||
cell_t res;
|
||||
int overflow;
|
||||
|
||||
__asm__(
|
||||
"movq %[a], %%rax\n\t"
|
||||
"addq %[b], %%rax\n\t"
|
||||
"movq %%rax, %[res]\n\t"
|
||||
"seto %%al\n\t" /* Set AL to 1 if overflow occurred */
|
||||
"movzbl %%al, %[ovf]\n\t"
|
||||
: [res]"=r"(res),
|
||||
[ovf]"=r"(overflow)
|
||||
: [a]"r"(a),
|
||||
[b]"r"(b)
|
||||
: "rax", "cc"
|
||||
);
|
||||
|
||||
*result = res;
|
||||
return overflow;
|
||||
}
|
||||
|
||||
/* Fast subtract with overflow detection */
|
||||
/**
|
||||
* @brief Performs subtraction with hardware overflow detection
|
||||
* @param a First operand (minuend)
|
||||
* @param b Second operand (subtrahend)
|
||||
* @param result Pointer to store result
|
||||
* @return 1 if overflow occurred, 0 otherwise
|
||||
*/
|
||||
static inline int vm_sub_check_overflow(cell_t a, cell_t b, cell_t *result) {
|
||||
cell_t res;
|
||||
int overflow;
|
||||
|
||||
__asm__(
|
||||
"movq %[a], %%rax\n\t"
|
||||
"subq %[b], %%rax\n\t"
|
||||
"movq %%rax, %[res]\n\t"
|
||||
"seto %%al\n\t" /* Set AL to 1 if overflow occurred */
|
||||
"movzbl %%al, %[ovf]\n\t"
|
||||
: [res]"=r"(res),
|
||||
[ovf]"=r"(overflow)
|
||||
: [a]"r"(a),
|
||||
[b]"r"(b)
|
||||
: "rax", "cc"
|
||||
);
|
||||
|
||||
*result = res;
|
||||
return overflow;
|
||||
}
|
||||
|
||||
/* Fast multiply with double-width result for */ /* MOD operations */
|
||||
/**
|
||||
* @brief Performs double-width multiplication
|
||||
* @param a First operand
|
||||
* @param b Second operand
|
||||
* @param hi Pointer to store high 64 bits
|
||||
* @param lo Pointer to store low 64 bits
|
||||
*/
|
||||
static inline void vm_mul_double(cell_t a, cell_t b, cell_t *hi, cell_t *lo) {
|
||||
__asm__(
|
||||
"movq %[a], %%rax\n\t"
|
||||
"imulq %[b]\n\t" /* rdx:rax = rax * b (signed) */
|
||||
"movq %%rax, %[lo]\n\t"
|
||||
"movq %%rdx, %[hi]\n\t"
|
||||
: [lo]"=r"(*lo),
|
||||
[hi]"=r"(*hi)
|
||||
: [a]"r"(a),
|
||||
[b]"r"(b)
|
||||
: "rax", "rdx", "cc"
|
||||
);
|
||||
}
|
||||
|
||||
/* Fast divide with remainder (for /MOD) */
|
||||
/**
|
||||
* @brief Performs division with remainder
|
||||
* @param dividend Dividend value
|
||||
* @param divisor Divisor value
|
||||
* @param quotient Pointer to store quotient
|
||||
* @param remainder Pointer to store remainder
|
||||
*/
|
||||
static inline void vm_divmod(cell_t dividend, cell_t divisor,
|
||||
cell_t *quotient, cell_t *remainder) {
|
||||
cell_t quot, rem;
|
||||
|
||||
__asm__(
|
||||
"movq %[dividend], %%rax\n\t"
|
||||
"cqo\n\t" /* Sign-extend rax into rdx:rax */
|
||||
"idivq %[divisor]\n\t" /* rax = quotient, rdx = remainder */
|
||||
"movq %%rax, %[quot]\n\t"
|
||||
"movq %%rdx, %[rem]\n\t"
|
||||
: [quot]"=r"(quot),
|
||||
[rem]"=r"(rem)
|
||||
: [dividend]"r"(dividend),
|
||||
[divisor]"r"(divisor)
|
||||
: "rax", "rdx", "cc"
|
||||
);
|
||||
|
||||
*quotient = quot;
|
||||
*remainder = rem;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 3: Fast String Operations for Dictionary Lookup
|
||||
* ============================================================================
|
||||
*
|
||||
* IMPACT: HIGH
|
||||
*
|
||||
* Benefits:
|
||||
* - Uses hardware string comparison (rep cmpsb)
|
||||
* - Processes bytes in parallel
|
||||
* - Much faster than byte-by-byte C loop
|
||||
*
|
||||
* L4Re Compatibility: YES
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/* Fast string comparison - returns 0 if equal, non-zero otherwise */
|
||||
/**
|
||||
* @brief Hardware-accelerated string comparison
|
||||
* @param s1 First string
|
||||
* @param s2 Second string
|
||||
* @param len Length to compare
|
||||
* @return 0 if equal, non-zero otherwise
|
||||
*/
|
||||
static inline int vm_strcmp_asm(const char *s1, const char *s2, size_t len) {
|
||||
int result;
|
||||
|
||||
__asm__(
|
||||
"movq %[s1], %%rsi\n\t"
|
||||
"movq %[s2], %%rdi\n\t"
|
||||
"movq %[len], %%rcx\n\t"
|
||||
"xorl %%eax, %%eax\n\t"
|
||||
"repe cmpsb\n\t" /* Compare bytes while equal */
|
||||
"je 1f\n\t" /* If all equal, result = 0 */
|
||||
"movl $1, %%eax\n\t" /* Not equal, result = 1 */
|
||||
"1:\n\t"
|
||||
: "=a"(result)
|
||||
: [s1]"r"(s1),
|
||||
[s2]"r"(s2),
|
||||
[len]"r"(len)
|
||||
: "rsi", "rdi", "rcx", "cc", "memory"
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Fast memory copy for block operations */
|
||||
/**
|
||||
* @brief Hardware-accelerated memory copy
|
||||
* @param dest Destination buffer
|
||||
* @param src Source buffer
|
||||
* @param len Number of bytes to copy
|
||||
*/
|
||||
static inline void vm_memcpy_asm(void *dest, const void *src, size_t len) {
|
||||
__asm__ __volatile__(
|
||||
"movq %[dst], %%rdi\n\t"
|
||||
"movq %[src], %%rsi\n\t"
|
||||
"movq %[len], %%rcx\n\t"
|
||||
"rep movsb\n\t" /* Copy bytes */
|
||||
:
|
||||
: [dst]"r"(dest),
|
||||
[src]"r"(src),
|
||||
[len]"r"(len)
|
||||
: "rdi", "rsi", "rcx", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/* Fast memory zero for alignment padding */
|
||||
/**
|
||||
* @brief Hardware-accelerated memory zero
|
||||
* @param dest Buffer to zero
|
||||
* @param len Number of bytes to zero
|
||||
*/
|
||||
static inline void vm_memzero_asm(void *dest, size_t len) {
|
||||
__asm__ __volatile__(
|
||||
"movq %[dst], %%rdi\n\t"
|
||||
"xorl %%eax, %%eax\n\t"
|
||||
"movq %[len], %%rcx\n\t"
|
||||
"rep stosb\n\t" /* Store zeros */
|
||||
:
|
||||
: [dst]"r"(dest),
|
||||
[len]"r"(len)
|
||||
: "rdi", "rax", "rcx", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 4: Branchless Min/Max
|
||||
* ============================================================================
|
||||
*
|
||||
* IMPACT: MEDIUM
|
||||
*
|
||||
* Benefits:
|
||||
* - No branches = no mispredictions
|
||||
* - Single cmov instruction
|
||||
*
|
||||
* L4Re Compatibility: YES
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Branchless minimum calculation
|
||||
* @param a First value
|
||||
* @param b Second value
|
||||
* @return Smaller of a and b
|
||||
*/
|
||||
static inline cell_t vm_min_asm(cell_t a, cell_t b) {
|
||||
cell_t result;
|
||||
__asm__(
|
||||
"movq %[a], %%rax\n\t"
|
||||
"movq %[b], %%rcx\n\t"
|
||||
"cmpq %%rcx, %%rax\n\t"
|
||||
"cmovg %%rcx, %%rax\n\t" /* Move if a > b */
|
||||
"movq %%rax, %[res]\n\t"
|
||||
: [res]"=r"(result)
|
||||
: [a]"r"(a),
|
||||
[b]"r"(b)
|
||||
: "rax", "rcx", "cc"
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Branchless maximum calculation
|
||||
* @param a First value
|
||||
* @param b Second value
|
||||
* @return Larger of a and b
|
||||
*/
|
||||
static inline cell_t vm_max_asm(cell_t a, cell_t b) {
|
||||
cell_t result;
|
||||
__asm__(
|
||||
"movq %[a], %%rax\n\t"
|
||||
"movq %[b], %%rcx\n\t"
|
||||
"cmpq %%rcx, %%rax\n\t"
|
||||
"cmovl %%rcx, %%rax\n\t" /* Move if a < b */
|
||||
"movq %%rax, %[res]\n\t"
|
||||
: [res]"=r"(result)
|
||||
: [a]"r"(a),
|
||||
[b]"r"(b)
|
||||
: "rax", "rcx", "cc"
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 5: CPU Feature Detection
|
||||
* ============================================================================
|
||||
*
|
||||
* For future SIMD optimizations
|
||||
* L4Re Compatibility: YES (cpuid is unprivileged)
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Wrapper for CPUID instruction
|
||||
* @param leaf CPUID leaf number
|
||||
* @param eax Pointer to store EAX result
|
||||
* @param ebx Pointer to store EBX result
|
||||
* @param ecx Pointer to store ECX result
|
||||
* @param edx Pointer to store EDX result
|
||||
*/
|
||||
static inline void vm_cpuid(uint32_t leaf, uint32_t *eax, uint32_t *ebx,
|
||||
uint32_t *ecx, uint32_t *edx) {
|
||||
__asm__(
|
||||
"cpuid"
|
||||
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
|
||||
: "a"(leaf)
|
||||
);
|
||||
}
|
||||
|
||||
/* Check for SSE4.2 support (for fast string operations) */
|
||||
/**
|
||||
* @brief Checks for SSE4.2 support
|
||||
* @return 1 if SSE4.2 is supported, 0 otherwise
|
||||
*/
|
||||
static inline int vm_has_sse42(void) {
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
vm_cpuid(1, &eax, &ebx, &ecx, &edx);
|
||||
return (ecx >> 20) & 1; /* Bit 20 = SSE4.2 */
|
||||
}
|
||||
|
||||
#else /* !USE_ASM_OPT */
|
||||
|
||||
/* Fallback to standard implementations when assembly is disabled */
|
||||
#define vm_push_asm(vm, val) vm_push(vm, val)
|
||||
#define vm_pop_asm(vm) vm_pop(vm)
|
||||
#define vm_rpush_asm(vm, val) vm_rpush(vm, val)
|
||||
#define vm_rpop_asm(vm) vm_rpop(vm)
|
||||
|
||||
#endif /* USE_ASM_OPT */
|
||||
|
||||
/* ============================================================================
|
||||
* L4Re SPECIFIC NOTES
|
||||
* ============================================================================
|
||||
*
|
||||
* All optimizations above are L4Re-compatible because they:
|
||||
* 1. Don't use privileged instructions
|
||||
* 2. Don't make syscalls
|
||||
* 3. Don't access hardware directly
|
||||
* 4. Are pure computational optimizations
|
||||
*
|
||||
* For L4Re integration:
|
||||
* - Compile with -march=native or -march=x86-64-v3 for modern CPUs
|
||||
* - Use L4Re's memory allocators for VM memory
|
||||
* - Consider using L4Re IPC for inter-VM communication
|
||||
* - Profile using L4Re's performance counters
|
||||
*
|
||||
* Build flags for optimal performance:
|
||||
* -DUSE_ASM_OPT=1 -O3 -march=native -flto
|
||||
*
|
||||
* For StarshipOS integration:
|
||||
* - These functions can be used in both userspace and kernel contexts
|
||||
* - No floating point operations (safe for kernel mode)
|
||||
* - No stack-intensive operations (safe for small kernel stacks)
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
#endif /* VM_ASM_OPT_H */
|
||||
@@ -0,0 +1,783 @@
|
||||
/*
|
||||
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_ASM_OPT_ARM64_H
|
||||
#define VM_ASM_OPT_ARM64_H
|
||||
|
||||
#include "vm.h"
|
||||
#include <stdint.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/auxv.h> /* For getauxval, AT_HWCAP */
|
||||
#include <asm/hwcap.h> /* For HWCAP_ASIMD */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Configuration: Define USE_ASM_OPT=1 to enable assembly optimizations
|
||||
*/
|
||||
#ifndef USE_ASM_OPT
|
||||
#define USE_ASM_OPT 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ARM64 Architecture Notes:
|
||||
* =========================
|
||||
*
|
||||
* Registers (64-bit):
|
||||
* - x0-x7: Argument/result registers
|
||||
* - x8: Indirect result location
|
||||
* - x9-x15: Temporary registers (caller-saved)
|
||||
* - x16-x17: Intra-procedure-call temporary (IP0, IP1)
|
||||
* - x18: Platform register (reserved on some platforms)
|
||||
* - x19-x28: Callee-saved registers (must preserve)
|
||||
* - x29: Frame pointer (FP)
|
||||
* - x30: Link register (LR)
|
||||
* - sp: Stack pointer
|
||||
*
|
||||
* 32-bit variants: w0-w30 (lower 32 bits of x0-x30)
|
||||
*
|
||||
* Advantages over x86_64:
|
||||
* - More registers (31 general-purpose vs 16)
|
||||
* - Conditional execution on most instructions
|
||||
* - Load/store with auto-increment
|
||||
* - Better power efficiency
|
||||
*
|
||||
* Raspberry Pi 4 specifics:
|
||||
* - Cortex-A72 CPU (4 cores @ 1.5GHz)
|
||||
* - ARMv8-A architecture
|
||||
* - 32KB L1I + 32KB L1D per core
|
||||
* - 1MB shared L2 cache
|
||||
* - NEON SIMD support
|
||||
*/
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 1: Stack Operations (vm_push, vm_pop, vm_rpush, vm_rpop)
|
||||
* ============================================================================
|
||||
*
|
||||
* ARM64 advantages:
|
||||
* - Conditional execution eliminates branches
|
||||
* - Load/store with immediate offset
|
||||
* - Post-increment addressing modes
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
#if USE_ASM_OPT
|
||||
|
||||
/* vm_push optimized - Data stack push */
|
||||
/**
|
||||
* @brief Optimized assembly implementation of data stack push operation
|
||||
* @param vm Pointer to VM instance
|
||||
* @param value Value to push onto data stack
|
||||
* @note Sets vm->error on stack overflow
|
||||
*/
|
||||
static inline void vm_push_asm(VM *vm, cell_t value) {
|
||||
int error = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
/* Load current dsp */
|
||||
"ldr w0, %[dsp]\n\t"
|
||||
|
||||
/* Check for overflow: dsp >= 1022 */
|
||||
"cmp w0, #1022\n\t"
|
||||
"b.ge 1f\n\t" /* Branch if >= */
|
||||
|
||||
/* No overflow: increment dsp */
|
||||
"add w1, w0, #1\n\t" /* w1 = dsp + 1 */
|
||||
"str w1, %[dsp]\n\t"
|
||||
|
||||
/* Store value: vm->data_stack[dsp+1] = value */
|
||||
/* Calculate address: base + (dsp+1)*8 */
|
||||
"str %[val], [%[stack], w1, sxtw #3]\n\t" /* base + index*8 */
|
||||
|
||||
/* Set error=0 in success path */
|
||||
"mov w3, #0\n\t"
|
||||
"str w3, %[err]\n\t"
|
||||
"b 2f\n\t"
|
||||
|
||||
/* Overflow path */
|
||||
"1:\n\t"
|
||||
"mov w3, #1\n\t"
|
||||
"str w3, %[err]\n\t"
|
||||
|
||||
"2:\n\t"
|
||||
: [dsp]"+m"(vm->dsp),
|
||||
[err]"=m"(error)
|
||||
: [val]"r"(value),
|
||||
[stack]"r"(vm->data_stack)
|
||||
: "x0", "x1", "x3", "cc", "memory"
|
||||
);
|
||||
|
||||
if (__builtin_expect(error, 0)) {
|
||||
vm->error = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* vm_pop optimized - Data stack pop */
|
||||
/**
|
||||
* @brief Optimized assembly implementation of data stack pop operation
|
||||
* @param vm Pointer to VM instance
|
||||
* @return Popped value, or 0 on underflow
|
||||
* @note Sets vm->error on stack underflow
|
||||
*/
|
||||
static inline cell_t vm_pop_asm(VM *vm) {
|
||||
cell_t value = 0;
|
||||
int error = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
/* Load current dsp */
|
||||
"ldr w0, %[dsp]\n\t"
|
||||
|
||||
/* Check for underflow: dsp < 0 */
|
||||
"cmp w0, #0\n\t"
|
||||
"b.lt 1f\n\t" /* Branch if less than */
|
||||
|
||||
/* No underflow: load value */
|
||||
"ldr %[val], [%[stack], w0, sxtw #3]\n\t" /* base + index*8 */
|
||||
|
||||
/* Decrement dsp */
|
||||
"sub w1, w0, #1\n\t"
|
||||
"str w1, %[dsp]\n\t"
|
||||
|
||||
/* Set error=0 in success path */
|
||||
"mov w3, #0\n\t"
|
||||
"str w3, %[err]\n\t"
|
||||
"b 2f\n\t"
|
||||
|
||||
/* Underflow path */
|
||||
"1:\n\t"
|
||||
"mov w3, #1\n\t"
|
||||
"str w3, %[err]\n\t"
|
||||
"mov %[val], #0\n\t" /* Return 0 on error */
|
||||
|
||||
"2:\n\t"
|
||||
: [dsp]"+m"(vm->dsp),
|
||||
[val]"=r"(value),
|
||||
[err]"=m"(error)
|
||||
: [stack]"r"(vm->data_stack)
|
||||
: "x0", "x1", "x3", "cc", "memory"
|
||||
);
|
||||
|
||||
if (__builtin_expect(error, 0)) {
|
||||
vm->error = 1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* vm_rpush optimized - Return stack push */
|
||||
/**
|
||||
* @brief Optimized assembly implementation of return stack push operation
|
||||
* @param vm Pointer to VM instance
|
||||
* @param value Value to push onto return stack
|
||||
* @note Sets vm->error on stack overflow
|
||||
*/
|
||||
static inline void vm_rpush_asm(VM *vm, cell_t value) {
|
||||
int error = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"ldr w0, %[rsp]\n\t"
|
||||
"cmp w0, #1022\n\t"
|
||||
"b.ge 1f\n\t"
|
||||
"add w1, w0, #1\n\t"
|
||||
"str w1, %[rsp]\n\t"
|
||||
"str %[val], [%[stack], w1, sxtw #3]\n\t"
|
||||
"mov w3, #0\n\t"
|
||||
"str w3, %[err]\n\t"
|
||||
"b 2f\n\t"
|
||||
"1:\n\t"
|
||||
"mov w3, #1\n\t"
|
||||
"str w3, %[err]\n\t"
|
||||
"2:\n\t"
|
||||
: [rsp]"+m"(vm->rsp),
|
||||
[err]"=m"(error)
|
||||
: [val]"r"(value),
|
||||
[stack]"r"(vm->return_stack)
|
||||
: "x0", "x1", "x3", "cc", "memory"
|
||||
);
|
||||
|
||||
if (__builtin_expect(error, 0)) {
|
||||
vm->error = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* vm_rpop optimized - Return stack pop */
|
||||
/**
|
||||
* @brief Optimized assembly implementation of return stack pop operation
|
||||
* @param vm Pointer to VM instance
|
||||
* @return Popped value, or 0 on underflow
|
||||
* @note Sets vm->error on stack underflow
|
||||
*/
|
||||
static inline cell_t vm_rpop_asm(VM *vm) {
|
||||
cell_t value = 0;
|
||||
int error = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"ldr w0, %[rsp]\n\t"
|
||||
"cmp w0, #0\n\t"
|
||||
"b.lt 1f\n\t"
|
||||
"ldr %[val], [%[stack], w0, sxtw #3]\n\t"
|
||||
"sub w1, w0, #1\n\t"
|
||||
"str w1, %[rsp]\n\t"
|
||||
"mov w3, #0\n\t"
|
||||
"str w3, %[err]\n\t"
|
||||
"b 2f\n\t"
|
||||
"1:\n\t"
|
||||
"mov w3, #1\n\t"
|
||||
"str w3, %[err]\n\t"
|
||||
"mov %[val], #0\n\t"
|
||||
"2:\n\t"
|
||||
: [rsp]"+m"(vm->rsp),
|
||||
[val]"=r"(value),
|
||||
[err]"=m"(error)
|
||||
: [stack]"r"(vm->return_stack)
|
||||
: "x0", "x1", "x3", "cc", "memory"
|
||||
);
|
||||
|
||||
if (__builtin_expect(error, 0)) {
|
||||
vm->error = 1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 2: Fast Arithmetic with Overflow Detection
|
||||
* ============================================================================
|
||||
*
|
||||
* ARM64 has excellent support for overflow detection via condition flags
|
||||
* and conditional select instructions
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/* Fast add with overflow detection using ADDS and conditional select */
|
||||
/**
|
||||
* @brief Fast addition with overflow detection using ARM64 ADDS instruction
|
||||
* @param a First operand
|
||||
* @param b Second operand
|
||||
* @param result Pointer to store result
|
||||
* @return 1 if overflow occurred, 0 otherwise
|
||||
*/
|
||||
static inline int vm_add_check_overflow(cell_t a, cell_t b, cell_t *result) {
|
||||
cell_t res;
|
||||
int overflow;
|
||||
|
||||
__asm__(
|
||||
"adds %[res], %[a], %[b]\n\t" /* Add and set flags */
|
||||
"cset %w[ovf], vs\n\t" /* Set overflow flag (V flag) */
|
||||
: [res]"=r"(res),
|
||||
[ovf]"=r"(overflow)
|
||||
: [a]"r"(a),
|
||||
[b]"r"(b)
|
||||
: "cc"
|
||||
);
|
||||
|
||||
*result = res;
|
||||
return overflow;
|
||||
}
|
||||
|
||||
/* Fast subtract with overflow detection */
|
||||
/**
|
||||
* @brief Fast subtraction with overflow detection using ARM64 SUBS instruction
|
||||
* @param a First operand
|
||||
* @param b Second operand
|
||||
* @param result Pointer to store result
|
||||
* @return 1 if overflow occurred, 0 otherwise
|
||||
*/
|
||||
static inline int vm_sub_check_overflow(cell_t a, cell_t b, cell_t *result) {
|
||||
cell_t res;
|
||||
int overflow;
|
||||
|
||||
__asm__(
|
||||
"subs %[res], %[a], %[b]\n\t"
|
||||
"cset %w[ovf], vs\n\t"
|
||||
: [res]"=r"(res),
|
||||
[ovf]"=r"(overflow)
|
||||
: [a]"r"(a),
|
||||
[b]"r"(b)
|
||||
: "cc"
|
||||
);
|
||||
|
||||
*result = res;
|
||||
return overflow;
|
||||
}
|
||||
|
||||
/* Fast multiply with high-word result (for star-slash-MOD) */
|
||||
/**
|
||||
* @brief Fast 128-bit multiplication using ARM64 SMULH instruction
|
||||
* @param a First operand
|
||||
* @param b Second operand
|
||||
* @param hi Pointer to store high 64 bits of result
|
||||
* @param lo Pointer to store low 64 bits of result
|
||||
*/
|
||||
static inline void vm_mul_double(cell_t a, cell_t b, cell_t *hi, cell_t *lo) {
|
||||
__asm__(
|
||||
"mul %[lo], %[a], %[b]\n\t" /* Low 64 bits */
|
||||
"smulh %[hi], %[a], %[b]\n\t" /* High 64 bits (signed) */
|
||||
: [lo]"=r"(*lo),
|
||||
[hi]"=r"(*hi)
|
||||
: [a]"r"(a),
|
||||
[b]"r"(b)
|
||||
);
|
||||
}
|
||||
|
||||
/* Fast divide with remainder (for /MOD) */
|
||||
/**
|
||||
* @brief Fast division with remainder using ARM64 SDIV and MSUB instructions
|
||||
* @param dividend Dividend value
|
||||
* @param divisor Divisor value
|
||||
* @param quotient Pointer to store quotient
|
||||
* @param remainder Pointer to store remainder
|
||||
*/
|
||||
static inline void vm_divmod(cell_t dividend, cell_t divisor,
|
||||
cell_t *quotient, cell_t *remainder) {
|
||||
cell_t quot, rem;
|
||||
|
||||
__asm__(
|
||||
"sdiv %[quot], %[dividend], %[divisor]\n\t" /* Quotient */
|
||||
"msub %[rem], %[quot], %[divisor], %[dividend]\n\t" /* Remainder = dividend - quot*divisor */
|
||||
: [quot]"=r"(quot),
|
||||
[rem]"=r"(rem)
|
||||
: [dividend]"r"(dividend),
|
||||
[divisor]"r"(divisor)
|
||||
);
|
||||
|
||||
*quotient = quot;
|
||||
*remainder = rem;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 3: Fast String Operations for Dictionary Lookup
|
||||
* ============================================================================
|
||||
*
|
||||
* ARM64 doesn't have rep-style instructions, but we can:
|
||||
* - Use NEON for parallel comparison
|
||||
* - Use load-multiple for cache efficiency
|
||||
* - Unroll loops for small strings
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/* Fast string comparison for short strings (<=32 bytes) */
|
||||
/**
|
||||
* @brief Optimized string comparison for short strings (<=32 bytes)
|
||||
* @param s1 First string
|
||||
* @param s2 Second string
|
||||
* @param len Length to compare
|
||||
* @return 0 if equal, 1 if different
|
||||
*/
|
||||
static inline int vm_strcmp_short_asm(const char *s1, const char *s2, size_t len) {
|
||||
int result = 0;
|
||||
|
||||
if (len <= 8) {
|
||||
/* Use single 64-bit load for strings <= 8 bytes */
|
||||
__asm__(
|
||||
"mov x0, #0\n\t" /* result = 0 */
|
||||
"cbz %[len], 2f\n\t" /* if len==0, equal */
|
||||
"ldr x1, [%[s1]]\n\t"
|
||||
"ldr x2, [%[s2]]\n\t"
|
||||
"eor x3, x1, x2\n\t" /* XOR to find differences */
|
||||
|
||||
/* Mask off bits beyond length */
|
||||
"mov x4, #64\n\t"
|
||||
"lsl x5, %[len], #3\n\t" /* len * 8 */
|
||||
"sub x4, x4, x5\n\t"
|
||||
"lsr x1, x1, x4\n\t"
|
||||
"lsr x2, x2, x4\n\t"
|
||||
"eor x3, x1, x2\n\t"
|
||||
|
||||
"cmp x3, #0\n\t"
|
||||
"cset %w[res], ne\n\t" /* result = (x3 != 0) */
|
||||
"2:\n\t"
|
||||
: [res]"=r"(result)
|
||||
: [s1]"r"(s1),
|
||||
[s2]"r"(s2),
|
||||
[len]"r"(len)
|
||||
: "x0", "x1", "x2", "x3", "x4", "x5", "cc", "memory"
|
||||
);
|
||||
} else {
|
||||
/* Fallback to byte-by-byte for longer strings */
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (s1[i] != s2[i]) {
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Fast string comparison using NEON (for longer strings) */
|
||||
#ifdef __ARM_NEON
|
||||
static inline int vm_strcmp_neon(const char *s1, const char *s2, size_t len) {
|
||||
int result = 0;
|
||||
|
||||
/* Process 16 bytes at a time using NEON */
|
||||
while (len >= 16) {
|
||||
__asm__ __volatile__(
|
||||
"ld1 {v0.16b}, [%[s1]], #16\n\t" /* Load 16 bytes from s1, post-increment */
|
||||
"ld1 {v1.16b}, [%[s2]], #16\n\t" /* Load 16 bytes from s2, post-increment */
|
||||
"cmeq v2.16b, v0.16b, v1.16b\n\t" /* Compare equal */
|
||||
"uminv b3, v2.16b\n\t" /* Get minimum (all must be 0xFF for equal) */
|
||||
"fmov %w[res], s3\n\t" /* Move to general register */
|
||||
"cmp %w[res], #0xff\n\t"
|
||||
"b.ne 1f\n\t" /* If not all equal, exit */
|
||||
"1:\n\t"
|
||||
: [res]"=r"(result),
|
||||
[s1]"+r"(s1),
|
||||
[s2]"+r"(s2)
|
||||
:
|
||||
: "v0", "v1", "v2", "v3", "cc", "memory"
|
||||
);
|
||||
|
||||
if (result != 0xFF) {
|
||||
return 1; /* Not equal */
|
||||
}
|
||||
|
||||
len -= 16;
|
||||
}
|
||||
|
||||
/* Handle remaining bytes */
|
||||
while (len > 0) {
|
||||
if (*s1++ != *s2++) return 1;
|
||||
len--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Main string comparison wrapper */
|
||||
static inline int vm_strcmp_asm(const char *s1, const char *s2, size_t len) {
|
||||
|
||||
|
||||
|
||||
#ifdef __ARM_NEON
|
||||
if (len>= 16) {
|
||||
return vm_strcmp_neon(s1, s2, len);
|
||||
}
|
||||
#endif
|
||||
return vm_strcmp_short_asm(s1, s2, len);
|
||||
}
|
||||
|
||||
/* Fast memory copy optimized for ARM64 */
|
||||
/**
|
||||
* @brief Optimized memory copy using ARM64 load/store pair instructions
|
||||
* @param dest Destination buffer
|
||||
* @param src Source buffer
|
||||
* @param len Number of bytes to copy
|
||||
*/
|
||||
static inline void vm_memcpy_asm(void *dest, const void *src, size_t len) {
|
||||
/* ARM64 has efficient load/store pair instructions */
|
||||
if (len >= 16) {
|
||||
__asm__ __volatile__(
|
||||
"1:\n\t"
|
||||
"ldp x0, x1, [%[src]], #16\n\t" /* Load pair, post-increment */
|
||||
"stp x0, x1, [%[dst]], #16\n\t" /* Store pair, post-increment */
|
||||
"subs %[len], %[len], #16\n\t"
|
||||
"b.ge 1b\n\t"
|
||||
: [src]"+r"(src),
|
||||
[dst]"+r"(dest),
|
||||
[len]"+r"(len)
|
||||
:
|
||||
: "x0", "x1", "cc", "memory"
|
||||
);
|
||||
len += 16; /* Adjust for overshoot */
|
||||
}
|
||||
|
||||
/* Copy remaining bytes */
|
||||
char *d = (char *) dest;
|
||||
const char *s = (const char *) src;
|
||||
while (len-- > 0) {
|
||||
*d++ = *s++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fast memory zero using store pair */
|
||||
/**
|
||||
* @brief Fast memory zeroing using ARM64 store pair instructions
|
||||
* @param dest Buffer to zero
|
||||
* @param len Number of bytes to zero
|
||||
*/
|
||||
static inline void vm_memzero_asm(void *dest, size_t len) {
|
||||
if (len >= 16) {
|
||||
__asm__ __volatile__(
|
||||
"1:\n\t"
|
||||
"stp xzr, xzr, [%[dst]], #16\n\t" /* Store zero pair */
|
||||
"subs %[len], %[len], #16\n\t"
|
||||
"b.ge 1b\n\t"
|
||||
: [dst]"+r"(dest),
|
||||
[len]"+r"(len)
|
||||
:
|
||||
: "cc", "memory"
|
||||
);
|
||||
len += 16;
|
||||
}
|
||||
|
||||
/* Zero remaining bytes */
|
||||
char *d = (char *) dest;
|
||||
while (len-- > 0) {
|
||||
*d++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 4: Branchless Min/Max using CSEL
|
||||
* ============================================================================
|
||||
*
|
||||
* ARM64's conditional select is perfect for branchless operations
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
static inline cell_t vm_min_asm(cell_t a, cell_t b) {
|
||||
cell_t result;
|
||||
__asm__(
|
||||
"cmp %[a], %[b]\n\t"
|
||||
"csel %[res], %[a], %[b], lt\n\t" /* Select a if a < b, else b */
|
||||
: [res]"=r"(result)
|
||||
: [a]"r"(a),
|
||||
[b]"r"(b)
|
||||
: "cc"
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline cell_t vm_max_asm(cell_t a, cell_t b) {
|
||||
cell_t result;
|
||||
__asm__(
|
||||
"cmp %[a], %[b]\n\t"
|
||||
"csel %[res], %[a], %[b], gt\n\t" /* Select a if a > b, else b */
|
||||
: [res]"=r"(result)
|
||||
: [a]"r"(a),
|
||||
[b]"r"(b)
|
||||
: "cc"
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Absolute value using conditional negate */
|
||||
static inline cell_t vm_abs_asm(cell_t a) {
|
||||
cell_t result;
|
||||
__asm__(
|
||||
"cmp %[a], #0\n\t"
|
||||
"cneg %[res], %[a], lt\n\t" /* Negate if negative */
|
||||
: [res]"=r"(result)
|
||||
: [a]"r"(a)
|
||||
: "cc"
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 5: Bit Manipulation
|
||||
* ============================================================================
|
||||
*
|
||||
* ARM64 has excellent bit manipulation instructions
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/* Count leading zeros */
|
||||
static inline int vm_clz(cell_t x) {
|
||||
int result;
|
||||
__asm__("clz %[res], %[x]": [res]"=r"(result): [x]"r"(x));
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Count trailing zeros */
|
||||
static inline int vm_ctz(cell_t x) {
|
||||
int result;
|
||||
__asm__(
|
||||
"rbit %[tmp], %[x]\n\t" /* Reverse bits */
|
||||
"clz %[res], %[tmp]\n\t" /* Count leading zeros of reversed */
|
||||
: [res]"=r"(result),
|
||||
[tmp]"=r"(x)
|
||||
: [x]"r"(x)
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Population count (count set bits) */
|
||||
static inline int vm_popcnt(cell_t x) {
|
||||
int result;
|
||||
__asm__(
|
||||
"fmov d0, %[x]\n\t" /* Move to NEON register */
|
||||
"cnt v0.8b, v0.8b\n\t" /* Count bits in each byte */
|
||||
"addv b0, v0.8b\n\t" /* Sum all bytes */
|
||||
"fmov %w[res], s0\n\t" /* Move back to general register */
|
||||
: [res]"=r"(result)
|
||||
: [x]"r"(x)
|
||||
: "v0"
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 6: CPU Feature Detection
|
||||
* ============================================================================
|
||||
*
|
||||
* ARM64 uses a different mechanism than x86 CPUID
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/* Read ARM64 CPU ID register */
|
||||
static inline uint64_t vm_read_midr(void) {
|
||||
uint64_t midr;
|
||||
__asm__("mrs %0, midr_el1": "=r"(midr));
|
||||
return midr;
|
||||
}
|
||||
|
||||
/* Check for specific CPU features via auxiliary control register */
|
||||
static inline int vm_has_neon(void) {
|
||||
|
||||
|
||||
|
||||
#ifdef __ARM_NEON
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Check for Cortex-A72 (Raspberry Pi 4) */
|
||||
static inline int vm_is_cortex_a72(void) {
|
||||
|
||||
|
||||
|
||||
/* Cortex-A72 MIDR: 0x410FD083 */
|
||||
/* Note: Reading MIDR requires EL1, may not work in userspace */
|
||||
/* Use hwcaps instead for userspace detection */
|
||||
#ifdef __linux__
|
||||
unsigned long hwcap = getauxval(AT_HWCAP);
|
||||
return (hwcap &HWCAP_ASIMD)!= 0; /* ASIMD = Advanced SIMD (NEON) */
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 7: Cache Management (Raspberry Pi 4 specific)
|
||||
* ============================================================================
|
||||
*
|
||||
* Raspberry Pi 4 cache hierarchy:
|
||||
* - L1I: 32KB (3-way, 64-byte lines)
|
||||
* - L1D: 32KB (2-way, 64-byte lines)
|
||||
* - L2: 1MB (16-way, 64-byte lines)
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/* Prefetch data into cache */
|
||||
static inline void vm_prefetch(const void *addr) {
|
||||
__asm__ __volatile__(
|
||||
"prfm pldl1keep, [%0]\n\t" /* Prefetch for load, L1 cache, temporal */
|
||||
:
|
||||
: "r"(addr)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/* Prefetch with streaming hint (for sequential access) */
|
||||
static inline void vm_prefetch_stream(const void *addr) {
|
||||
__asm__ __volatile__(
|
||||
"prfm pldl1strm, [%0]\n\t" /* Prefetch for load, L1 cache, streaming */
|
||||
:
|
||||
: "r"(addr)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/* Data cache zero (fast way to zero cache lines) */
|
||||
static inline void vm_dc_zva(void *addr) {
|
||||
__asm__ __volatile__(
|
||||
"dc zva, %0\n\t" /* Zero entire cache line (64 bytes) */
|
||||
:
|
||||
: "r"(addr)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
#else /* !USE_ASM_OPT */
|
||||
|
||||
/* Fallback to standard implementations */
|
||||
#define vm_push_asm(vm, val) vm_push(vm, val)
|
||||
#define vm_pop_asm(vm) vm_pop(vm)
|
||||
#define vm_rpush_asm(vm, val) vm_rpush(vm, val)
|
||||
#define vm_rpop_asm(vm) vm_rpop(vm)
|
||||
|
||||
#endif /* USE_ASM_OPT */
|
||||
|
||||
/* ============================================================================
|
||||
* RASPBERRY PI 4 SPECIFIC NOTES
|
||||
* ============================================================================
|
||||
*
|
||||
* CPU: Broadcom BCM2711 (Quad-core Cortex-A72 @ 1.5GHz)
|
||||
* Architecture: ARMv8-A
|
||||
* L1 Cache: 32KB I + 32KB D per core
|
||||
* L2 Cache: 1MB shared
|
||||
* RAM: 1GB/2GB/4GB/8GB LPDDR4-3200
|
||||
*
|
||||
* Optimization Tips:
|
||||
* 1. Align hot data to 64-byte cache lines
|
||||
* 2. Use NEON for parallel operations
|
||||
* 3. Prefetch sequential data access
|
||||
* 4. Keep hot code < 32KB (fits in L1I)
|
||||
* 5. Use load/store pairs (ldp/stp) for efficiency
|
||||
*
|
||||
* Thermal Throttling:
|
||||
* - CPU throttles at 80°C
|
||||
* - Add heatsink for sustained performance
|
||||
* - Monitor: vcgencmd measure_temp
|
||||
*
|
||||
* Build Flags:
|
||||
* -march=armv8-a+crc+simd -mtune=cortex-a72 -O3 -DUSE_ASM_OPT=1
|
||||
*
|
||||
* Cross-compilation:
|
||||
* CC=aarch64-linux-gnu-gcc
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
#endif /* VM_ASM_OPT_ARM64_H */
|
||||
@@ -0,0 +1,525 @@
|
||||
/*
|
||||
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_ASM_OPT_RISCV64_H
|
||||
#define VM_ASM_OPT_RISCV64_H
|
||||
|
||||
#include "vm.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* Configuration: Define USE_ASM_OPT=1 to enable assembly optimizations
|
||||
*/
|
||||
#ifndef USE_ASM_OPT
|
||||
#define USE_ASM_OPT 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* RISC-V 64 Architecture Notes:
|
||||
* ==============================
|
||||
*
|
||||
* Register ABI (RV64GC / rv64gc):
|
||||
* - x0 (zero): hardwired zero
|
||||
* - x1 (ra): return address
|
||||
* - x2 (sp): stack pointer
|
||||
* - x3 (gp): global pointer
|
||||
* - x4 (tp): thread pointer
|
||||
* - x5–x7 (t0–t2): temporaries (caller-saved)
|
||||
* - x8–x9 (s0–s1): callee-saved / frame pointer
|
||||
* - x10–x17 (a0–a7): argument/return (caller-saved)
|
||||
* - x18–x27 (s2–s11): callee-saved
|
||||
* - x28–x31 (t3–t6): temporaries (caller-saved)
|
||||
*
|
||||
* Key differences vs x86_64 / ARM64:
|
||||
* - Pure load/store architecture — all ops work on registers
|
||||
* - No flags register; overflow detection requires explicit instructions
|
||||
* - No conditional move in base ISA (Zicond ext adds czero.eqz/czero.nez)
|
||||
* - No string-comparison instructions; byte loops required
|
||||
* - M extension: mul, mulh, div, rem — all present in rv64gc
|
||||
* - B extension (Zba/Zbb/Zbc): clz, ctz, cpop, etc. — NOT guaranteed in rv64gc
|
||||
*
|
||||
* Branchless min/max without Zicond:
|
||||
* slt t0, a, b # t0 = (a < b) ? 1 : 0
|
||||
* neg t0, t0 # t0 = (a < b) ? -1 : 0 (all-ones mask or zero)
|
||||
* sub t1, a, b # t1 = a - b
|
||||
* and t1, t1, t0 # t1 = (a-b) if a<b, else 0
|
||||
* add res, b, t1 # res = b + (a-b if a<b) = min(a,b)
|
||||
*
|
||||
* Overflow detection (no flags):
|
||||
* add res, a, b
|
||||
* # signed overflow iff same-sign operands produce opposite-sign result
|
||||
* xor t0, a, b # t0[63]=0 means same sign
|
||||
* xor t1, a, res # t1[63]=1 means sign changed
|
||||
* srai t0, t0, 63 # 0 or -1
|
||||
* srai t1, t1, 63 # 0 or -1
|
||||
* xori t0, t0, -1 # invert: -1 -> 0, 0 -> -1
|
||||
* and ovf, t0, t1 # -1 iff overflow
|
||||
* snez ovf, ovf # 0 or 1
|
||||
*/
|
||||
|
||||
#if USE_ASM_OPT
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 1: Stack Operations
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
static inline void vm_push_asm(VM *vm, cell_t value) {
|
||||
int error = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"lw t0, %[dsp]\n\t" /* t0 = dsp */
|
||||
"li t1, 1022\n\t"
|
||||
"bge t0, t1, 1f\n\t" /* branch if dsp >= 1022 (overflow) */
|
||||
"addi t0, t0, 1\n\t" /* dsp++ */
|
||||
"sw t0, %[dsp]\n\t"
|
||||
"slli t1, t0, 3\n\t" /* t1 = dsp * 8 */
|
||||
"add t1, %[stack], t1\n\t" /* t1 = &data_stack[dsp] */
|
||||
"sd %[val], 0(t1)\n\t" /* store value */
|
||||
"sw zero, %[err]\n\t"
|
||||
"j 2f\n\t"
|
||||
"1:\n\t"
|
||||
"li t0, 1\n\t"
|
||||
"sw t0, %[err]\n\t"
|
||||
"2:\n\t"
|
||||
: [dsp]"+m"(vm->dsp),
|
||||
[err]"=m"(error)
|
||||
: [val]"r"(value),
|
||||
[stack]"r"(vm->data_stack)
|
||||
: "t0", "t1", "memory"
|
||||
);
|
||||
|
||||
if (__builtin_expect(error, 0)) {
|
||||
vm->error = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline cell_t vm_pop_asm(VM *vm) {
|
||||
cell_t value = 0;
|
||||
int error = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"lw t0, %[dsp]\n\t"
|
||||
"bltz t0, 1f\n\t" /* branch if dsp < 0 (underflow) */
|
||||
"slli t1, t0, 3\n\t"
|
||||
"add t1, %[stack], t1\n\t" /* t1 = &data_stack[dsp] (old dsp) */
|
||||
"addi t0, t0, -1\n\t"
|
||||
"sw t0, %[dsp]\n\t" /* write dsp back BEFORE loading the
|
||||
value: %[dsp] is a memory operand
|
||||
referenced twice in this template
|
||||
(read above, write here); loading
|
||||
%[val] (a register output) before
|
||||
this write-back let clang's
|
||||
allocator reuse %[dsp]'s address
|
||||
register for %[val], corrupting
|
||||
the write-back — GCC happened to
|
||||
pick different registers and
|
||||
never hit it. Loading %[val] last
|
||||
means %[dsp]'s final use has
|
||||
already happened by the time the
|
||||
output register is live. */
|
||||
"ld %[val], 0(t1)\n\t"
|
||||
"sw zero, %[err]\n\t"
|
||||
"j 2f\n\t"
|
||||
"1:\n\t"
|
||||
"li t0, 1\n\t"
|
||||
"sw t0, %[err]\n\t"
|
||||
"mv %[val], zero\n\t"
|
||||
"2:\n\t"
|
||||
: [dsp]"+m"(vm->dsp),
|
||||
[val]"=r"(value),
|
||||
[err]"=m"(error)
|
||||
: [stack]"r"(vm->data_stack)
|
||||
: "t0", "t1", "memory"
|
||||
);
|
||||
|
||||
if (__builtin_expect(error, 0)) {
|
||||
vm->error = 1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline void vm_rpush_asm(VM *vm, cell_t value) {
|
||||
int error = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"lw t0, %[rsp]\n\t"
|
||||
"li t1, 1022\n\t"
|
||||
"bge t0, t1, 1f\n\t"
|
||||
"addi t0, t0, 1\n\t"
|
||||
"sw t0, %[rsp]\n\t"
|
||||
"slli t1, t0, 3\n\t"
|
||||
"add t1, %[stack], t1\n\t"
|
||||
"sd %[val], 0(t1)\n\t"
|
||||
"sw zero, %[err]\n\t"
|
||||
"j 2f\n\t"
|
||||
"1:\n\t"
|
||||
"li t0, 1\n\t"
|
||||
"sw t0, %[err]\n\t"
|
||||
"2:\n\t"
|
||||
: [rsp]"+m"(vm->rsp),
|
||||
[err]"=m"(error)
|
||||
: [val]"r"(value),
|
||||
[stack]"r"(vm->return_stack)
|
||||
: "t0", "t1", "memory"
|
||||
);
|
||||
|
||||
if (__builtin_expect(error, 0)) {
|
||||
vm->error = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline cell_t vm_rpop_asm(VM *vm) {
|
||||
cell_t value = 0;
|
||||
int error = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"lw t0, %[rsp]\n\t"
|
||||
"bltz t0, 1f\n\t"
|
||||
"slli t1, t0, 3\n\t"
|
||||
"add t1, %[stack], t1\n\t" /* t1 = &return_stack[rsp] (old rsp) */
|
||||
"addi t0, t0, -1\n\t"
|
||||
"sw t0, %[rsp]\n\t" /* write rsp back before loading the
|
||||
value — see vm_pop_asm for why */
|
||||
"ld %[val], 0(t1)\n\t"
|
||||
"sw zero, %[err]\n\t"
|
||||
"j 2f\n\t"
|
||||
"1:\n\t"
|
||||
"li t0, 1\n\t"
|
||||
"sw t0, %[err]\n\t"
|
||||
"mv %[val], zero\n\t"
|
||||
"2:\n\t"
|
||||
: [rsp]"+m"(vm->rsp),
|
||||
[val]"=r"(value),
|
||||
[err]"=m"(error)
|
||||
: [stack]"r"(vm->return_stack)
|
||||
: "t0", "t1", "memory"
|
||||
);
|
||||
|
||||
if (__builtin_expect(error, 0)) {
|
||||
vm->error = 1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 2: Arithmetic with Overflow Detection
|
||||
* ============================================================================
|
||||
*
|
||||
* RISC-V has no flags register. Overflow is detected by inspecting operand
|
||||
* and result sign bits directly.
|
||||
*
|
||||
* Signed addition overflows iff operands have the same sign and the result
|
||||
* has the opposite sign:
|
||||
* ovf = ~(a XOR b)[63] AND (a XOR result)[63]
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
static inline int vm_add_check_overflow(cell_t a, cell_t b, cell_t *result) {
|
||||
cell_t res;
|
||||
cell_t ovf;
|
||||
|
||||
__asm__(
|
||||
"add %[res], %[a], %[b]\n\t"
|
||||
"xor t0, %[a], %[b]\n\t" /* t0[63]=0 if same sign */
|
||||
"xor t1, %[a], %[res]\n\t" /* t1[63]=1 if sign changed */
|
||||
"not t0, t0\n\t" /* invert: same-sign -> MSB=1 */
|
||||
"and t0, t0, t1\n\t" /* both conditions met */
|
||||
"srli %[ovf], t0, 63\n\t" /* extract MSB as 0 or 1 */
|
||||
: [res]"=r"(res), [ovf]"=r"(ovf)
|
||||
: [a]"r"(a), [b]"r"(b)
|
||||
: "t0", "t1"
|
||||
);
|
||||
|
||||
*result = res;
|
||||
return (int)ovf;
|
||||
}
|
||||
|
||||
static inline int vm_sub_check_overflow(cell_t a, cell_t b, cell_t *result) {
|
||||
cell_t res;
|
||||
cell_t ovf;
|
||||
|
||||
__asm__(
|
||||
"sub %[res], %[a], %[b]\n\t"
|
||||
/* Signed subtraction a-b overflows iff a and b have different signs
|
||||
* AND result has different sign from a:
|
||||
* ovf = (a XOR b)[63] AND (a XOR result)[63] */
|
||||
"xor t0, %[a], %[b]\n\t" /* t0[63]=1 if different sign */
|
||||
"xor t1, %[a], %[res]\n\t" /* t1[63]=1 if result sign differs */
|
||||
"and t0, t0, t1\n\t"
|
||||
"srli %[ovf], t0, 63\n\t"
|
||||
: [res]"=r"(res), [ovf]"=r"(ovf)
|
||||
: [a]"r"(a), [b]"r"(b)
|
||||
: "t0", "t1"
|
||||
);
|
||||
|
||||
*result = res;
|
||||
return (int)ovf;
|
||||
}
|
||||
|
||||
/* RV64M: mulh gives the high 64 bits of signed 64×64 product */
|
||||
static inline void vm_mul_double(cell_t a, cell_t b, cell_t *hi, cell_t *lo) {
|
||||
__asm__(
|
||||
"mul %[lo], %[a], %[b]\n\t" /* low 64 bits */
|
||||
"mulh %[hi], %[a], %[b]\n\t" /* high 64 bits (signed) */
|
||||
: [lo]"=r"(*lo), [hi]"=r"(*hi)
|
||||
: [a]"r"(a), [b]"r"(b)
|
||||
);
|
||||
}
|
||||
|
||||
/* RV64M: div and rem */
|
||||
static inline void vm_divmod(cell_t dividend, cell_t divisor,
|
||||
cell_t *quotient, cell_t *remainder) {
|
||||
cell_t quot, rem;
|
||||
__asm__(
|
||||
"div %[quot], %[dividend], %[divisor]\n\t"
|
||||
"rem %[rem], %[dividend], %[divisor]\n\t"
|
||||
: [quot]"=r"(quot), [rem]"=r"(rem)
|
||||
: [dividend]"r"(dividend), [divisor]"r"(divisor)
|
||||
);
|
||||
*quotient = quot;
|
||||
*remainder = rem;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 3: String Operations
|
||||
* ============================================================================
|
||||
*
|
||||
* RISC-V has no string or SIMD instructions in the base ISA (rv64gc).
|
||||
* We write tight scalar loops in asm to avoid libc call overhead inside
|
||||
* the inner interpreter hot path.
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
static inline int vm_strcmp_asm(const char *s1, const char *s2, size_t len) {
|
||||
int result = 0;
|
||||
__asm__ __volatile__(
|
||||
"beqz %[len], 2f\n\t"
|
||||
"1:\n\t"
|
||||
"lb t0, 0(%[s1])\n\t"
|
||||
"lb t1, 0(%[s2])\n\t"
|
||||
"bne t0, t1, 3f\n\t"
|
||||
"addi %[s1], %[s1], 1\n\t"
|
||||
"addi %[s2], %[s2], 1\n\t"
|
||||
"addi %[len], %[len], -1\n\t"
|
||||
"bnez %[len], 1b\n\t"
|
||||
"2:\n\t"
|
||||
"mv %[res], zero\n\t"
|
||||
"j 4f\n\t"
|
||||
"3:\n\t"
|
||||
"li %[res], 1\n\t"
|
||||
"4:\n\t"
|
||||
: [res]"=r"(result),
|
||||
[s1]"+r"(s1), [s2]"+r"(s2), [len]"+r"(len)
|
||||
:
|
||||
: "t0", "t1", "memory"
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void vm_memcpy_asm(void *dest, const void *src, size_t len) {
|
||||
/* Copy 8 bytes at a time, then handle remainder */
|
||||
__asm__ __volatile__(
|
||||
"srli t2, %[len], 3\n\t" /* t2 = len / 8 */
|
||||
"beqz t2, 2f\n\t"
|
||||
"1:\n\t"
|
||||
"ld t0, 0(%[src])\n\t"
|
||||
"sd t0, 0(%[dst])\n\t"
|
||||
"addi %[src], %[src], 8\n\t"
|
||||
"addi %[dst], %[dst], 8\n\t"
|
||||
"addi t2, t2, -1\n\t"
|
||||
"bnez t2, 1b\n\t"
|
||||
"2:\n\t"
|
||||
"andi %[len], %[len], 7\n\t" /* remaining bytes */
|
||||
"beqz %[len], 4f\n\t"
|
||||
"3:\n\t"
|
||||
"lb t0, 0(%[src])\n\t"
|
||||
"sb t0, 0(%[dst])\n\t"
|
||||
"addi %[src], %[src], 1\n\t"
|
||||
"addi %[dst], %[dst], 1\n\t"
|
||||
"addi %[len], %[len], -1\n\t"
|
||||
"bnez %[len], 3b\n\t"
|
||||
"4:\n\t"
|
||||
: [src]"+r"(src), [dst]"+r"(dest), [len]"+r"(len)
|
||||
:
|
||||
: "t0", "t2", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
static inline void vm_memzero_asm(void *dest, size_t len) {
|
||||
__asm__ __volatile__(
|
||||
"srli t2, %[len], 3\n\t"
|
||||
"beqz t2, 2f\n\t"
|
||||
"1:\n\t"
|
||||
"sd zero, 0(%[dst])\n\t"
|
||||
"addi %[dst], %[dst], 8\n\t"
|
||||
"addi t2, t2, -1\n\t"
|
||||
"bnez t2, 1b\n\t"
|
||||
"2:\n\t"
|
||||
"andi %[len], %[len], 7\n\t"
|
||||
"beqz %[len], 4f\n\t"
|
||||
"3:\n\t"
|
||||
"sb zero, 0(%[dst])\n\t"
|
||||
"addi %[dst], %[dst], 1\n\t"
|
||||
"addi %[len], %[len], -1\n\t"
|
||||
"bnez %[len], 3b\n\t"
|
||||
"4:\n\t"
|
||||
: [dst]"+r"(dest), [len]"+r"(len)
|
||||
:
|
||||
: "t2", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 4: Branchless Min/Max
|
||||
* ============================================================================
|
||||
*
|
||||
* RISC-V base ISA has no conditional move. The bit-mask trick avoids branches:
|
||||
* mask = -(a < b) → all-ones if a<b, zero otherwise
|
||||
* min = b + (a-b) & mask
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
static inline cell_t vm_min_asm(cell_t a, cell_t b) {
|
||||
cell_t result;
|
||||
__asm__(
|
||||
"slt t0, %[a], %[b]\n\t" /* t0 = (a < b) ? 1 : 0 */
|
||||
"neg t0, t0\n\t" /* t0 = mask (-1 or 0) */
|
||||
"sub t1, %[a], %[b]\n\t"
|
||||
"and t1, t1, t0\n\t"
|
||||
"add %[res], %[b], t1\n\t"
|
||||
: [res]"=r"(result)
|
||||
: [a]"r"(a), [b]"r"(b)
|
||||
: "t0", "t1"
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline cell_t vm_max_asm(cell_t a, cell_t b) {
|
||||
cell_t result;
|
||||
__asm__(
|
||||
"slt t0, %[b], %[a]\n\t" /* t0 = (b < a) ? 1 : 0 → (a > b) */
|
||||
"neg t0, t0\n\t"
|
||||
"sub t1, %[a], %[b]\n\t"
|
||||
"and t1, t1, t0\n\t"
|
||||
"add %[res], %[b], t1\n\t"
|
||||
: [res]"=r"(result)
|
||||
: [a]"r"(a), [b]"r"(b)
|
||||
: "t0", "t1"
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline cell_t vm_abs_asm(cell_t a) {
|
||||
cell_t result;
|
||||
__asm__(
|
||||
"srai t0, %[a], 63\n\t" /* t0 = sign mask (-1 or 0) */
|
||||
"xor %[res], %[a], t0\n\t" /* flip bits if negative */
|
||||
"sub %[res], %[res], t0\n\t" /* add 1 if negative (two's complement) */
|
||||
: [res]"=r"(result)
|
||||
: [a]"r"(a)
|
||||
: "t0"
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 5: Bit Manipulation
|
||||
* ============================================================================
|
||||
*
|
||||
* rv64gc does NOT include the B extension (Zbb), so clz/ctz/cpop are not
|
||||
* available as single instructions. GCC builtins map to the best available
|
||||
* form — if the toolchain targets Zbb they'll lower to single instructions;
|
||||
* on plain rv64gc they fall back to efficient multi-instruction sequences.
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
static inline int vm_clz(cell_t x) {
|
||||
return __builtin_clzll((unsigned long long)x);
|
||||
}
|
||||
|
||||
static inline int vm_ctz(cell_t x) {
|
||||
return __builtin_ctzll((unsigned long long)x);
|
||||
}
|
||||
|
||||
static inline int vm_popcnt(cell_t x) {
|
||||
return __builtin_popcountll((unsigned long long)x);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 6: Cache Operations
|
||||
* ============================================================================
|
||||
*
|
||||
* RISC-V uses fence.i for instruction cache coherence.
|
||||
* There is no user-space prefetch instruction in the base ISA;
|
||||
* we use GCC's __builtin_prefetch which lowers to a hint if the target
|
||||
* supports one (e.g., Zicbop extension), otherwise it's a no-op.
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
static inline void vm_prefetch(const void *addr) {
|
||||
__builtin_prefetch(addr, 0, 3);
|
||||
}
|
||||
|
||||
static inline void vm_prefetch_stream(const void *addr) {
|
||||
__builtin_prefetch(addr, 0, 0);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* OPTIMIZATION 7: CPU Feature Detection
|
||||
* ============================================================================
|
||||
*
|
||||
* RISC-V has no CPUID equivalent in user space. Extension detection requires
|
||||
* either kernel support (/proc/cpuinfo, riscv_hwprobe syscall on Linux 6.4+)
|
||||
* or compiler defines. We expose a simple interface here.
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
static inline int vm_has_riscv_m_ext(void) {
|
||||
#if defined(__riscv_mul)
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int vm_has_riscv_b_ext(void) {
|
||||
#if defined(__riscv_zbb)
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#else /* !USE_ASM_OPT */
|
||||
|
||||
#define vm_push_asm(vm, val) vm_push(vm, val)
|
||||
#define vm_pop_asm(vm) vm_pop(vm)
|
||||
#define vm_rpush_asm(vm, val) vm_rpush(vm, val)
|
||||
#define vm_rpop_asm(vm) vm_rpop(vm)
|
||||
|
||||
#endif /* USE_ASM_OPT */
|
||||
|
||||
#endif /* VM_ASM_OPT_RISCV64_H */
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/** @cond */
|
||||
#ifndef VM_DEBUG_H
|
||||
#define VM_DEBUG_H
|
||||
/** @endcond */
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
/**
|
||||
* @brief Sets the VM instance to dump state for when signals occur
|
||||
* @param vm Pointer to the VM instance to monitor
|
||||
* @note Should be called once after vm_init
|
||||
*/
|
||||
void vm_debug_set_current_vm(VM * vm);
|
||||
|
||||
/**
|
||||
* @brief Installs signal handlers for SIGSEGV and SIGABRT
|
||||
* @details The handlers will print VM state before re-raising the signal
|
||||
*/
|
||||
void vm_debug_install_signal_handlers(void);
|
||||
|
||||
/**
|
||||
* @brief Manually dumps the state of a VM instance
|
||||
* @param vm The VM instance to dump state for
|
||||
* @param reason Description of why the dump was triggered
|
||||
* @note Typically called before exit(1) on test failure
|
||||
*/
|
||||
void vm_debug_dump_state(const VM *vm, const char *reason);
|
||||
|
||||
#endif /* VM_DEBUG_H */
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* vm_host.h - Host services abstraction for StarForth VM
|
||||
*
|
||||
* Provides pluggable allocator, time, and mutex services.
|
||||
* Hosted builds use libc; kernel builds use PMM/kmalloc/TSC.
|
||||
*
|
||||
* M7 Non-Negotiable: All allocation goes through these hooks.
|
||||
* No direct malloc/free in VM code.
|
||||
*/
|
||||
|
||||
#ifndef STARKERNEL_VM_HOST_H
|
||||
#define STARKERNEL_VM_HOST_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct DictEntry;
|
||||
|
||||
/**
|
||||
* Host allocator function signature
|
||||
* Returns NULL on failure
|
||||
*/
|
||||
typedef void* (*sk_alloc_fn)(size_t size, size_t align);
|
||||
|
||||
/**
|
||||
* Host free function signature
|
||||
*/
|
||||
typedef void (*sk_free_fn)(void *ptr);
|
||||
|
||||
/**
|
||||
* Host monotonic time function signature
|
||||
* Returns nanoseconds since arbitrary epoch
|
||||
* In PARITY_MODE, returns deterministic fake time
|
||||
*/
|
||||
typedef uint64_t (*sk_time_fn)(void);
|
||||
|
||||
/**
|
||||
* Host mutex operations (no-op in kernel single-threaded mode)
|
||||
*/
|
||||
typedef int (*sk_mutex_init_fn)(void *mutex);
|
||||
typedef int (*sk_mutex_lock_fn)(void *mutex);
|
||||
typedef int (*sk_mutex_unlock_fn)(void *mutex);
|
||||
typedef void (*sk_mutex_destroy_fn)(void *mutex);
|
||||
|
||||
/**
|
||||
* Host console output (for words that print)
|
||||
* Returns number of bytes written, or -1 on error
|
||||
*/
|
||||
typedef int (*sk_puts_fn)(const char *str);
|
||||
typedef int (*sk_putc_fn)(int c);
|
||||
|
||||
/**
|
||||
* Host execution-token validation
|
||||
* Returns 1 if pointer is executable, 0 otherwise
|
||||
*/
|
||||
typedef bool (*sk_xt_validate_fn)(const void *ptr);
|
||||
|
||||
/**
|
||||
* Host dictionary entry ownership check
|
||||
*/
|
||||
typedef bool (*sk_xt_entry_owns_fn)(const void *entry, size_t bytes);
|
||||
|
||||
/**
|
||||
* Host fatal handler (panic)
|
||||
*/
|
||||
typedef void (*sk_panic_fn)(const char *message);
|
||||
|
||||
/**
|
||||
* VMHostServices - pluggable host operations
|
||||
*
|
||||
* Hosted builds point these at libc functions.
|
||||
* Kernel builds point these at kernel implementations.
|
||||
*/
|
||||
typedef struct VMHostServices {
|
||||
/* Memory allocation */
|
||||
sk_alloc_fn alloc;
|
||||
sk_free_fn free;
|
||||
|
||||
/* Monotonic time source */
|
||||
sk_time_fn monotonic_ns;
|
||||
|
||||
/* Mutex operations (can be no-ops) */
|
||||
sk_mutex_init_fn mutex_init;
|
||||
sk_mutex_lock_fn mutex_lock;
|
||||
sk_mutex_unlock_fn mutex_unlock;
|
||||
sk_mutex_destroy_fn mutex_destroy;
|
||||
|
||||
/* Console output */
|
||||
sk_puts_fn puts;
|
||||
sk_putc_fn putc;
|
||||
|
||||
/* XT validation */
|
||||
sk_xt_validate_fn is_executable_ptr;
|
||||
sk_xt_entry_owns_fn owns_xt_entry;
|
||||
sk_panic_fn panic;
|
||||
|
||||
/* Flags */
|
||||
int parity_mode; /* 1 = deterministic time */
|
||||
int verbose; /* 1 = enable logging */
|
||||
} VMHostServices;
|
||||
|
||||
/**
|
||||
* Get the current host services instance.
|
||||
* Returns hosted or kernel services depending on build.
|
||||
*/
|
||||
const VMHostServices* sk_host_services(void);
|
||||
|
||||
/**
|
||||
* Initialize host services for kernel build.
|
||||
* Must be called before any VM operations.
|
||||
*/
|
||||
#ifdef __STARKERNEL__
|
||||
void sk_host_init(void);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Convenience macros for common operations
|
||||
* Use these instead of direct malloc/free/etc.
|
||||
*/
|
||||
#define SK_ALLOC(size) (sk_host_services()->alloc((size), sizeof(void*)))
|
||||
#define SK_ALLOC_ALIGNED(s, a) (sk_host_services()->alloc((s), (a)))
|
||||
#define SK_FREE(ptr) (sk_host_services()->free(ptr))
|
||||
#define SK_TIME_NS() (sk_host_services()->monotonic_ns())
|
||||
#define SK_PUTS(str) (sk_host_services()->puts(str))
|
||||
#define SK_PUTC(c) (sk_host_services()->putc(c))
|
||||
|
||||
/**
|
||||
* Mutex wrapper macros
|
||||
*/
|
||||
#define SK_MUTEX_INIT(m) (sk_host_services()->mutex_init(m))
|
||||
#define SK_MUTEX_LOCK(m) (sk_host_services()->mutex_lock(m))
|
||||
#define SK_MUTEX_UNLOCK(m) (sk_host_services()->mutex_unlock(m))
|
||||
#define SK_MUTEX_DESTROY(m) (sk_host_services()->mutex_destroy(m))
|
||||
|
||||
#endif /* STARKERNEL_VM_HOST_H */
|
||||
@@ -0,0 +1,556 @@
|
||||
/*
|
||||
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_INNER_INTERP_ARM64_H
|
||||
#define VM_INNER_INTERP_ARM64_H
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
/*
|
||||
* ARM64 DIRECT-THREADED INNER INTERPRETER
|
||||
* ========================================
|
||||
*
|
||||
* ARM64 advantages over x86_64:
|
||||
* - More registers (31 vs 16)
|
||||
* - Better branch prediction
|
||||
* - Conditional execution
|
||||
* - Load/store with auto-increment
|
||||
* - Lower power consumption
|
||||
*
|
||||
* Register Allocation (using callee-saved registers):
|
||||
* - x19: VM pointer (callee-saved)
|
||||
* - x20: Instruction Pointer (IP)
|
||||
* - x21: Data Stack Pointer (DSP) - actual pointer
|
||||
* - x22: Return Stack Pointer (RSP) - actual pointer
|
||||
* - x23: Top of Stack cache (TOS) - keep in register
|
||||
* - x24-x28: Available for future use
|
||||
*
|
||||
* Why more registers help:
|
||||
* - Can keep TOS in register (x23)
|
||||
* - More scratch registers for complex operations
|
||||
* - Less memory traffic
|
||||
*
|
||||
* Performance on Raspberry Pi 4:
|
||||
* - Expected 3-5x speedup for threaded code
|
||||
* - Better than x86_64 due to more registers
|
||||
* - Lower power = less thermal throttling
|
||||
*/
|
||||
|
||||
#ifndef USE_DIRECT_THREADING
|
||||
#define USE_DIRECT_THREADING 0
|
||||
#endif
|
||||
|
||||
#if USE_DIRECT_THREADING
|
||||
|
||||
/*
|
||||
* NEXT macro - ARM64 version
|
||||
*
|
||||
* On ARM64, we can use:
|
||||
* - Load with post-increment (ldr x0, [x20], #8)
|
||||
* - Indirect branch (br x0)
|
||||
*/
|
||||
#define NEXT_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x20], #8\n\t" /* Load word ptr from [IP], IP += 8 */ \
|
||||
"br x0\n\t" /* Branch to word */ \
|
||||
::: "x0", "memory" \
|
||||
)
|
||||
|
||||
/*
|
||||
* DOCOL - Enter a colon definition (ARM64)
|
||||
*/
|
||||
static inline void vm_docol_arm64(VM *vm) {
|
||||
__asm__ __volatile__(
|
||||
/* Save current IP on return stack */
|
||||
"str x20, [x22], #8\n\t" /* [RSP] = IP, RSP += 8 */
|
||||
|
||||
/* IP = address of colon definition body */
|
||||
"ldr x0, %[entry]\n\t"
|
||||
"ldr x20, [x0]\n\t" /* IP = entry->code_field */
|
||||
|
||||
/* Continue with NEXT */
|
||||
"ldr x0, [x20], #8\n\t"
|
||||
"br x0\n\t"
|
||||
:
|
||||
: [entry]"m"(vm->current_executing_entry)
|
||||
: "x0", "x20", "x22", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* EXIT - Return from a colon definition (ARM64)
|
||||
*/
|
||||
static inline void vm_exit_arm64(void) {
|
||||
__asm__ __volatile__(
|
||||
/* Restore IP from return stack */
|
||||
"ldr x20, [x22, #-8]!\n\t" /* RSP -= 8, IP = [RSP] (pre-decrement) */
|
||||
|
||||
/* Continue with NEXT */
|
||||
"ldr x0, [x20], #8\n\t"
|
||||
"br x0\n\t"
|
||||
::: "x0", "x20", "x22", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup registers before entering inner loop
|
||||
*/
|
||||
static inline void vm_setup_registers_arm64(VM *vm) {
|
||||
__asm__ __volatile__(
|
||||
/* Save callee-saved registers (required by ARM64 ABI) */
|
||||
"stp x19, x20, [sp, #-80]!\n\t"
|
||||
"stp x21, x22, [sp, #16]\n\t"
|
||||
"stp x23, x24, [sp, #32]\n\t"
|
||||
"stp x25, x26, [sp, #48]\n\t"
|
||||
"stp x27, x28, [sp, #64]\n\t"
|
||||
|
||||
/* Load VM pointer */
|
||||
"mov x19, %[vm]\n\t" /* x19 = VM pointer */
|
||||
|
||||
/* Load IP */
|
||||
"ldr x20, %[ip]\n\t" /* x20 = IP */
|
||||
|
||||
/* Calculate data stack pointer */
|
||||
"ldr w0, %[dsp]\n\t"
|
||||
"ldr x1, %[dstack]\n\t"
|
||||
"add x21, x1, x0, sxtw #3\n\t" /* x21 = &data_stack[dsp] */
|
||||
|
||||
/* Calculate return stack pointer */
|
||||
"ldr w0, %[rsp]\n\t"
|
||||
"ldr x1, %[rstack]\n\t"
|
||||
"add x22, x1, x0, sxtw #3\n\t" /* x22 = &return_stack[rsp] */
|
||||
|
||||
/* Load TOS into x23 if stack not empty */
|
||||
"ldr w0, %[dsp]\n\t"
|
||||
"cmp w0, #0\n\t"
|
||||
"b.lt 1f\n\t"
|
||||
"ldr x23, [x21]\n\t" /* x23 = TOS (cached) */
|
||||
"1:\n\t"
|
||||
:
|
||||
: [vm]"r"(vm),
|
||||
[ip]"m"(vm->ip),
|
||||
[dsp]"m"(vm->dsp),
|
||||
[rsp]"m"(vm->rsp),
|
||||
[dstack]"m"(vm->data_stack),
|
||||
[rstack]"m"(vm->return_stack)
|
||||
: "x0", "x1", "x19", "x20", "x21", "x22", "x23", "x24",
|
||||
"x25", "x26", "x27", "x28", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Save registers back to VM structure
|
||||
*/
|
||||
static inline void vm_save_registers_arm64(VM *vm) {
|
||||
__asm__ __volatile__(
|
||||
/* Save TOS back to stack */
|
||||
"str x23, [x21]\n\t"
|
||||
|
||||
/* Save IP */
|
||||
"str x20, %[ip]\n\t"
|
||||
|
||||
/* Calculate and save dsp */
|
||||
"ldr x0, %[dstack]\n\t"
|
||||
"sub x1, x21, x0\n\t"
|
||||
"lsr x1, x1, #3\n\t" /* / 8 */
|
||||
"str w1, %[dsp]\n\t"
|
||||
|
||||
/* Calculate and save rsp */
|
||||
"ldr x0, %[rstack]\n\t"
|
||||
"sub x1, x22, x0\n\t"
|
||||
"lsr x1, x1, #3\n\t"
|
||||
"str w1, %[rsp]\n\t"
|
||||
|
||||
/* Restore callee-saved registers */
|
||||
"ldp x27, x28, [sp, #64]\n\t"
|
||||
"ldp x25, x26, [sp, #48]\n\t"
|
||||
"ldp x23, x24, [sp, #32]\n\t"
|
||||
"ldp x21, x22, [sp, #16]\n\t"
|
||||
"ldp x19, x20, [sp], #80\n\t"
|
||||
: [ip]"=m"(vm->ip),
|
||||
[dsp]"=m"(vm->dsp),
|
||||
[rsp]"=m"(vm->rsp)
|
||||
: [dstack]"m"(vm->data_stack),
|
||||
[rstack]"m"(vm->return_stack)
|
||||
: "x0", "x1", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @defgroup primitives Fast Primitive Word Implementations
|
||||
* @brief Register-based stack primitives optimized for ARM64
|
||||
*
|
||||
* Key optimization: TOS stays in x23, avoiding memory traffic
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @brief DUP primitive - duplicates top of stack item */
|
||||
#define PRIM_DUP_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"str x23, [x21, #8]!\n\t" /* Store TOS, advance DSP */ \
|
||||
::: "x21", "memory" \
|
||||
)
|
||||
|
||||
/* DROP - remove top of stack */
|
||||
#define PRIM_DROP_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x23, [x21], #-8\n\t" /* Load new TOS, decrement DSP */ \
|
||||
::: "x21", "x23", "memory" \
|
||||
)
|
||||
|
||||
/* SWAP - swap top two stack items */
|
||||
#define PRIM_SWAP_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21]\n\t" /* Load NOS */ \
|
||||
"str x23, [x21]\n\t" /* Store TOS to NOS position */ \
|
||||
"mov x23, x0\n\t" /* TOS = old NOS */ \
|
||||
::: "x0", "x23", "memory" \
|
||||
)
|
||||
|
||||
/* OVER - copy second item to top */
|
||||
#define PRIM_OVER_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21]\n\t" /* Load NOS */ \
|
||||
"str x23, [x21, #8]!\n\t" /* Store TOS, advance DSP */ \
|
||||
"mov x23, x0\n\t" /* TOS = NOS */ \
|
||||
::: "x0", "x21", "x23", "memory" \
|
||||
)
|
||||
|
||||
/* ROT - rotate top three items */
|
||||
#define PRIM_ROT_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21]\n\t" /* Load item2 */ \
|
||||
"ldr x1, [x21, #-8]\n\t" /* Load item3 */ \
|
||||
"str x1, [x21]\n\t" /* item3 -> NOS */ \
|
||||
"str x23, [x21, #-8]\n\t" /* TOS -> item3 */ \
|
||||
"mov x23, x0\n\t" /* item2 -> TOS */ \
|
||||
::: "x0", "x1", "x23", "memory" \
|
||||
)
|
||||
|
||||
/* + - add top two stack items */
|
||||
#define PRIM_PLUS_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21], #-8\n\t" /* Load NOS, decrement DSP */ \
|
||||
"add x23, x23, x0\n\t" /* TOS = TOS + NOS */ \
|
||||
::: "x0", "x21", "x23" \
|
||||
)
|
||||
|
||||
/* - - subtract top from second */
|
||||
#define PRIM_MINUS_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21], #-8\n\t" /* Load NOS, decrement DSP */ \
|
||||
"sub x23, x0, x23\n\t" /* TOS = NOS - TOS */ \
|
||||
::: "x0", "x21", "x23" \
|
||||
)
|
||||
|
||||
/* * - multiply top two stack items */
|
||||
#define PRIM_STAR_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21], #-8\n\t" /* Load NOS, decrement DSP */ \
|
||||
"mul x23, x23, x0\n\t" /* TOS = TOS * NOS */ \
|
||||
::: "x0", "x21", "x23" \
|
||||
)
|
||||
|
||||
/* / - divide second by top */
|
||||
#define PRIM_SLASH_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21], #-8\n\t" /* Load NOS (dividend), decrement DSP */ \
|
||||
"sdiv x23, x0, x23\n\t" /* TOS = NOS / TOS */ \
|
||||
::: "x0", "x21", "x23" \
|
||||
)
|
||||
|
||||
/* MOD - modulo */
|
||||
#define PRIM_MOD_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21], #-8\n\t" /* Load NOS, decrement DSP */ \
|
||||
"sdiv x1, x0, x23\n\t" /* quotient = NOS / TOS */ \
|
||||
"msub x23, x1, x23, x0\n\t" /* TOS = NOS - quotient * TOS */ \
|
||||
::: "x0", "x1", "x21", "x23" \
|
||||
)
|
||||
|
||||
/* AND - bitwise and */
|
||||
#define PRIM_AND_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21], #-8\n\t" \
|
||||
"and x23, x23, x0\n\t" \
|
||||
::: "x0", "x21", "x23" \
|
||||
)
|
||||
|
||||
/* OR - bitwise or */
|
||||
#define PRIM_OR_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21], #-8\n\t" \
|
||||
"orr x23, x23, x0\n\t" \
|
||||
::: "x0", "x21", "x23" \
|
||||
)
|
||||
|
||||
/* XOR - bitwise xor */
|
||||
#define PRIM_XOR_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21], #-8\n\t" \
|
||||
"eor x23, x23, x0\n\t" \
|
||||
::: "x0", "x21", "x23" \
|
||||
)
|
||||
|
||||
/* INVERT - bitwise not */
|
||||
#define PRIM_INVERT_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"mvn x23, x23\n\t" \
|
||||
::: "x23" \
|
||||
)
|
||||
|
||||
/* NEGATE - two's complement */
|
||||
#define PRIM_NEGATE_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"neg x23, x23\n\t" \
|
||||
::: "x23" \
|
||||
)
|
||||
|
||||
/* @ - fetch cell from memory */
|
||||
#define PRIM_FETCH_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x19]\n\t" /* Load VM->memory */ \
|
||||
"ldr x23, [x0, x23]\n\t" /* TOS = memory[TOS] */ \
|
||||
::: "x0", "x23", "memory" \
|
||||
)
|
||||
|
||||
/* ! - store cell to memory */
|
||||
#define PRIM_STORE_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21], #-8\n\t" /* Load address, decrement DSP */ \
|
||||
"ldr x1, [x19]\n\t" /* Load VM->memory */ \
|
||||
"str x23, [x1, x0]\n\t" /* memory[addr] = value */ \
|
||||
"ldr x23, [x21]\n\t" /* Load new TOS */ \
|
||||
::: "x0", "x1", "x21", "x23", "memory" \
|
||||
)
|
||||
|
||||
/* C@ - fetch byte from memory */
|
||||
#define PRIM_C_FETCH_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x19]\n\t" \
|
||||
"ldrb w23, [x0, x23]\n\t" /* Load byte, zero-extend */ \
|
||||
::: "x0", "x23", "memory" \
|
||||
)
|
||||
|
||||
/* C! - store byte to memory */
|
||||
#define PRIM_C_STORE_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21], #-8\n\t" \
|
||||
"ldr x1, [x19]\n\t" \
|
||||
"strb w23, [x1, x0]\n\t" /* Store byte */ \
|
||||
"ldr x23, [x21]\n\t" \
|
||||
::: "x0", "x1", "x21", "x23", "memory" \
|
||||
)
|
||||
|
||||
/* >R - push to return stack */
|
||||
#define PRIM_TO_R_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"str x23, [x22], #8\n\t" /* Store to RS, advance RSP */ \
|
||||
"ldr x23, [x21], #-8\n\t" /* Load new TOS, decrement DSP */ \
|
||||
::: "x21", "x22", "x23", "memory" \
|
||||
)
|
||||
|
||||
/* R> - pop from return stack */
|
||||
#define PRIM_R_FROM_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"str x23, [x21, #8]!\n\t" /* Store TOS, advance DSP */ \
|
||||
"ldr x23, [x22, #-8]!\n\t" /* Load from RS, decrement RSP */ \
|
||||
::: "x21", "x22", "x23", "memory" \
|
||||
)
|
||||
|
||||
/* R@ - copy top of return stack to data stack */
|
||||
#define PRIM_R_FETCH_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"str x23, [x21, #8]!\n\t" /* Store TOS, advance DSP */ \
|
||||
"ldr x23, [x22, #-8]\n\t" /* Load from RS (don't pop) */ \
|
||||
::: "x21", "x23", "memory" \
|
||||
)
|
||||
|
||||
/* 2DUP - duplicate top two items */
|
||||
#define PRIM_2DUP_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21]\n\t" /* Load NOS */ \
|
||||
"stp x0, x23, [x21, #8]\n\t" /* Store pair */ \
|
||||
"add x21, x21, #16\n\t" /* DSP += 2 */ \
|
||||
::: "x0", "x21", "memory" \
|
||||
)
|
||||
|
||||
/* 2DROP - drop top two items */
|
||||
#define PRIM_2DROP_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x23, [x21, #-8]\n\t" /* Load new TOS */ \
|
||||
"sub x21, x21, #16\n\t" /* DSP -= 2 */ \
|
||||
::: "x21", "x23", "memory" \
|
||||
)
|
||||
|
||||
/* 0= - test if zero */
|
||||
#define PRIM_ZERO_EQUALS_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"cmp x23, #0\n\t" \
|
||||
"cset x23, eq\n\t" /* Set to 1 if zero, else 0 */ \
|
||||
"neg x23, x23\n\t" /* Convert to -1 (true flag) */ \
|
||||
::: "x23", "cc" \
|
||||
)
|
||||
|
||||
/* 0< - test if negative */
|
||||
#define PRIM_ZERO_LESS_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"cmp x23, #0\n\t" \
|
||||
"cset x23, lt\n\t" \
|
||||
"neg x23, x23\n\t" \
|
||||
::: "x23", "cc" \
|
||||
)
|
||||
|
||||
/* = - test equality */
|
||||
#define PRIM_EQUALS_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21], #-8\n\t" \
|
||||
"cmp x0, x23\n\t" \
|
||||
"cset x23, eq\n\t" \
|
||||
"neg x23, x23\n\t" \
|
||||
::: "x0", "x21", "x23", "cc" \
|
||||
)
|
||||
|
||||
/* < - test less than (signed) */
|
||||
#define PRIM_LESS_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21], #-8\n\t" \
|
||||
"cmp x0, x23\n\t" \
|
||||
"cset x23, lt\n\t" \
|
||||
"neg x23, x23\n\t" \
|
||||
::: "x0", "x21", "x23", "cc" \
|
||||
)
|
||||
|
||||
/* > - test greater than (signed) */
|
||||
#define PRIM_GREATER_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x21], #-8\n\t" \
|
||||
"cmp x0, x23\n\t" \
|
||||
"cset x23, gt\n\t" \
|
||||
"neg x23, x23\n\t" \
|
||||
::: "x0", "x21", "x23", "cc" \
|
||||
)
|
||||
|
||||
/*
|
||||
* Control flow primitives
|
||||
*/
|
||||
|
||||
/* BRANCH - unconditional branch */
|
||||
#define PRIM_BRANCH_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x20]\n\t" /* Load offset */ \
|
||||
"add x20, x20, x0\n\t" /* IP += offset */ \
|
||||
::: "x0", "x20", "memory" \
|
||||
)
|
||||
|
||||
/* 0BRANCH - branch if TOS is zero */
|
||||
#define PRIM_ZBRANCH_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x0, [x20]\n\t" /* Load offset */ \
|
||||
"cmp x23, #0\n\t" \
|
||||
"add x1, x20, x0\n\t" /* Calculate target */ \
|
||||
"add x2, x20, #8\n\t" /* Calculate skip */ \
|
||||
"csel x20, x1, x2, eq\n\t" /* Select target if zero */ \
|
||||
"ldr x23, [x21], #-8\n\t" /* Load new TOS */ \
|
||||
::: "x0", "x1", "x2", "x20", "x21", "x23", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* EXECUTE - execute word whose xt is on stack */
|
||||
#define PRIM_EXECUTE_ARM64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ldr x23, [x21], #-8\n\t" /* Load new TOS */ \
|
||||
"br x0\n\t" /* Branch to xt */ \
|
||||
::: "x0", "x21", "x23", "memory" \
|
||||
)
|
||||
|
||||
#else /* !USE_DIRECT_THREADING */
|
||||
|
||||
/* Fallback definitions */
|
||||
#define NEXT_ARM64() do {} while(0)
|
||||
#define vm_setup_registers_arm64(vm) do {} while(0)
|
||||
#define vm_save_registers_arm64(vm) do {} while(0)
|
||||
|
||||
#endif /* USE_DIRECT_THREADING */
|
||||
|
||||
/*
|
||||
* ============================================================================
|
||||
* RASPBERRY PI 4 PERFORMANCE TUNING
|
||||
* ============================================================================
|
||||
*
|
||||
* Cache Optimization:
|
||||
* -------------------
|
||||
* 1. Align VM structure to cache line (64 bytes):
|
||||
* typedef struct VM { ... } __attribute__((aligned(64))) VM;
|
||||
*
|
||||
* 2. Separate hot/cold data:
|
||||
* - Keep stacks, IP, SP together (hot)
|
||||
* - Move error handling, I/O to separate structure (cold)
|
||||
*
|
||||
* 3. Prefetch dictionary entries:
|
||||
* vm_prefetch(vm->latest);
|
||||
* vm_prefetch(vm->latest->link);
|
||||
*
|
||||
* Thermal Management:
|
||||
* -------------------
|
||||
* 1. Monitor temperature:
|
||||
* vcgencmd measure_temp
|
||||
*
|
||||
* 2. Add heatsink or fan for sustained load
|
||||
*
|
||||
* 3. Reduce CPU frequency if needed:
|
||||
* echo 1200000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
|
||||
*
|
||||
* Power Optimization:
|
||||
* -------------------
|
||||
* 1. Use WFE (Wait For Event) in idle loops
|
||||
* 2. Disable unused cores if single-threaded
|
||||
* 3. Use NEON for parallel operations (lower power than scalar)
|
||||
*
|
||||
* Build Configuration:
|
||||
* --------------------
|
||||
* Native: -march=armv8-a+crc+simd -mtune=cortex-a72
|
||||
* Cross: CC=aarch64-linux-gnu-gcc
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
#endif /* VM_INNER_INTERP_ARM64_H */
|
||||
@@ -0,0 +1,643 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
*** StarForth ***
|
||||
vm_inner_interp_asm.h - Optimized Inner Interpreter using Direct Threading
|
||||
Compatible with: Linux, bare metal x86_64
|
||||
|
||||
HISTORICAL: the "L4Re Compatibility"/"L4RE INTEGRATION NOTES" throughout
|
||||
this file describe a target that was supported through mid-2026. L4Re
|
||||
support has since been removed as an active target; these notes are
|
||||
retained as historical design rationale.
|
||||
|
||||
This work is released under license terms in LICENSE file.
|
||||
No warranty. Use at your own risk.
|
||||
*/
|
||||
|
||||
#ifndef VM_INNER_INTERP_ASM_H
|
||||
#define VM_INNER_INTERP_ASM_H
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
/*
|
||||
* INNER INTERPRETER OPTIMIZATION
|
||||
* ===============================
|
||||
*
|
||||
* This is the MOST CRITICAL optimization for Forth performance.
|
||||
*
|
||||
* Background:
|
||||
* -----------
|
||||
* Forth uses "threaded code" where compiled words are sequences of pointers
|
||||
* to other words. The inner interpreter's job is to:
|
||||
* 1. Fetch the next word pointer (IP)
|
||||
* 2. Execute that word
|
||||
* 3. Increment IP
|
||||
* 4. Repeat
|
||||
*
|
||||
* Traditional C implementation has massive overhead:
|
||||
* - Function call/return for each word (push/pop return address)
|
||||
* - Register spilling
|
||||
* - No tail-call optimization in most C compilers
|
||||
*
|
||||
* This optimization uses:
|
||||
* - Direct threading: Each word ends with a jump to NEXT
|
||||
* - Register allocation: Keep IP, SP, RP in registers
|
||||
* - Tail calls: Use jmp instead of call/ret
|
||||
* - Computed goto: Fast dispatch using indirect jump
|
||||
*
|
||||
* Performance Impact:
|
||||
* ------------------
|
||||
* Can improve interpreter speed by 2-5x for threaded code execution
|
||||
*
|
||||
* L4Re Compatibility: YES
|
||||
* All operations are unprivileged and pure computation
|
||||
*/
|
||||
|
||||
#ifndef USE_DIRECT_THREADING
|
||||
#define USE_DIRECT_THREADING 0
|
||||
#endif
|
||||
|
||||
#if USE_DIRECT_THREADING
|
||||
|
||||
/**
|
||||
* @brief Register allocation strategy for optimized execution
|
||||
*
|
||||
* Register usage:
|
||||
* @arg r12 VM pointer (callee-saved, preserved across calls)
|
||||
* @arg r13 Instruction Pointer (IP) - points to current cell
|
||||
* @arg r14 Data Stack Pointer (DSP) - points to vm->data_stack[vm->dsp]
|
||||
* @arg r15 Return Stack Pointer (RSP) - points to vm->return_stack[vm->rsp]
|
||||
*
|
||||
* Callee-saved registers are used because:
|
||||
* - They're preserved across function calls
|
||||
* - Allows mixing C and assembly code
|
||||
* - No need to save/restore on each word execution
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Core inner interpreter macro - executes next word
|
||||
* @details Fetches next word pointer from IP, increments IP, and jumps to word
|
||||
*/
|
||||
#define NEXT_ASM() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r13), %%rax\n\t" /* Load word pointer from [IP] */ \
|
||||
"addq $8, %%r13\n\t" /* IP++ */ \
|
||||
"jmpq *%%rax\n\t" /* Jump to word's code */ \
|
||||
::: "rax", "memory" \
|
||||
)
|
||||
|
||||
/**
|
||||
* @brief Enter a colon definition (DOCOL)
|
||||
* @param vm Pointer to VM instance
|
||||
* @details Called when executing a user-defined word
|
||||
*/
|
||||
static inline void vm_docol_asm(VM *vm) {
|
||||
__asm__ __volatile__(
|
||||
/* Save current IP on return stack */
|
||||
"movq %%r13, (%%r15)\n\t" /* *RSP = IP */
|
||||
"addq $8, %%r15\n\t" /* RSP++ */
|
||||
|
||||
/* IP = address of colon definition body (from DictEntry) */
|
||||
"movq %[entry], %%rax\n\t"
|
||||
"movq (%%rax), %%r13\n\t" /* IP = entry->code_field */
|
||||
|
||||
/* Continue with NEXT */
|
||||
"movq (%%r13), %%rax\n\t"
|
||||
"addq $8, %%r13\n\t"
|
||||
"jmpq *%%rax\n\t"
|
||||
:
|
||||
: [entry]"r"(vm->current_executing_entry)
|
||||
: "rax", "r13", "r15", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return from a colon definition (EXIT)
|
||||
* @details Pops IP from return stack and continues execution
|
||||
*/
|
||||
static inline void vm_exit_asm(void) {
|
||||
__asm__ __volatile__(
|
||||
/* Restore IP from return stack */
|
||||
"subq $8, %%r15\n\t" /* RSP-- */
|
||||
"movq (%%r15), %%r13\n\t" /* IP = *RSP */
|
||||
|
||||
/* Continue with NEXT */
|
||||
"movq (%%r13), %%rax\n\t"
|
||||
"addq $8, %%r13\n\t"
|
||||
"jmpq *%%rax\n\t"
|
||||
::: "rax", "r13", "r15", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main interpreter loop implementation
|
||||
* @details Replaces execute_colon_word function with optimized assembly
|
||||
*
|
||||
* Usage example:
|
||||
* @code
|
||||
* vm_setup_registers(vm);
|
||||
* VM_INNER_LOOP();
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
/* Setup registers before entering inner loop */
|
||||
static inline void vm_setup_registers(VM *vm) {
|
||||
__asm__ __volatile__(
|
||||
"movq %[vm], %%r12\n\t" /* R12 = VM pointer */
|
||||
"movq %[ip], %%r13\n\t" /* R13 = IP */
|
||||
|
||||
/* Calculate actual stack pointers */
|
||||
"movl %[dsp], %%eax\n\t"
|
||||
"leaq (%[dstack], %%rax, 8), %%r14\n\t" /* R14 = &data_stack[dsp] */
|
||||
|
||||
"movl %[rsp], %%eax\n\t"
|
||||
"leaq (%[rstack], %%rax, 8), %%r15\n\t" /* R15 = &return_stack[rsp] */
|
||||
:
|
||||
: [vm]"r"(vm),
|
||||
[ip]"r"(vm->ip),
|
||||
[dsp]"m"(vm->dsp),
|
||||
[rsp]"m"(vm->rsp),
|
||||
[dstack]"r"(vm->data_stack),
|
||||
[rstack]"r"(vm->return_stack)
|
||||
: "r12", "r13", "r14", "r15", "rax", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/* Save registers back to VM structure */
|
||||
static inline void vm_save_registers(VM *vm) {
|
||||
__asm__ __volatile__(
|
||||
"movq %%r13, %[ip]\n\t" /* Save IP */
|
||||
|
||||
/* Calculate dsp from R14 */
|
||||
"movq %%r14, %%rax\n\t"
|
||||
"subq %[dstack], %%rax\n\t"
|
||||
"shrq $3, %%rax\n\t" /* / 8 */
|
||||
"movl %%eax, %[dsp]\n\t"
|
||||
|
||||
/* Calculate rsp from R15 */
|
||||
"movq %%r15, %%rax\n\t"
|
||||
"subq %[rstack], %%rax\n\t"
|
||||
"shrq $3, %%rax\n\t"
|
||||
"movl %%eax, %[rsp]\n\t"
|
||||
: [ip]"=m"(vm->ip),
|
||||
[dsp]"=m"(vm->dsp),
|
||||
[rsp]"=m"(vm->rsp)
|
||||
: [dstack]"r"(vm->data_stack),
|
||||
[rstack]"r"(vm->return_stack)
|
||||
: "rax", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fast primitive word implementations using register-based stacks
|
||||
*
|
||||
* These use the register-allocated stack pointers (R14/R15) directly
|
||||
* without going through memory or function calls
|
||||
*/
|
||||
|
||||
/* DUP - duplicate top of stack */
|
||||
#define PRIM_DUP() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"addq $8, %%r14\n\t" /* DSP++ */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store duplicate */ \
|
||||
::: "rax", "r14", "memory" \
|
||||
)
|
||||
|
||||
/* DROP - remove top of stack */
|
||||
#define PRIM_DROP() \
|
||||
__asm__ __volatile__ ( \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "r14" \
|
||||
)
|
||||
|
||||
/* SWAP - swap top two stack items */
|
||||
#define PRIM_SWAP() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"movq -8(%%r14), %%rcx\n\t" /* Load NOS */ \
|
||||
"movq %%rcx, (%%r14)\n\t" /* Store NOS to TOS */ \
|
||||
"movq %%rax, -8(%%r14)\n\t" /* Store TOS to NOS */ \
|
||||
::: "rax", "rcx", "memory" \
|
||||
)
|
||||
|
||||
/* + - add top two stack items */
|
||||
#define PRIM_PLUS() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"addq %%rax, -8(%%r14)\n\t" /* Add to NOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* - - subtract top from second */
|
||||
#define PRIM_MINUS() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"subq %%rax, -8(%%r14)\n\t" /* Subtract from NOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* * - multiply top two stack items */
|
||||
#define PRIM_STAR() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"imulq -8(%%r14), %%rax\n\t" /* Multiply with NOS */ \
|
||||
"movq %%rax, -8(%%r14)\n\t" /* Store result */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "rdx", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* @ - fetch cell from memory */
|
||||
#define PRIM_FETCH() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load address from TOS */ \
|
||||
"movq (%%r12), %%rcx\n\t" /* Load VM->memory */ \
|
||||
"movq (%%rcx, %%rax, 1), %%rax\n\t" /* Load cell from memory */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store to TOS */ \
|
||||
::: "rax", "rcx", "memory" \
|
||||
)
|
||||
|
||||
/* ! - store cell to memory */
|
||||
#define PRIM_STORE() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load address from TOS */ \
|
||||
"movq -8(%%r14), %%rcx\n\t" /* Load value from NOS */ \
|
||||
"movq (%%r12), %%rdx\n\t" /* Load VM->memory */ \
|
||||
"movq %%rcx, (%%rdx, %%rax, 1)\n\t" /* Store to memory */ \
|
||||
"subq $16, %%r14\n\t" /* DSP -= 2 */ \
|
||||
::: "rax", "rcx", "rdx", "r14", "memory" \
|
||||
)
|
||||
|
||||
/* >R - push to return stack */
|
||||
#define PRIM_TO_R() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load from TOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
"movq %%rax, (%%r15)\n\t" /* Store to return stack */ \
|
||||
"addq $8, %%r15\n\t" /* RSP++ */ \
|
||||
::: "rax", "r14", "r15", "memory" \
|
||||
)
|
||||
|
||||
/* R> - pop from return stack */
|
||||
#define PRIM_R_FROM() \
|
||||
__asm__ __volatile__ ( \
|
||||
"subq $8, %%r15\n\t" /* RSP-- */ \
|
||||
"movq (%%r15), %%rax\n\t" /* Load from return stack */ \
|
||||
"addq $8, %%r14\n\t" /* DSP++ */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store to TOS */ \
|
||||
::: "rax", "r14", "r15", "memory" \
|
||||
)
|
||||
|
||||
/* R@ - copy top of return stack to data stack */
|
||||
#define PRIM_R_FETCH() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq -8(%%r15), %%rax\n\t" /* Load from top of return stack */ \
|
||||
"addq $8, %%r14\n\t" /* DSP++ */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store to TOS */ \
|
||||
::: "rax", "r14", "memory" \
|
||||
)
|
||||
|
||||
/*
|
||||
* BRANCH and 0BRANCH - Control flow primitives
|
||||
* These modify IP based on conditions
|
||||
*/
|
||||
|
||||
/* BRANCH - unconditional branch */
|
||||
#define PRIM_BRANCH() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r13), %%rax\n\t" /* Load offset from IP */ \
|
||||
"addq %%rax, %%r13\n\t" /* IP += offset */ \
|
||||
::: "rax", "r13", "memory" \
|
||||
)
|
||||
|
||||
/* 0BRANCH - branch if TOS is zero */
|
||||
#define PRIM_ZBRANCH() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
"testq %%rax, %%rax\n\t" /* Test if zero */ \
|
||||
"jnz 1f\n\t" /* Skip if non-zero */ \
|
||||
"movq (%%r13), %%rax\n\t" /* Load offset */ \
|
||||
"addq %%rax, %%r13\n\t" /* IP += offset */ \
|
||||
"jmp 2f\n\t" \
|
||||
"1:\n\t" \
|
||||
"addq $8, %%r13\n\t" /* Skip offset */ \
|
||||
"2:\n\t" \
|
||||
::: "rax", "r13", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* OVER - copy second item to top of stack */
|
||||
#define PRIM_OVER() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq -8(%%r14), %%rax\n\t" /* Load NOS */ \
|
||||
"addq $8, %%r14\n\t" /* DSP++ */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store NOS as new TOS */ \
|
||||
::: "rax", "r14", "memory" \
|
||||
)
|
||||
|
||||
/* ROT - rotate top three items ( a b c -- b c a ) */
|
||||
#define PRIM_ROT() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* c = TOS */ \
|
||||
"movq -8(%%r14), %%rcx\n\t" /* b = NOS */ \
|
||||
"movq -16(%%r14), %%rdx\n\t" /* a = third */ \
|
||||
"movq %%rdx, -8(%%r14)\n\t" /* a -> NOS */ \
|
||||
"movq %%rax, -16(%%r14)\n\t" /* c -> third */ \
|
||||
"movq %%rcx, (%%r14)\n\t" /* b -> TOS */ \
|
||||
::: "rax", "rcx", "rdx", "memory" \
|
||||
)
|
||||
|
||||
/* / - signed divide second by top */
|
||||
#define PRIM_SLASH() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq -8(%%r14), %%rax\n\t" /* Load dividend (NOS) */ \
|
||||
"cqo\n\t" /* Sign-extend rax to rdx:rax */ \
|
||||
"idivq (%%r14)\n\t" /* Divide by TOS */ \
|
||||
"movq %%rax, -8(%%r14)\n\t" /* Store quotient to NOS slot */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "rdx", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* MOD - signed modulo */
|
||||
#define PRIM_MOD() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq -8(%%r14), %%rax\n\t" /* Load dividend (NOS) */ \
|
||||
"cqo\n\t" /* Sign-extend */ \
|
||||
"idivq (%%r14)\n\t" /* rdx = remainder */ \
|
||||
"movq %%rdx, -8(%%r14)\n\t" /* Store remainder to NOS slot */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "rdx", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* AND - bitwise and */
|
||||
#define PRIM_AND() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"andq %%rax, -8(%%r14)\n\t" /* NOS &= TOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* OR - bitwise or */
|
||||
#define PRIM_OR() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"orq %%rax, -8(%%r14)\n\t" /* NOS |= TOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* XOR - bitwise xor */
|
||||
#define PRIM_XOR() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"xorq %%rax, -8(%%r14)\n\t" /* NOS ^= TOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* INVERT - bitwise complement */
|
||||
#define PRIM_INVERT() \
|
||||
__asm__ __volatile__ ( \
|
||||
"notq (%%r14)\n\t" /* TOS = ~TOS */ \
|
||||
::: "memory" \
|
||||
)
|
||||
|
||||
/* NEGATE - two's complement negation */
|
||||
#define PRIM_NEGATE() \
|
||||
__asm__ __volatile__ ( \
|
||||
"negq (%%r14)\n\t" /* TOS = -TOS */ \
|
||||
::: "cc", "memory" \
|
||||
)
|
||||
|
||||
/* C@ - fetch byte from memory */
|
||||
#define PRIM_C_FETCH() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load address from TOS */ \
|
||||
"movq (%%r12), %%rcx\n\t" /* Load VM->memory */ \
|
||||
"movzbq (%%rcx, %%rax, 1), %%rax\n\t" /* Load byte, zero-extend */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store byte value to TOS */ \
|
||||
::: "rax", "rcx", "memory" \
|
||||
)
|
||||
|
||||
/* C! - store byte to memory */
|
||||
#define PRIM_C_STORE() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load address from TOS */ \
|
||||
"movq -8(%%r14), %%rcx\n\t" /* Load byte value from NOS */ \
|
||||
"movq (%%r12), %%rdx\n\t" /* Load VM->memory */ \
|
||||
"movb %%cl, (%%rdx, %%rax, 1)\n\t" /* Store byte */ \
|
||||
"subq $16, %%r14\n\t" /* DSP -= 2 */ \
|
||||
::: "rax", "rcx", "rdx", "r14", "memory" \
|
||||
)
|
||||
|
||||
/* 2DUP - duplicate top two items ( a b -- a b a b ) */
|
||||
#define PRIM_2DUP() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq -8(%%r14), %%rax\n\t" /* Load NOS (a) */ \
|
||||
"movq (%%r14), %%rcx\n\t" /* Load TOS (b) */ \
|
||||
"movq %%rax, 8(%%r14)\n\t" /* Store a above TOS */ \
|
||||
"movq %%rcx, 16(%%r14)\n\t" /* Store b above that */ \
|
||||
"addq $16, %%r14\n\t" /* DSP += 2 */ \
|
||||
::: "rax", "rcx", "r14", "memory" \
|
||||
)
|
||||
|
||||
/* 2DROP - drop top two items */
|
||||
#define PRIM_2DROP() \
|
||||
__asm__ __volatile__ ( \
|
||||
"subq $16, %%r14\n\t" /* DSP -= 2 */ \
|
||||
::: "r14" \
|
||||
)
|
||||
|
||||
/* 0= - true (-1) if TOS is zero, false (0) otherwise */
|
||||
#define PRIM_ZERO_EQUALS() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"testq %%rax, %%rax\n\t" /* Test for zero */ \
|
||||
"sete %%al\n\t" /* AL = 1 if zero */ \
|
||||
"movzbq %%al, %%rax\n\t" /* Zero-extend */ \
|
||||
"negq %%rax\n\t" /* -1 if true, 0 if false */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store result */ \
|
||||
::: "rax", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* 0< - true (-1) if TOS is negative, false (0) otherwise */
|
||||
#define PRIM_ZERO_LESS() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS */ \
|
||||
"sarq $63, %%rax\n\t" /* Arithmetic shift: -1 if negative, 0 otherwise */ \
|
||||
"movq %%rax, (%%r14)\n\t" /* Store result */ \
|
||||
::: "rax", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* = - true (-1) if top two items are equal, false (0) otherwise */
|
||||
#define PRIM_EQUALS() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load TOS (b) */ \
|
||||
"cmpq %%rax, -8(%%r14)\n\t" /* Compare NOS (a) with TOS (b) */ \
|
||||
"sete %%al\n\t" /* AL = 1 if equal */ \
|
||||
"movzbq %%al, %%rax\n\t" /* Zero-extend */ \
|
||||
"negq %%rax\n\t" /* -1 if true, 0 if false */ \
|
||||
"movq %%rax, -8(%%r14)\n\t" /* Store result to NOS slot */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* < - true (-1) if NOS < TOS (signed), false (0) otherwise */
|
||||
#define PRIM_LESS() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rcx\n\t" /* Load TOS (b) */ \
|
||||
"movq -8(%%r14), %%rax\n\t" /* Load NOS (a) */ \
|
||||
"cmpq %%rcx, %%rax\n\t" /* rax - rcx: signed compare a vs b */ \
|
||||
"setl %%al\n\t" /* AL = 1 if a < b */ \
|
||||
"movzbq %%al, %%rax\n\t" /* Zero-extend */ \
|
||||
"negq %%rax\n\t" /* -1 if true, 0 if false */ \
|
||||
"movq %%rax, -8(%%r14)\n\t" /* Store result to NOS slot */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "rcx", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* > - true (-1) if NOS > TOS (signed), false (0) otherwise */
|
||||
#define PRIM_GREATER() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rcx\n\t" /* Load TOS (b) */ \
|
||||
"movq -8(%%r14), %%rax\n\t" /* Load NOS (a) */ \
|
||||
"cmpq %%rcx, %%rax\n\t" /* rax - rcx: signed compare a vs b */ \
|
||||
"setg %%al\n\t" /* AL = 1 if a > b */ \
|
||||
"movzbq %%al, %%rax\n\t" /* Zero-extend */ \
|
||||
"negq %%rax\n\t" /* -1 if true, 0 if false */ \
|
||||
"movq %%rax, -8(%%r14)\n\t" /* Store result to NOS slot */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
::: "rax", "rcx", "r14", "cc", "memory" \
|
||||
)
|
||||
|
||||
/* EXECUTE - execute word whose execution token is on stack */
|
||||
#define PRIM_EXECUTE() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movq (%%r14), %%rax\n\t" /* Load xt from TOS */ \
|
||||
"subq $8, %%r14\n\t" /* DSP-- */ \
|
||||
"jmpq *%%rax\n\t" /* Jump to execution token */ \
|
||||
::: "rax", "r14", "memory" \
|
||||
)
|
||||
|
||||
#else /* !USE_DIRECT_THREADING */
|
||||
|
||||
/* Fallback definitions when direct threading is disabled */
|
||||
#define NEXT_ASM() do {} while(0)
|
||||
#define vm_setup_registers(vm) do {} while(0)
|
||||
#define vm_save_registers(vm) do {} while(0)
|
||||
|
||||
#endif /* USE_DIRECT_THREADING */
|
||||
|
||||
/*
|
||||
* ============================================================================
|
||||
* INTEGRATION GUIDE
|
||||
* ============================================================================
|
||||
*
|
||||
* To integrate these optimizations:
|
||||
*
|
||||
* 1. Enable in your build:
|
||||
* -DUSE_DIRECT_THREADING=1 -DUSE_ASM_OPT=1
|
||||
*
|
||||
* 2. Modify your inner interpreter in vm.c:
|
||||
*
|
||||
* void execute_threaded_code(VM *vm) {
|
||||
* vm_setup_registers(vm);
|
||||
*
|
||||
* // Execute words...
|
||||
* // Each primitive word should use PRIM_* macros
|
||||
* // and end with NEXT_ASM()
|
||||
*
|
||||
* vm_save_registers(vm);
|
||||
* }
|
||||
*
|
||||
* 3. For each primitive word, replace function with macro:
|
||||
*
|
||||
* Instead of:
|
||||
* void word_dup(VM *vm) { vm_push(vm, vm->data_stack[vm->dsp]); }
|
||||
*
|
||||
* Use:
|
||||
* PRIM_DUP(); NEXT_ASM();
|
||||
*
|
||||
* 4. Benchmark before and after!
|
||||
*
|
||||
* ============================================================================
|
||||
* PERFORMANCE NOTES
|
||||
* ============================================================================
|
||||
*
|
||||
* Expected speedup: 2-5x for threaded code execution
|
||||
*
|
||||
* Why so fast?
|
||||
* - No function call overhead (10+ cycles saved per word)
|
||||
* - Register allocation eliminates memory traffic
|
||||
* - Direct jumps instead of call/return (better branch prediction)
|
||||
* - Cache-friendly: hot path stays in I-cache
|
||||
*
|
||||
* Trade-offs:
|
||||
* - More complex code
|
||||
* - Harder to debug (use regular C version for debugging)
|
||||
* - Platform-specific (x86_64 only)
|
||||
* - Requires more careful register management
|
||||
*
|
||||
* ============================================================================
|
||||
* L4RE INTEGRATION NOTES
|
||||
* ============================================================================
|
||||
*
|
||||
* All safe for L4Re because:
|
||||
* - No privileged instructions
|
||||
* - No system calls
|
||||
* - Preserved ABI (callee-saved registers)
|
||||
* - Can be used in both user and kernel contexts
|
||||
*
|
||||
* For StarshipOS:
|
||||
* - These optimizations work in L4Re tasks
|
||||
* - Safe for real-time contexts (deterministic timing)
|
||||
* - No FPU usage (kernel-safe)
|
||||
* - Stack usage is minimal
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
#endif /* VM_INNER_INTERP_ASM_H */
|
||||
@@ -0,0 +1,539 @@
|
||||
/*
|
||||
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_INNER_INTERP_RISCV64_H
|
||||
#define VM_INNER_INTERP_RISCV64_H
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
/*
|
||||
* RISC-V 64 DIRECT-THREADED INNER INTERPRETER
|
||||
* ============================================
|
||||
*
|
||||
* Register Allocation (callee-saved s2–s6):
|
||||
* - s2 (x18): VM pointer
|
||||
* - s3 (x19): Instruction Pointer (IP)
|
||||
* - s4 (x20): Data Stack Pointer (DSP) — pointer to TOS cell
|
||||
* - s5 (x21): Return Stack Pointer (RSP) — pointer to top return entry
|
||||
* - s6 (x22): Top-Of-Stack cache (TOS) — avoids memory round-trip for TOS
|
||||
*
|
||||
* Using callee-saved registers means the compiler will save/restore them
|
||||
* around any C calls made while the inner interpreter is running.
|
||||
*
|
||||
* Stack convention (matches x86_64 and ARM64 implementations):
|
||||
* - Stack grows upward; DSP points to the current TOS cell.
|
||||
* - TOS is additionally cached in s6 to avoid one load per operation.
|
||||
* - RSP points to the top return-address cell (not past it).
|
||||
*
|
||||
* NEXT macro (direct-threaded dispatch):
|
||||
* ld a0, 0(s3) # a0 = *IP
|
||||
* addi s3, s3, 8 # IP += 8
|
||||
* jr a0 # jump to code field (jalr x0, a0, 0)
|
||||
*
|
||||
* Expected performance: 1.5–2.5x vs interpreter loop (fewer branch
|
||||
* mispredictions, tighter dispatch path). Less than ARM64 TOS-cache gain
|
||||
* because RISC-V lacks post-increment addressing for combined load+advance.
|
||||
*/
|
||||
|
||||
#ifndef USE_DIRECT_THREADING
|
||||
#define USE_DIRECT_THREADING 0
|
||||
#endif
|
||||
|
||||
#if USE_DIRECT_THREADING
|
||||
|
||||
/*
|
||||
* NEXT — fetch and dispatch the next word
|
||||
*
|
||||
* 'jr a0' is a pseudoinstruction for 'jalr x0, a0, 0' which discards
|
||||
* the return address (x0 is hardwired zero).
|
||||
*/
|
||||
#define NEXT_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s3)\n\t" /* a0 = *IP */ \
|
||||
"addi s3, s3, 8\n\t" /* IP++ */ \
|
||||
"jr a0\n\t" /* jump to code field */ \
|
||||
::: "a0", "memory" \
|
||||
)
|
||||
|
||||
/*
|
||||
* DOCOL — enter a colon definition
|
||||
*/
|
||||
static inline void vm_docol_rv64(VM *vm) {
|
||||
__asm__ __volatile__(
|
||||
/* Push IP onto return stack */
|
||||
"addi s5, s5, 8\n\t"
|
||||
"sd s3, 0(s5)\n\t"
|
||||
|
||||
/* Load new IP from current_executing_entry->code_field */
|
||||
"ld a0, %[entry]\n\t"
|
||||
"ld s3, 0(a0)\n\t"
|
||||
|
||||
/* NEXT */
|
||||
"ld a0, 0(s3)\n\t"
|
||||
"addi s3, s3, 8\n\t"
|
||||
"jr a0\n\t"
|
||||
:
|
||||
: [entry]"m"(vm->current_executing_entry)
|
||||
: "a0", "s3", "s5", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* EXIT — return from a colon definition
|
||||
*/
|
||||
static inline void vm_exit_rv64(void) {
|
||||
__asm__ __volatile__(
|
||||
/* Pop IP from return stack */
|
||||
"ld s3, 0(s5)\n\t"
|
||||
"addi s5, s5, -8\n\t"
|
||||
|
||||
/* NEXT */
|
||||
"ld a0, 0(s3)\n\t"
|
||||
"addi s3, s3, 8\n\t"
|
||||
"jr a0\n\t"
|
||||
::: "a0", "s3", "s5", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup registers before entering the inner interpreter loop.
|
||||
* Saves all callee-saved registers we're about to clobber, then
|
||||
* loads VM state into s2–s6.
|
||||
*/
|
||||
static inline void vm_setup_registers_rv64(VM *vm) {
|
||||
__asm__ __volatile__(
|
||||
/* Save callee-saved regs we're about to use */
|
||||
"addi sp, sp, -80\n\t"
|
||||
"sd s2, 0(sp)\n\t"
|
||||
"sd s3, 8(sp)\n\t"
|
||||
"sd s4, 16(sp)\n\t"
|
||||
"sd s5, 24(sp)\n\t"
|
||||
"sd s6, 32(sp)\n\t"
|
||||
"sd s7, 40(sp)\n\t"
|
||||
"sd s8, 48(sp)\n\t"
|
||||
"sd s9, 56(sp)\n\t"
|
||||
"sd s10,64(sp)\n\t"
|
||||
"sd s11,72(sp)\n\t"
|
||||
|
||||
/* s2 = VM pointer */
|
||||
"mv s2, %[vm]\n\t"
|
||||
|
||||
/* s3 = IP */
|
||||
"ld s3, %[ip]\n\t"
|
||||
|
||||
/* s4 = &data_stack[dsp] */
|
||||
"lw a0, %[dsp]\n\t"
|
||||
"ld a1, %[dstack]\n\t"
|
||||
"slli a0, a0, 3\n\t"
|
||||
"add s4, a1, a0\n\t"
|
||||
|
||||
/* s5 = &return_stack[rsp] */
|
||||
"lw a0, %[rsp]\n\t"
|
||||
"ld a1, %[rstack]\n\t"
|
||||
"slli a0, a0, 3\n\t"
|
||||
"add s5, a1, a0\n\t"
|
||||
|
||||
/* s6 = TOS (load if stack non-empty) */
|
||||
"lw a0, %[dsp]\n\t"
|
||||
"bltz a0, 1f\n\t"
|
||||
"ld s6, 0(s4)\n\t"
|
||||
"1:\n\t"
|
||||
:
|
||||
: [vm]"r"(vm),
|
||||
[ip]"m"(vm->ip),
|
||||
[dsp]"m"(vm->dsp),
|
||||
[rsp]"m"(vm->rsp),
|
||||
[dstack]"m"(vm->data_stack),
|
||||
[rstack]"m"(vm->return_stack)
|
||||
: "a0", "a1",
|
||||
"s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11",
|
||||
"memory"
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Save registers back to VM structure and restore callee-saved regs.
|
||||
*/
|
||||
static inline void vm_save_registers_rv64(VM *vm) {
|
||||
__asm__ __volatile__(
|
||||
/* Flush TOS back to stack */
|
||||
"sd s6, 0(s4)\n\t"
|
||||
|
||||
/* Save IP */
|
||||
"sd s3, %[ip]\n\t"
|
||||
|
||||
/* dsp = (s4 - data_stack_base) / 8 */
|
||||
"ld a0, %[dstack]\n\t"
|
||||
"sub a1, s4, a0\n\t"
|
||||
"srli a1, a1, 3\n\t"
|
||||
"sw a1, %[dsp]\n\t"
|
||||
|
||||
/* rsp = (s5 - return_stack_base) / 8 */
|
||||
"ld a0, %[rstack]\n\t"
|
||||
"sub a1, s5, a0\n\t"
|
||||
"srli a1, a1, 3\n\t"
|
||||
"sw a1, %[rsp]\n\t"
|
||||
|
||||
/* Restore callee-saved regs */
|
||||
"ld s2, 0(sp)\n\t"
|
||||
"ld s3, 8(sp)\n\t"
|
||||
"ld s4, 16(sp)\n\t"
|
||||
"ld s5, 24(sp)\n\t"
|
||||
"ld s6, 32(sp)\n\t"
|
||||
"ld s7, 40(sp)\n\t"
|
||||
"ld s8, 48(sp)\n\t"
|
||||
"ld s9, 56(sp)\n\t"
|
||||
"ld s10,64(sp)\n\t"
|
||||
"ld s11,72(sp)\n\t"
|
||||
"addi sp, sp, 80\n\t"
|
||||
: [ip]"=m"(vm->ip),
|
||||
[dsp]"=m"(vm->dsp),
|
||||
[rsp]"=m"(vm->rsp)
|
||||
: [dstack]"m"(vm->data_stack),
|
||||
[rstack]"m"(vm->return_stack)
|
||||
: "a0", "a1", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* Primitive Word Macros
|
||||
*
|
||||
* TOS is cached in s6. s4 = DSP (pointer to TOS cell). s5 = RSP.
|
||||
* s2 = VM pointer (used for memory-relative ops).
|
||||
* a0/a1 are scratch (caller-saved, safe to clobber).
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/* DUP — duplicate TOS */
|
||||
#define PRIM_DUP_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"addi s4, s4, 8\n\t" /* DSP++ */ \
|
||||
"sd s6, 0(s4)\n\t" /* push TOS copy */ \
|
||||
::: "s4", "memory" \
|
||||
)
|
||||
|
||||
/* DROP — discard TOS */
|
||||
#define PRIM_DROP_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld s6, -8(s4)\n\t" /* new TOS = NOS */ \
|
||||
"addi s4, s4, -8\n\t" /* DSP-- */ \
|
||||
::: "s4", "s6", "memory" \
|
||||
)
|
||||
|
||||
/* SWAP — swap TOS and NOS */
|
||||
#define PRIM_SWAP_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" /* a0 = NOS */ \
|
||||
"sd s6, 0(s4)\n\t" /* NOS = old TOS */ \
|
||||
"mv s6, a0\n\t" /* TOS = old NOS */ \
|
||||
::: "a0", "s6", "memory" \
|
||||
)
|
||||
|
||||
/* OVER — copy NOS to TOS */
|
||||
#define PRIM_OVER_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" /* a0 = NOS */ \
|
||||
"addi s4, s4, 8\n\t" /* DSP++ */ \
|
||||
"sd s6, 0(s4)\n\t" /* push old TOS */ \
|
||||
"mv s6, a0\n\t" /* TOS = NOS */ \
|
||||
::: "a0", "s4", "s6", "memory" \
|
||||
)
|
||||
|
||||
/* ROT — rotate top three: ( a b c — b c a ) */
|
||||
#define PRIM_ROT_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" /* a0 = item2 (NOS) */ \
|
||||
"ld a1, -8(s4)\n\t" /* a1 = item3 */ \
|
||||
"sd a1, 0(s4)\n\t" /* item3 -> NOS position */ \
|
||||
"sd s6, -8(s4)\n\t" /* TOS -> item3 position */ \
|
||||
"mv s6, a0\n\t" /* item2 -> TOS */ \
|
||||
::: "a0", "a1", "s6", "memory" \
|
||||
)
|
||||
|
||||
/* + */
|
||||
#define PRIM_PLUS_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
"add s6, s6, a0\n\t" \
|
||||
::: "a0", "s4", "s6" \
|
||||
)
|
||||
|
||||
/* - */
|
||||
#define PRIM_MINUS_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" /* a0 = NOS */ \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
"sub s6, a0, s6\n\t" /* TOS = NOS - TOS */ \
|
||||
::: "a0", "s4", "s6" \
|
||||
)
|
||||
|
||||
/* * */
|
||||
#define PRIM_STAR_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
"mul s6, s6, a0\n\t" \
|
||||
::: "a0", "s4", "s6" \
|
||||
)
|
||||
|
||||
/* / — signed divide NOS/TOS */
|
||||
#define PRIM_SLASH_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" /* a0 = NOS (dividend) */ \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
"div s6, a0, s6\n\t" /* TOS = NOS / TOS */ \
|
||||
::: "a0", "s4", "s6" \
|
||||
)
|
||||
|
||||
/* MOD */
|
||||
#define PRIM_MOD_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
"rem s6, a0, s6\n\t" \
|
||||
::: "a0", "s4", "s6" \
|
||||
)
|
||||
|
||||
/* AND */
|
||||
#define PRIM_AND_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
"and s6, s6, a0\n\t" \
|
||||
::: "a0", "s4", "s6" \
|
||||
)
|
||||
|
||||
/* OR */
|
||||
#define PRIM_OR_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
"or s6, s6, a0\n\t" \
|
||||
::: "a0", "s4", "s6" \
|
||||
)
|
||||
|
||||
/* XOR */
|
||||
#define PRIM_XOR_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
"xor s6, s6, a0\n\t" \
|
||||
::: "a0", "s4", "s6" \
|
||||
)
|
||||
|
||||
/* INVERT — bitwise NOT */
|
||||
#define PRIM_INVERT_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"not s6, s6\n\t" \
|
||||
::: "s6" \
|
||||
)
|
||||
|
||||
/* NEGATE — two's complement */
|
||||
#define PRIM_NEGATE_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"neg s6, s6\n\t" \
|
||||
::: "s6" \
|
||||
)
|
||||
|
||||
/* @ — fetch cell from VM memory */
|
||||
#define PRIM_FETCH_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s2)\n\t" /* a0 = vm->memory */ \
|
||||
"add a0, a0, s6\n\t" /* a0 = &memory[TOS] */ \
|
||||
"ld s6, 0(a0)\n\t" /* TOS = memory[TOS] */ \
|
||||
::: "a0", "s6", "memory" \
|
||||
)
|
||||
|
||||
/* ! — store cell to VM memory */
|
||||
#define PRIM_STORE_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" /* a0 = addr (NOS) */ \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
"ld a1, 0(s2)\n\t" /* a1 = vm->memory */ \
|
||||
"add a1, a1, a0\n\t" \
|
||||
"sd s6, 0(a1)\n\t" /* memory[addr] = TOS */ \
|
||||
"ld s6, 0(s4)\n\t" /* load new TOS */ \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
::: "a0", "a1", "s4", "s6", "memory" \
|
||||
)
|
||||
|
||||
/* C@ — fetch byte */
|
||||
#define PRIM_C_FETCH_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s2)\n\t" \
|
||||
"add a0, a0, s6\n\t" \
|
||||
"lbu s6, 0(a0)\n\t" /* zero-extend byte */ \
|
||||
::: "a0", "s6", "memory" \
|
||||
)
|
||||
|
||||
/* C! — store byte */
|
||||
#define PRIM_C_STORE_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" /* a0 = addr */ \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
"ld a1, 0(s2)\n\t" \
|
||||
"add a1, a1, a0\n\t" \
|
||||
"sb s6, 0(a1)\n\t" \
|
||||
"ld s6, 0(s4)\n\t" \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
::: "a0", "a1", "s4", "s6", "memory" \
|
||||
)
|
||||
|
||||
/* >R — push TOS to return stack */
|
||||
#define PRIM_TO_R_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"addi s5, s5, 8\n\t" \
|
||||
"sd s6, 0(s5)\n\t" /* RS[top] = TOS */ \
|
||||
"ld s6, 0(s4)\n\t" /* new TOS = NOS */ \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
::: "s4", "s5", "s6", "memory" \
|
||||
)
|
||||
|
||||
/* R> — pop return stack to data stack */
|
||||
#define PRIM_R_FROM_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"addi s4, s4, 8\n\t" \
|
||||
"sd s6, 0(s4)\n\t" /* push old TOS */ \
|
||||
"ld s6, 0(s5)\n\t" /* TOS = RS top */ \
|
||||
"addi s5, s5, -8\n\t" \
|
||||
::: "s4", "s5", "s6", "memory" \
|
||||
)
|
||||
|
||||
/* R@ — copy top of return stack (no pop) */
|
||||
#define PRIM_R_FETCH_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"addi s4, s4, 8\n\t" \
|
||||
"sd s6, 0(s4)\n\t" \
|
||||
"ld s6, 0(s5)\n\t" /* TOS = RS top (no pop) */ \
|
||||
::: "s4", "s6", "memory" \
|
||||
)
|
||||
|
||||
/* 2DUP — duplicate top two */
|
||||
#define PRIM_2DUP_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" /* a0 = NOS */ \
|
||||
"addi s4, s4, 8\n\t" \
|
||||
"sd a0, 0(s4)\n\t" /* push NOS */ \
|
||||
"addi s4, s4, 8\n\t" \
|
||||
"sd s6, 0(s4)\n\t" /* push TOS copy */ \
|
||||
::: "a0", "s4", "memory" \
|
||||
)
|
||||
|
||||
/* 2DROP — drop top two */
|
||||
#define PRIM_2DROP_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld s6, -8(s4)\n\t" /* new TOS = item3 */ \
|
||||
"addi s4, s4, -16\n\t" \
|
||||
::: "s4", "s6", "memory" \
|
||||
)
|
||||
|
||||
/* 0= */
|
||||
#define PRIM_ZERO_EQUALS_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"seqz s6, s6\n\t" /* 1 if zero, else 0 */ \
|
||||
"neg s6, s6\n\t" /* convert to -1 (true) */ \
|
||||
::: "s6" \
|
||||
)
|
||||
|
||||
/* 0< */
|
||||
#define PRIM_ZERO_LESS_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"srai s6, s6, 63\n\t" /* -1 if negative, 0 if >= 0 */ \
|
||||
::: "s6" \
|
||||
)
|
||||
|
||||
/* = */
|
||||
#define PRIM_EQUALS_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
"sub s6, a0, s6\n\t" \
|
||||
"seqz s6, s6\n\t" \
|
||||
"neg s6, s6\n\t" \
|
||||
::: "a0", "s4", "s6" \
|
||||
)
|
||||
|
||||
/* < — NOS < TOS */
|
||||
#define PRIM_LESS_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" /* a0 = NOS */ \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
"slt s6, a0, s6\n\t" /* 1 if NOS < TOS */ \
|
||||
"neg s6, s6\n\t" \
|
||||
::: "a0", "s4", "s6" \
|
||||
)
|
||||
|
||||
/* > — NOS > TOS */
|
||||
#define PRIM_GREATER_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s4)\n\t" \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
"slt s6, s6, a0\n\t" /* 1 if TOS < NOS, i.e. NOS > TOS */ \
|
||||
"neg s6, s6\n\t" \
|
||||
::: "a0", "s4", "s6" \
|
||||
)
|
||||
|
||||
/* BRANCH — unconditional */
|
||||
#define PRIM_BRANCH_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s3)\n\t" /* offset */ \
|
||||
"add s3, s3, a0\n\t" /* IP += offset */ \
|
||||
::: "a0", "s3", "memory" \
|
||||
)
|
||||
|
||||
/* 0BRANCH — branch if TOS == 0 */
|
||||
#define PRIM_ZBRANCH_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"ld a0, 0(s3)\n\t" /* offset */ \
|
||||
"ld s6, 0(s4)\n\t" /* new TOS */ \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
/* if old TOS was zero: IP += offset; else IP += 8 */ \
|
||||
"bnez s6, 1f\n\t" /* skip branch if nonzero */ \
|
||||
"add s3, s3, a0\n\t" \
|
||||
"j 2f\n\t" \
|
||||
"1:\n\t" \
|
||||
"addi s3, s3, 8\n\t" \
|
||||
"2:\n\t" \
|
||||
::: "a0", "s3", "s4", "s6", "memory" \
|
||||
)
|
||||
|
||||
/* EXECUTE — execute word whose xt is TOS */
|
||||
#define PRIM_EXECUTE_RV64() \
|
||||
__asm__ __volatile__ ( \
|
||||
"mv a0, s6\n\t" /* a0 = xt */ \
|
||||
"ld s6, 0(s4)\n\t" /* new TOS */ \
|
||||
"addi s4, s4, -8\n\t" \
|
||||
"jr a0\n\t" /* jump to xt */ \
|
||||
::: "a0", "s4", "s6", "memory" \
|
||||
)
|
||||
|
||||
#else /* !USE_DIRECT_THREADING */
|
||||
|
||||
#define NEXT_RV64() do {} while(0)
|
||||
#define vm_setup_registers_rv64(vm) do {} while(0)
|
||||
#define vm_save_registers_rv64(vm) do {} while(0)
|
||||
|
||||
#endif /* USE_DIRECT_THREADING */
|
||||
|
||||
#endif /* VM_INNER_INTERP_RISCV64_H */
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
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 WORD_REGISTRY_H
|
||||
#define WORD_REGISTRY_H
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
/**
|
||||
* @brief Registers a single FORTH word in the virtual machine
|
||||
* @param vm Pointer to the virtual machine instance
|
||||
* @param name Name of the FORTH word to register
|
||||
* @param func Function pointer to the word's implementation
|
||||
*/
|
||||
void register_word(VM *vm, const char *name, word_func_t func);
|
||||
|
||||
/*
|
||||
* @brief Registers all standard FORTH-79 words in the virtual machine
|
||||
* @param vm Pointer to the virtual machine instance
|
||||
*/
|
||||
void register_forth79_words(VM * vm);
|
||||
|
||||
#endif /* WORD_REGISTRY_H */
|
||||
Reference in New Issue
Block a user