First code for StarForth v4 (JUSTIFICATION.md section 10, step 1): one node of the 32-instruction core as a C99 model, with cell width as a build parameter. - Node: P, A, B, F18 circular stacks (10 and 9 deep, D-2), word-addressed memory (D-1), 5% guard bands on every bounded list. - Instruction word: six 5-bit slots in 32 bits at every cell width. - Executor: all 32 opcodes of DECOMPOSITION.md 1.3. Cell arithmetic wraps explicitly; no signed overflow or implementation-defined shift. - Heat: per-opcode and per-call-target counters and the anti-clock, driven by instruction retirement (1.4, D-6 interim). - Slot packer and runner for tests, and a reference unsigned multiply in plain C99 with no 128-bit type. Tests run at 32- and 64-bit cells, and under ASan and UBSan. They cover every opcode and execute the first section 4 definitions (NIP SWAP OR NEGATE ROT 0< 0= 2DUP - U<) against the C operation each stands for. UM* as written in section 4 is exact only while u1 <= 2^(n-2). Two known failing cases are pinned in test_foundation.c until it is rewritten. DECOMPOSITION.md: record D-9, the instruction word is 32 bits at every cell width (ruled 2026-10-02). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
80 lines
4.0 KiB
C
80 lines
4.0 KiB
C
/* heat.h -- heat and anti-clock, not instructions themselves (1.4).
|
||
*
|
||
* DECOMPOSITION.md section 1.4 states: "Execution itself drives the physics.
|
||
* Every retired instruction increments that opcode's heat counter and advances
|
||
* the anti-clock; every `call` increments the heat counter of its target. None
|
||
* of this costs an instruction."
|
||
*
|
||
* D-6 is deferred to step 2, but the note immediately adds: "The golden model
|
||
* implements per-opcode and per-call-target heat in the meantime." The
|
||
* counters here are therefore a two-dimensional heat structure (op × target)
|
||
* on a per-call basis, with counts stored in 64-bit (because heat is
|
||
* accumulated and must not roll over at 32-bit). The "per-call-target"
|
||
* counter is indexed by the address of the called target -- on a word-addressed
|
||
* node, that is a cell value equal to the address `a` passed to `call a`.
|
||
*
|
||
* The anti-clock is a virtual tick driven by execution -- one tick per retired
|
||
* instruction -- and the rest of §7 refers to it via `ANTICLOCK`. It is not a
|
||
* wall-clock time, it is a function of the execution stream, as specified.
|
||
*
|
||
* The MM names in §7: `HEAT-OP[0..31]` (R), `HEAT-CALL[...]` (R/W) with a
|
||
* freeze bit (D-6). D-4 (memory map) is deferred, so the model does not
|
||
* materialize those MM addresses yet. This header defines the backing storage
|
||
* that a later MM layer will expose; it keeps the counts separate from the node
|
||
* itself because heat is physics data, not the instruction word stream, and it
|
||
* needs to be accessible to assertions such as K conservation tests later.
|
||
*
|
||
* Counts are 64-bit. Even for a short run a call-heavy word can heat up many
|
||
* times, and the golden model must run POST and remain monotonic and
|
||
* comparable. They are stored as `v4_uheat_t` (which is uint64_t by cell-width
|
||
* parameterisation) rather than cell-sized so that a 32-bit node's heat is not
|
||
* width-truncated in the model. (JUSTIFICATION.md §5 keeps heat arithmetic
|
||
* 64-bit regardless of cell width.)
|
||
*/
|
||
#ifndef V4_HEAT_H
|
||
#define V4_HEAT_H
|
||
|
||
#include "v4/cell.h"
|
||
#include "v4/opcode.h"
|
||
|
||
#define V4_HEAT_MAX_CALL_TARGETS 4096u
|
||
|
||
typedef struct {
|
||
v4_uheat_t op[V4_OPCODE_COUNT]; /* HEAT-OP[0..31] */
|
||
|
||
/* Per-call-target heat. Indexed by target address. The table is fixed-size
|
||
* for the golden model; the actual hardware table size is deferred with D-6,
|
||
* but "in the meantime" the model must have a per-call-target structure.
|
||
* 4096 entries is small (32KB if using 64-bit per entry), enough to cover a
|
||
* typical test program's call targets without needing to allocate on-the-fly
|
||
* -- and since the node has boundaries, over-indexing is caught. */
|
||
v4_uheat_t call[V4_HEAT_MAX_CALL_TARGETS];
|
||
v4_ucell call_freeze_mask[V4_HEAT_MAX_CALL_TARGETS / (sizeof(v4_ucell) * 8u)];
|
||
/* freeze bits: one per call-target entry (D-6). Stored as a bitmask. */
|
||
} v4_heat;
|
||
|
||
void v4_heat_reset(v4_heat *h);
|
||
|
||
/* Every retired instruction: +1 to the opcode's heat and +1 to ANTICLOCK. */
|
||
void v4_heat_on_retire(v4_heat *h, unsigned op, v4_uheat_t *anticlock);
|
||
|
||
/* Every call: +1 to the call-target's heat. The target is the address passed
|
||
* to call (word address). If the target is outside the table range, the model
|
||
* must not silently wrap -- a boundary-style check is the conservative choice
|
||
* here, because the table size is a model parameter. */
|
||
void v4_heat_on_call(v4_heat *h, v4_cell target);
|
||
|
||
/* Read/write helpers for per-call-target heat (R/W). Freeze bits are set per
|
||
* target when D-6's freeze bit is asserted; writes to a frozen target should be
|
||
* ignored in a real implementation, but the MM layer is not defined yet. The
|
||
* golden model stores freeze bits and the accessor can enforce them later. */
|
||
v4_uheat_t v4_heat_call_get(const v4_heat *h, v4_cell target);
|
||
int v4_heat_call_set(v4_heat *h, v4_cell target, v4_uheat_t val);
|
||
|
||
/* Freeze control (D-6). */
|
||
void v4_heat_call_freeze(v4_heat *h, v4_cell target);
|
||
void v4_heat_call_unfreeze(v4_heat *h, v4_cell target);
|
||
int v4_heat_call_is_frozen(const v4_heat *h, v4_cell target);
|
||
|
||
#endif /* V4_HEAT_H */
|