test(v4.0.0): execute M+, D-, D0= and D= on the golden model

All four run exactly as written in DECOMPOSITION.md 5.6/5.7 and need no
change now that D+ and DNEGATE are call-free.

Checked against C at 32- and 64-bit cells, optimised and ASan+UBSan:
every edge-vector combination (M+ over all triples, D- and D= over all
50625 quadruples, D0= over all pairs) plus 20000 pseudo-random cases
weighted to equal low or high cells and low sums that wrap to 0.
Mutations of D0= and M+ are caught.

Headroom (data cells under args / return entries under return address):
  M+ 6/4, D- 5/4, D0= 7/7, D= 5/3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-02 22:50:30 -04:00
co-authored by Claude Opus 5.5
parent 092036c97a
commit 6a371b850e
2 changed files with 79 additions and 6 deletions
+4 -4
View File
@@ -390,7 +390,7 @@ A double is two 32-bit cells on a mesh node.
| Word | Fate | v4 definition |
| --- | --- | --- |
| `M+` | CAP | `S>D D+` |
| `M+` | CAP | `S>D D+` — executed on the golden model (2026-10-02). |
| `M-` | CAP | `NEGATE M+` |
| `M*` | CAP | `2DUP xor push ABS SWAP ABS UM* pop 0< IF DNEGATE THEN` |
| `M/MOD` | CAP | `SM/REM` |
@@ -406,11 +406,11 @@ A double is two 32-bit cells on a mesh node.
| `S>D` | CAP | `dup 0<` — executed on the golden model (2026-10-02). |
| `D+` | CAP | `push over push push drop pop over over xor -if L1 drop + -if C1 jump C0 L1: drop over -if L2 drop + jump C1 L2: drop + C0: pop pop + ; C1: pop pop + 1 + ;` — call-free. The carry out of the low cells is found by sign tests: if their top bits differ, there is a carry exactly when the sum's top bit is clear; if they match, exactly when both are set. Executed on the golden model (2026-10-02); leaves its caller 6 data cells under its arguments and 5 return entries. Replaces `push SWAP push over + 2DUP U> ROT drop NEGATE pop pop + +`, which was exact but left 1 return entry. |
| `DNEGATE` | CAP | `inv over if L1 drop push inv 1 + pop ; L1: drop 1 + ;` — call-free: `~d + 1`, carrying into the high cell exactly when the low cell is 0. Executed on the golden model (2026-10-02). Replaces `inv SWAP inv SWAP 1 0 D+`, which left a caller no return-stack room, so `DABS` could not run at all. |
| `D-` | CAP | `DNEGATE D+` |
| `D-` | CAP | `DNEGATE D+` — executed on the golden model (2026-10-02). |
| `DABS` | CAP | `dup 0< IF DNEGATE THEN` — executed on the golden model (2026-10-02). |
| `D0=` | CAP | `OR 0=` |
| `D0=` | CAP | `OR 0=` — executed on the golden model (2026-10-02). |
| `D0<` | CAP | `NIP 0<` |
| `D=` | CAP | `D- D0=` |
| `D=` | CAP | `D- D0=` — executed on the golden model (2026-10-02). |
| `D<` | CAP | `ROT 2DUP = IF 2DROP U< ELSE SWAP < NIP NIP THEN` |
| `DMAX` | CAP | `2OVER 2OVER D< IF 2SWAP THEN 2DROP` |
| `DMIN` | CAP | `2OVER 2OVER D< 0= IF 2SWAP THEN 2DROP` |
+75 -2
View File
@@ -14,7 +14,8 @@
* call-free versions in sections 4 and 5.7; SM/REM is checked on dividends
* built as q*n + r. D+ is the call-free version in 5.7, checked against C
* over every quadruple of the edge vectors and pseudo-random pairs. /MOD,
* U>, ABS, S>D and DABS are as written in section 5 and checked against C. Every one is also probed for how much of
* U>, ABS, S>D, DABS, M+, D-, D0= and D= are as written in section 5 and
* checked against C. Every one is also probed for how much of
* the 10- and 9-deep circular stacks (D-2) it leaves to its caller.
*
* Every call is made with a canary under the arguments, and the canary must
@@ -41,7 +42,7 @@ static v4_asm as;
static v4_cell w_nip, w_swap, w_or, w_negate, w_rot, w_zless, w_zequal,
w_2dup, w_minus, w_uless, w_umstar, w_ummod,
w_ugreater, w_abs, w_s2d, w_dplus, w_dnegate, w_dabs, w_smrem,
w_slashmod;
w_slashmod, w_mplus, w_dminus, w_d0equal, w_dequal;
#define O(name) v4_asm_op(&as, V4_OP_##name)
#define LIT(v) v4_asm_lit(&as, (v4_cell)(v))
@@ -325,6 +326,22 @@ static void build(void)
w_slashmod = v4_asm_label(&as);
O(PUSH); CALL(w_s2d); O(RPOP); CALL(w_smrem); O(SEMI);
/* : M+ S>D D+ ; 5.6 */
w_mplus = v4_asm_label(&as);
CALL(w_s2d); CALL(w_dplus); O(SEMI);
/* : D- DNEGATE D+ ; 5.7 */
w_dminus = v4_asm_label(&as);
CALL(w_dnegate); CALL(w_dplus); O(SEMI);
/* : D0= OR 0= ; 5.7 */
w_d0equal = v4_asm_label(&as);
CALL(w_or); CALL(w_zequal); O(SEMI);
/* : D= D- D0= ; 5.7 */
w_dequal = v4_asm_label(&as);
CALL(w_dminus); CALL(w_d0equal); O(SEMI);
CHECK(v4_asm_ok(&as), "foundation words assemble");
}
@@ -664,6 +681,50 @@ int main(void)
}
}
/* M+, D-, D0= and D= against C: every edge-vector combination, then
* pseudo-random doubles. */
for (unsigned i = 0; i < NVEC; i++)
for (unsigned j = 0; j < NVEC; j++) {
v4_ucell ua = (v4_ucell)vec[i], ub = (v4_ucell)vec[j], rl, rh;
CHECK(call(w_d0equal, 2, vec[i], vec[j], 0) && left1(FLAG(ua == 0 && ub == 0)),
"D0= [%u,%u]", i, j);
for (unsigned k = 0; k < NVEC; k++) {
v4_cell c = vec[k];
dadd(ua, ub, (v4_ucell)c, c < 0 ? MAXU : 0u, &rl, &rh);
CHECK(call(w_mplus, 3, vec[i], vec[j], c) && left2((v4_cell)rl, (v4_cell)rh),
"M+ [%u,%u,%u]", i, j, k);
for (unsigned l = 0; l < NVEC; l++) {
v4_ucell nl, nh;
dneg((v4_ucell)vec[k], (v4_ucell)vec[l], &nl, &nh);
dadd(ua, ub, nl, nh, &rl, &rh);
CHECK(call4(w_dminus, vec[i], vec[j], vec[k], vec[l])
&& left2((v4_cell)rl, (v4_cell)rh), "D- [%u,%u,%u,%u]", i, j, k, l);
CHECK(call4(w_dequal, vec[i], vec[j], vec[k], vec[l])
&& left1(FLAG(i == k && j == l)), "D= [%u,%u,%u,%u]", i, j, k, l);
}
}
}
{
v4_ucell x = (v4_ucell)0x5851F42Du, v[4], rl, rh, nl, nh;
for (unsigned i = 0; i < 20000; i++) {
for (unsigned k = 0; k < 4; k++) { x ^= x << 13; x ^= x >> 7; x ^= x << 17; v[k] = x; }
if (i & 1u) v[2] = v[0]; /* equal low cells */
if (i & 2u) v[3] = v[1]; /* equal high cells */
if (i & 4u) v[2] = 0u - v[0]; /* low sum wraps */
dadd(v[0], v[1], v[2], (v4_cell)v[2] < 0 ? MAXU : 0u, &rl, &rh);
CHECK(call(w_mplus, 3, (v4_cell)v[0], (v4_cell)v[1], (v4_cell)v[2])
&& left2((v4_cell)rl, (v4_cell)rh), "M+ random [%u]", i);
dneg(v[2], v[3], &nl, &nh);
dadd(v[0], v[1], nl, nh, &rl, &rh);
CHECK(call4(w_dminus, (v4_cell)v[0], (v4_cell)v[1], (v4_cell)v[2], (v4_cell)v[3])
&& left2((v4_cell)rl, (v4_cell)rh), "D- random [%u]", i);
CHECK(call4(w_dequal, (v4_cell)v[0], (v4_cell)v[1], (v4_cell)v[2], (v4_cell)v[3])
&& left1(FLAG(v[0] == v[2] && v[1] == v[3])), "D= random [%u]", i);
CHECK(call(w_d0equal, 2, (v4_cell)(v[0] & v[2]), (v4_cell)(v[1] & v[3]), 0)
&& left1(FLAG((v[0] & v[2]) == 0 && (v[1] & v[3]) == 0)), "D0= random [%u]", i);
}
}
/* Stack headroom of the two longest definitions. */
{
static const v4_cell umstar_args[][4] = {
@@ -701,6 +762,18 @@ int main(void)
CHECK(rh >= 5, "D+ leaves 5 return entries");
headroom("DNEGATE", w_dnegate, 2, two_args, 5, 2, &dh, &rh);
headroom("DABS", w_dabs, 2, two_args, 5, 2, &dh, &rh);
static const v4_cell mplus_args[][4] = {
{ -1, 0, 1, 0 }, { 5, 7, -9, 0 }, { 0, 0, -1, 0 }, { (v4_cell)V4_MSB, 0, (v4_cell)V4_MSB, 0 }
};
static const v4_cell dsub_args[][4] = {
{ 0, 0, 1, 0 }, { 5, 7, 9, 11 }, { -1, -1, -1, -1 }, { 0, 1, 0, 1 },
{ (v4_cell)V4_MSB, 0, (v4_cell)V4_MSB, 0 }, { 1, 0, 2, 0 }
};
headroom("M+", w_mplus, 3, mplus_args, 4, 2, &dh, &rh);
headroom("D-", w_dminus, 4, dsub_args, 6, 2, &dh, &rh);
headroom("D0=", w_d0equal, 2, two_args, 5, 1, &dh, &rh);
headroom("D=", w_dequal, 4, dsub_args, 6, 1, &dh, &rh);
CHECK(dh >= 0 && rh >= 0, "D= runs at all");
headroom("SM/REM", w_smrem, 3, smrem_args, 5, 2, &dh, &rh);
CHECK(rh >= 1, "SM/REM leaves room for /MOD's return address");
headroom("/MOD", w_slashmod, 2, slashmod_args, 4, 2, &dh, &rh);