401 lines
12 KiB
C
401 lines
12 KiB
C
/*
|
||
StarForth — Steady-State Virtual Machine Runtime
|
||
|
||
Copyright (c) 2023–2025 Robert A. James
|
||
All rights reserved.
|
||
|
||
This file is part of the StarForth project.
|
||
|
||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||
you may not use this file except in compliance with the License.
|
||
|
||
You may obtain a copy of the License at:
|
||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||
|
||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
express or implied, including but not limited to the warranties of
|
||
merchantability, fitness for a particular purpose, and noninfringement.
|
||
|
||
See the License for the specific language governing permissions and
|
||
limitations under the License.
|
||
|
||
StarForth — Steady-State Virtual Machine Runtime
|
||
Copyright (c) 2023–2025 Robert A. James
|
||
All rights reserved.
|
||
|
||
This file is part of the StarForth project.
|
||
|
||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||
you may not use this file except in compliance with the License.
|
||
|
||
You may obtain a copy of the License at:
|
||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||
|
||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
express or implied, including but not limited to the warranties of
|
||
merchantability, fitness for a particular purpose, and noninfringement.
|
||
|
||
See the License for the specific language governing permissions and
|
||
limitations under the License.
|
||
|
||
*/
|
||
|
||
/*
|
||
* vm_bootstrap.c - How a VM comes into existence
|
||
*
|
||
* Contains:
|
||
* - dictionary seeding
|
||
* - primitive registration
|
||
* - initial vocabularies
|
||
* - host vs kernel bootstrap branching
|
||
* - early init code paths
|
||
*
|
||
* Rule: If it runs before the VM executes a single word, it belongs here.
|
||
*/
|
||
|
||
#include "vm_internal.h"
|
||
#include "../include/log.h"
|
||
#include "../include/word_registry.h"
|
||
#include "../include/vm_debug.h"
|
||
#include "../include/physics_hotwords_cache.h"
|
||
#include "../include/rolling_window_of_truth.h"
|
||
#include "../include/dictionary_heat_optimization.h"
|
||
#include "../include/ssm_jacquard.h"
|
||
#include "../include/platform_alloc.h"
|
||
#include "word_source/include/vocabulary_words.h"
|
||
|
||
#ifdef __STARKERNEL__
|
||
#include "../include/starkernel/capsule_birth.h"
|
||
#include "../include/starkernel/capsule_run.h"
|
||
#endif
|
||
|
||
#include <string.h>
|
||
|
||
/* ====================== Bootstrap helpers ======================= */
|
||
|
||
/**
|
||
* @brief Ensure the SCR (Screen) system variable cell is allocated and zeroed.
|
||
*
|
||
* If @c vm->scr_addr does not point to a valid, aligned cell inside the VM
|
||
* memory array, aligns the allocation pointer and allots a new cell.
|
||
* Always writes 0 to the SCR cell on exit. Called at the end of @c vm_init()
|
||
* to guarantee a clean SCR state regardless of how the initial allot
|
||
* sequence progressed.
|
||
*
|
||
* @param vm Active VM; sets @c vm->error = 1 and logs if allot fails
|
||
*/
|
||
static void vm_bootstrap_scr(VM* vm)
|
||
{
|
||
if (!vm) return;
|
||
/* Ensure SCR cell exists and is zero */
|
||
if (!vm_addr_ok(vm, vm->scr_addr, sizeof(cell_t)) ||
|
||
(vm->scr_addr % sizeof(cell_t)) != 0)
|
||
{
|
||
vm_align(vm);
|
||
void* p = vm_allot(vm, sizeof(cell_t));
|
||
if (!p)
|
||
{
|
||
vm->error = 1;
|
||
log_message(LOG_ERROR, "bootstrap SCR allot failed");
|
||
return;
|
||
}
|
||
vm->scr_addr = (vaddr_t)((uint8_t*)p - vm->memory);
|
||
}
|
||
vm_store_cell(vm, vm->scr_addr, 0);
|
||
}
|
||
|
||
/**
|
||
* @brief Bootstrap a root vocabulary for the VM
|
||
*
|
||
* Creates a vocabulary by name, finds its dictionary entry, executes
|
||
* it to make it current, then calls DEFINITIONS.
|
||
*
|
||
* @param vm Pointer to VM structure
|
||
* @param name Vocabulary name to bootstrap
|
||
*/
|
||
void vm_bootstrap_root_vocabulary(VM *vm, const char *name)
|
||
{
|
||
if (!vm || !name) {
|
||
return;
|
||
}
|
||
vocabulary_create_vocabulary_direct(vm, name);
|
||
size_t len = strlen(name);
|
||
DictEntry *entry = vm_find_word(vm, name, len);
|
||
if (!entry || !entry->func) {
|
||
vm->error = 1;
|
||
log_message(LOG_ERROR, "bootstrap: missing vocabulary '%s'", name);
|
||
return;
|
||
}
|
||
entry->func(vm);
|
||
vocabulary_word_definitions(vm);
|
||
}
|
||
|
||
/* ====================== VM init / teardown ======================= */
|
||
|
||
/**
|
||
* @brief Initialize a new virtual machine instance
|
||
*
|
||
* Allocates memory and initializes all VM structures including:
|
||
* - Memory array
|
||
* - Data and return stacks
|
||
* - System variables (SCR, STATE, BASE)
|
||
* - Dictionary
|
||
* - Forth-79 wordset
|
||
*
|
||
* @param vm Pointer to VM structure to initialize
|
||
*/
|
||
void vm_init(VM* vm)
|
||
{
|
||
if (!vm) return;
|
||
memset(vm, 0, sizeof(*vm));
|
||
vm->next_word_id = 0;
|
||
vm->recycled_word_id_count = 0;
|
||
|
||
if (sf_mutex_init(&vm->dict_lock) != 0)
|
||
{
|
||
log_message(LOG_ERROR, "vm_init: dict_lock init failed");
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
|
||
if (sf_mutex_init(&vm->tuning_lock) != 0)
|
||
{
|
||
log_message(LOG_ERROR, "vm_init: tuning_lock init failed");
|
||
sf_mutex_destroy(&vm->dict_lock);
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
|
||
vm->memory = (uint8_t*)sf_malloc(VM_MEMORY_SIZE);
|
||
if (!vm->memory)
|
||
{
|
||
log_message(LOG_ERROR, "vm_init: out of memory");
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
|
||
vm->dsp = -1;
|
||
vm->rsp = -1;
|
||
vm->here = 0;
|
||
vm->exit_colon = 0;
|
||
vm->abort_requested = 0;
|
||
|
||
vm_align(vm);
|
||
|
||
/* SCR */
|
||
{
|
||
void* p = vm_allot(vm, sizeof(cell_t));
|
||
if (!p)
|
||
{
|
||
vm->error = 1;
|
||
log_message(LOG_ERROR, "vm_init: SCR allot failed");
|
||
return;
|
||
}
|
||
vm->scr_addr = (vaddr_t)((uint8_t*)p - vm->memory);
|
||
vm_store_cell(vm, vm->scr_addr, 0);
|
||
}
|
||
|
||
/* STATE (0=interpret, -1=compile) */
|
||
{
|
||
void* p = vm_allot(vm, sizeof(cell_t));
|
||
if (!p)
|
||
{
|
||
vm->error = 1;
|
||
log_message(LOG_ERROR, "vm_init: STATE allot failed");
|
||
return;
|
||
}
|
||
vm->state_addr = (vaddr_t)((uint8_t*)p - vm->memory);
|
||
vm_store_cell(vm, vm->state_addr, 0);
|
||
vm->state_var = 0;
|
||
}
|
||
|
||
/* BASE (default 10) */
|
||
{
|
||
void* p = vm_allot(vm, sizeof(cell_t));
|
||
if (!p)
|
||
{
|
||
vm->error = 1;
|
||
log_message(LOG_ERROR, "vm_init: BASE allot failed");
|
||
return;
|
||
}
|
||
vm->base_addr = (vaddr_t)((uint8_t*)p - vm->memory);
|
||
vm_set_base(vm, 10);
|
||
}
|
||
|
||
/* Pictured-number hold buffer (64 bytes, per-VM so child VMs are independent) */
|
||
{
|
||
void* p = vm_allot(vm, 64);
|
||
if (!p)
|
||
{
|
||
vm->error = 1;
|
||
log_message(LOG_ERROR, "vm_init: hold buffer allot failed");
|
||
return;
|
||
}
|
||
vm->hold_addr = (vaddr_t)((uint8_t*)p - vm->memory);
|
||
vm->hold_pos = 0;
|
||
memset(p, 0, 64);
|
||
}
|
||
|
||
vm_bootstrap_scr(vm);
|
||
|
||
vm->mode = MODE_INTERPRET;
|
||
vm->compiling_word = NULL;
|
||
vm->latest = NULL;
|
||
vm->error = 0;
|
||
vm->halted = 0;
|
||
|
||
vm->input_length = 0;
|
||
vm->input_pos = 0;
|
||
vm->current_executing_entry = NULL;
|
||
|
||
vm_debug_set_current_vm(vm);
|
||
vm_debug_install_signal_handlers();
|
||
|
||
/* Register Forth-79 wordset */
|
||
register_forth79_words(vm);
|
||
|
||
/* Set FORGET fence to post-boot */
|
||
vm->dict_fence_latest = vm->latest;
|
||
vm->dict_fence_here = vm->here;
|
||
|
||
/* Initialize hot-words cache (physics frequency-driven acceleration) */
|
||
vm->hotwords_cache = (HotwordsCache*)sf_malloc(sizeof(HotwordsCache));
|
||
if (!vm->hotwords_cache)
|
||
{
|
||
log_message(LOG_ERROR, "vm_init: hotwords cache alloc failed");
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
hotwords_cache_init(vm->hotwords_cache);
|
||
|
||
/* Initialize rolling window of truth (deterministic execution history) */
|
||
if (rolling_window_init(&vm->rolling_window) != 0)
|
||
{
|
||
log_message(LOG_ERROR, "vm_init: rolling window alloc failed");
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
|
||
/* Initialize VM heartbeat (centralized time-driven tuning) */
|
||
vm->heartbeat.tick_count = 0;
|
||
vm->heartbeat.last_inference_tick = 0;
|
||
vm->heartbeat.check_counter = 0;
|
||
vm->heartbeat.heartbeat_enabled = 1; /* Enabled by default */
|
||
vm->heartbeat.tick_target_ns = HEARTBEAT_TICK_NS;
|
||
vm->heartbeat.snapshot_index = 0;
|
||
vm->heartbeat.worker = NULL;
|
||
vm->heartbeat_decay_cursor_id = WORD_ID_INVALID;
|
||
|
||
/* Initialize pipelining global metrics (aggregated prefetch tracking) */
|
||
vm->pipeline_metrics.prefetch_attempts = 0;
|
||
vm->pipeline_metrics.prefetch_hits = 0;
|
||
vm->pipeline_metrics.window_tuning_checks = 0;
|
||
vm->pipeline_metrics.last_checked_window_size = vm->rolling_window.effective_window_size;
|
||
vm->pipeline_metrics.last_checked_accuracy = 0.0;
|
||
vm->pipeline_metrics.suggested_next_size = vm->rolling_window.effective_window_size;
|
||
|
||
vm_heartbeat_publish_snapshot(vm);
|
||
|
||
/* Start heartbeat thread (if enabled) */
|
||
vm_heartbeat_start_thread(vm);
|
||
|
||
/* Initialize adaptive heat decay tuning (Loop #3) */
|
||
/* Start with 2:1 ratio in Q48.16 format: 2.0 << 16 = 131072 */
|
||
vm->decay_slope_q48 = (1ULL << 16) / 3; /* 1/3 starting slope (Q48.16) */
|
||
vm->last_decay_check_ns = 0;
|
||
vm->total_heat_at_last_check = 0;
|
||
vm->stale_word_count_at_check = 0;
|
||
vm->decay_slope_direction = 0; /* Start neutral */
|
||
|
||
/* Phase 2: Initialize heat-aware dictionary optimization */
|
||
vm->lookup_strategy = 0; /* Start with naive lookup, will adapt based on patterns */
|
||
vm->last_bucket_reorg_ns = 0; /* Force first reorg quickly */
|
||
dict_update_heat_percentiles(vm); /* Calculate initial percentiles */
|
||
|
||
/* SSM L8: Jacquard Mode Selector initialization */
|
||
vm->ssm_l8_state = sf_malloc(sizeof(ssm_l8_state_t));
|
||
if (!vm->ssm_l8_state)
|
||
{
|
||
log_message(LOG_ERROR, "vm_init: SSM L8 state alloc failed");
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
ssm_l8_init((ssm_l8_state_t*)vm->ssm_l8_state, SSM_MODE_C0);
|
||
ssm_l8_init_table((ssm_l8_state_t*)vm->ssm_l8_state);
|
||
|
||
vm->ssm_config = sf_malloc(sizeof(ssm_config_t));
|
||
if (!vm->ssm_config)
|
||
{
|
||
log_message(LOG_ERROR, "vm_init: SSM config alloc failed");
|
||
vm->error = 1;
|
||
return;
|
||
}
|
||
/* Initialize with C0 (minimal) mode: all 7 loops off. The adaptive
|
||
* table's first trial-end (or an explicit ssm_apply_mode_from_table()
|
||
* call) overwrites this with the real DoE-seeded starting config. */
|
||
((ssm_config_t*)vm->ssm_config)->L1_heat_tracking = 0;
|
||
((ssm_config_t*)vm->ssm_config)->L2_rolling_window = 0;
|
||
((ssm_config_t*)vm->ssm_config)->L3_linear_decay = 0;
|
||
((ssm_config_t*)vm->ssm_config)->L4_pipelining = 0;
|
||
((ssm_config_t*)vm->ssm_config)->L5_window_inference = 0;
|
||
((ssm_config_t*)vm->ssm_config)->L6_decay_inference = 0;
|
||
((ssm_config_t*)vm->ssm_config)->L7_adaptive_heartrate = 0;
|
||
|
||
#ifdef __STARKERNEL__
|
||
/* Capsule subsystem: register VM hooks, init registry and run log.
|
||
* capsule_birth_mama() is called separately after vm_init() returns,
|
||
* once the caller has access to the compiled-in capsule directory. */
|
||
capsule_vm_hooks_register();
|
||
capsule_vm_registry_init();
|
||
capsule_run_log_init();
|
||
#endif
|
||
}
|
||
|
||
/**
|
||
* @brief Clean up and free VM resources
|
||
*
|
||
* Frees allocated memory and resets VM state.
|
||
*
|
||
* @param vm Pointer to VM structure to clean up
|
||
*/
|
||
void vm_cleanup(VM* vm)
|
||
{
|
||
if (!vm) return;
|
||
|
||
/* Stop heartbeat thread (if running) */
|
||
vm_heartbeat_stop_thread(vm);
|
||
|
||
/* Clean up hot-words cache */
|
||
if (vm->hotwords_cache)
|
||
{
|
||
hotwords_cache_cleanup(vm->hotwords_cache);
|
||
sf_free(vm->hotwords_cache);
|
||
vm->hotwords_cache = NULL;
|
||
}
|
||
|
||
/* Clean up rolling window of truth */
|
||
rolling_window_cleanup(&vm->rolling_window);
|
||
|
||
/* Clean up SSM L8 state */
|
||
if (vm->ssm_l8_state)
|
||
{
|
||
ssm_l8_free_table((ssm_l8_state_t*)vm->ssm_l8_state);
|
||
sf_free(vm->ssm_l8_state);
|
||
vm->ssm_l8_state = NULL;
|
||
}
|
||
if (vm->ssm_config)
|
||
{
|
||
sf_free(vm->ssm_config);
|
||
vm->ssm_config = NULL;
|
||
}
|
||
|
||
if (vm->memory)
|
||
{
|
||
sf_free(vm->memory);
|
||
vm->memory = NULL;
|
||
}
|
||
vm->here = 0;
|
||
|
||
sf_mutex_destroy(&vm->tuning_lock);
|
||
sf_mutex_destroy(&vm->dict_lock);
|
||
} |