feat(v4): a hook at call, for whoever runs the node
Plan 2026-10-20 (ENGINE step 4), Task A1, for #189 -- Captain Bob, 2026-11-12: the physics blocks alpha.5; order A, 1, 2, then 7. - exec.h/exec.c: v4_call_hook, es->on_call/on_call_ctx; called at `call` once the return address is pushed, before P moves. NULL: as before. - heat.h/heat.c: the per-call-target table (4096 slots, no capsule word counted), its freeze mask and v4_heat_call_* go (ENGINE.md step 3: "The per-call-target count goes"); the kernel will keep a word's accounts. - tests: test_call_hook_sees_every_call, seen to fail first (no on_call); test_heat's per-call tests go with the table. - the plan records today's rulings (order; HEAT-OP: both, a full array). Suite (logs/20261112-225951/v4-tests): test, test-32, sanitize, 0 failures; hosted-check, the three alike. amd64 boot: POST 538/538, dict_hash 0x8c92508f748f99cf, unchanged -- nothing sets the hook yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017RzZgrf7V7Zk2iczBXDRbm
This commit is contained in:
+10
-1
@@ -11,7 +11,10 @@
|
||||
*
|
||||
* Section 1.4: "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."
|
||||
* target. None of this costs an instruction." A call's target is counted by
|
||||
* whoever runs the node, through the hook below (ENGINE.md step 3; plan
|
||||
* 2026-10-20, Task A1, #189): the kernel keeps a word's accounts. The engine's
|
||||
* own per-call-target table, a stand-in, is gone.
|
||||
*
|
||||
* The node owns P, A, B, the stacks and memory; heat and the anti-clock are
|
||||
* separate physics state (section 7 names them as registers).
|
||||
@@ -37,8 +40,14 @@
|
||||
#include "v4/iword.h"
|
||||
#include "v4/node.h"
|
||||
|
||||
/* Called at `call` once the return address is pushed and before P moves to the
|
||||
* target (so P is still the return address). NULL: no hook. */
|
||||
typedef void (*v4_call_hook)(void *ctx, v4_node *n, v4_cell target);
|
||||
|
||||
typedef struct {
|
||||
v4_uheat_t anticlock;
|
||||
v4_call_hook on_call; /* reset leaves NULL */
|
||||
void *on_call_ctx;
|
||||
|
||||
/* A node blocked at its port in the middle of an instruction word
|
||||
* (node.h) goes on, when it has been served, from the slot after the
|
||||
|
||||
+9
-45
@@ -8,27 +8,21 @@
|
||||
* 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 per-opcode counters are here. A call's target is counted by whoever runs
|
||||
* the node, through exec.h's hook at `call` (ENGINE.md step 3: "The
|
||||
* per-call-target count goes"; plan 2026-10-20, Task A1, #189) -- the kernel
|
||||
* keeps a word's accounts. The 4096-slot per-call-target table and its freeze
|
||||
* mask, the golden model's stand-in "in the meantime" (D-6), were here until
|
||||
* 2026-11-12; a target at 4096 or above was not counted, so no capsule word was.
|
||||
*
|
||||
* 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.
|
||||
* The MM names in §7: `HEAT-OP[0..31]` (R) are these counters; `HEAT-CALL[...]`
|
||||
* is the kernel's word accounts (above). D-4 (memory map) is deferred.
|
||||
*
|
||||
* 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
|
||||
* Counts are 64-bit. Even for a short run an opcode can retire 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
|
||||
@@ -40,20 +34,8 @@
|
||||
#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);
|
||||
@@ -61,22 +43,4 @@ 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 */
|
||||
|
||||
Reference in New Issue
Block a user