feat(v4.0.0): D< and Q comparisons; call-free (D<), DMAX, DMIN, 2SWAP
Executed on the golden model as written, and correct: <, =, D<, 2OVER,
and Q.=, Q.<, Q.0= (the D=, D<, D0= words).
Fixed, because they could not work under D-2 or were wrong:
- DMAX, DMIN: 2OVER 2OVER D< needs 8 data cells plus D<'s 2, the whole
10-deep data stack, so both failed on every case once the caller held
anything. Rewritten on a new call-free helper
(D<) ( d1 d2 -- d1 d2 flag )
which compares copies of the high cells, then the low cells unsigned
on a tie, with in-line sign tests; DMAX and DMIN then drop the loser.
- 2SWAP: ROT and SWAP written in line. Return headroom 2 -> 4, which
also lifts 2OVER 1 -> 3 and Q.> 1 -> 2.
- Q.>: section 5.26 gave SWAP D<, which swaps single cells; it was wrong
in 11702 of 20169 cases. Now 2SWAP D<.
Checked at 32- and 64-bit cells, optimised and ASan+UBSan: every
edge-vector quadruple (50625) for D<, (D<), 2SWAP, 2OVER, DMAX and DMIN;
Q comparisons on every pair of 13 edge Q values plus 20000 pseudo-random
pairs weighted to ties and one-bit differences, signed (D-8) and against
v3's unsigned comparisons where both values have the same sign.
Mutating any (D<) branch or subtraction fails 700+ checks.
The new fatal-UBSan setting caught a test-side array overflow in the
headroom probe (results buffer sized 4, six needed); fixed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
167fdc3973
commit
d0accdbb88
@@ -373,9 +373,9 @@ Section numbers match the v3 primitive reference.
|
||||
| `0=` `0<` | CAP | §4 |
|
||||
| `0<>` | CAP | `0= 0=` |
|
||||
| `0>` | CAP | `dup 0< SWAP 0= OR 0=` (correct for the most negative number) |
|
||||
| `=` | CAP | `xor 0=` |
|
||||
| `=` | CAP | `xor 0=` — executed on the golden model (2026-10-02). |
|
||||
| `<>` | CAP | `xor 0<>` |
|
||||
| `<` | CAP | `2DUP xor 0< IF drop 0< ELSE - 0< THEN` (overflow-safe) |
|
||||
| `<` | CAP | `2DUP xor 0< IF drop 0< ELSE - 0< THEN` (overflow-safe) — executed on the golden model (2026-10-02). |
|
||||
| `>` | CAP | `SWAP <` |
|
||||
| `<=` | CAP | `> 0=` |
|
||||
| `>=` | CAP | `< 0=` |
|
||||
@@ -412,15 +412,16 @@ A double is two 32-bit cells on a mesh node.
|
||||
| `D0=` | CAP | `OR 0=` — executed on the golden model (2026-10-02). |
|
||||
| `D0<` | CAP | `NIP 0<` |
|
||||
| `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` |
|
||||
| `D<` | CAP | `ROT 2DUP = IF 2DROP U< ELSE SWAP < NIP NIP THEN` — executed on the golden model (2026-10-02). |
|
||||
| `(D<)` | CAP | `( d1 d2 -- d1 d2 flag )`, call-free; internal to `DMAX` and `DMIN`. Copies of the high cells go on top; if they differ the flag comes from them, else from the low cells unsigned. The value tested always has its top bit set exactly when `d1 < d2`: `ah` (high signs differ), `ah - bh` (agree), `bl` (low top bits differ), `al - bl` (agree); `x - y` with `y` on top is `push inv pop + inv`. `dup push push over pop over over xor if TIE drop over over xor -if HS drop drop jump S1 HS: drop push inv pop + inv S1: -if N1 drop -1 jump D1 N1: drop 0 D1: pop SWAP ; TIE: drop drop drop dup push push over pop over over xor -if LS drop NIP jump S2 LS: drop push inv pop + inv S2: -if N2 drop -1 jump D2 N2: drop 0 D2: pop pop ROT ;` with `SWAP`, `NIP` and `ROT` in line. Executed on the golden model (2026-10-02). |
|
||||
| `DMAX` | CAP | `(D<) if L drop push push drop drop pop pop ; L: drop drop drop ;` Executed on the golden model (2026-10-02). Replaces `2OVER 2OVER D< IF 2SWAP THEN 2DROP`, which needs 8 data cells plus `D<`'s 2: the whole 10-deep data stack, so it failed whenever the caller held anything at all (D-2). |
|
||||
| `DMIN` | CAP | `(D<) if L drop drop drop ; L: drop push push drop drop pop pop ;` Executed on the golden model (2026-10-02); replaces `2OVER 2OVER D< 0= IF 2SWAP THEN 2DROP` for the same reason as `DMAX`. |
|
||||
| `D2*` | CAP | `2* over 0< NEGATE OR SWAP 2* SWAP` |
|
||||
| `D2/` | CAP | `dup 1 and push 2/ SWAP 1 RSHIFT pop IF MSB OR THEN SWAP` |
|
||||
| `2DROP` | IN | `drop drop` |
|
||||
| `2DUP` | IN | `over over` |
|
||||
| `2SWAP` | CAP | `ROT push ROT pop` |
|
||||
| `2OVER` | CAP | `push push 2DUP pop pop 2SWAP` |
|
||||
| `2SWAP` | CAP | `ROT push ROT pop`, with `ROT` and `SWAP` in line so it makes no calls. Executed on the golden model (2026-10-02). Called, `ROT` and `SWAP` left its caller 2 return entries; in line, 4. |
|
||||
| `2OVER` | CAP | `push push 2DUP pop pop 2SWAP` — executed on the golden model (2026-10-02). |
|
||||
| `2ROT` | CAP | `2>R 2SWAP 2R> 2SWAP` |
|
||||
| `2>R` | IN | `SWAP push push` |
|
||||
| `2R>` | IN | `pop pop SWAP` |
|
||||
@@ -653,7 +654,7 @@ word. Per D-10 it occupies two cells on a 64-bit node too. Per D-8, v4 Q values
|
||||
| `Q.FROM-INT` | CAP | `push 0 a! 0 pop 15 FOR +* UNEXT push drop a pop` — `n * 2^16` as a signed double. `+*` with `S = 0` never adds, so each step is an exact arithmetic right shift of `T:A`; starting from `T:A = n:0` (that is, `n * 2^N`) and shifting `N-16` bits leaves `n * 2^16`. The count is `N-17`: 15 at 32-bit cells, 47 at 64. Negative values are no longer clamped to 0. Executed on the golden model (2026-10-02): matches v3's `q48_from_u64` for `n >= 0` (at 64-bit cells in the low cell, where v3 wraps for `n >= 2^47`, per D-10). Clobbers `A`. |
|
||||
| `Q.TO-INT` | CAP | `push a! 0 pop 15 FOR +* UNEXT drop drop a` — the same `+*` shift, 16 bits at every cell width, leaving the low cell in `A`. Rounds toward minus infinity, as v3's arithmetic shift does (−1.5 gives −2). Executed on the golden model (2026-10-02): v3's `q48_to_u64` exactly at 64-bit cells, its low cell at 32. Clobbers `A`. |
|
||||
| `Q.1` `Q.0` `Q.SCALE` | IN | Double-cell constants. |
|
||||
| `Q.=` `Q.<` `Q.>` `Q.0=` `Q.MAX` `Q.MIN` | CAP | `D=`, `D<`, `SWAP D<` (for `Q.>`), `D0=`, `DMAX`, `DMIN`. Signed. |
|
||||
| `Q.=` `Q.<` `Q.>` `Q.0=` `Q.MAX` `Q.MIN` | CAP | `D=`, `D<`, `2SWAP D<` (for `Q.>`), `D0=`, `DMAX`, `DMIN`. Signed (D-8); v3 compared unsigned, so v3 agrees only when both values have the same sign. Executed on the golden model (2026-10-02). `Q.>` was given here as `SWAP D<`, which swaps single cells, not Q values, and gave a wrong answer in 11702 of 20169 test cases. |
|
||||
| `Q.PRINT` | CAP | Pictured output, five fractional digits. |
|
||||
|
||||
### 5.27 Inference engine
|
||||
|
||||
+258
-3
@@ -19,7 +19,10 @@
|
||||
* DABS and DNEGATE words) are checked against v3's 64-bit q48_add, q48_sub,
|
||||
* q48_abs and 0 - q, with Q values two cells wide at both widths (D-10).
|
||||
* Q.FROM-INT and Q.TO-INT are checked against n * 2^16 and q >> 16 in C and
|
||||
* against v3's q48_from_u64 and q48_to_u64 where v4 keeps v3's meaning. Every word is
|
||||
* against v3's q48_from_u64 and q48_to_u64 where v4 keeps v3's meaning.
|
||||
* <, =, D<, 2OVER, the call-free (D<), DMAX, DMIN and 2SWAP, and the Q
|
||||
* comparisons on them, are checked against C, signed (D-8), and against v3
|
||||
* where v3's unsigned Q comparisons agree. Every word is
|
||||
* also probed for how much of the 10- and 9-deep circular stacks (D-2) it
|
||||
* leaves to its caller.
|
||||
*
|
||||
@@ -49,7 +52,8 @@ 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_mplus, w_dminus, w_d0equal, w_dequal,
|
||||
w_qfromint, w_qtoint;
|
||||
w_qfromint, w_qtoint, w_less, w_equal, w_dless, w_2swap, w_2over,
|
||||
w_dmax, w_dmin, w_qgt_doc, w_qgt, w_dltkeep;
|
||||
|
||||
#define O(name) v4_asm_op(&as, V4_OP_##name)
|
||||
#define LIT(v) v4_asm_lit(&as, (v4_cell)(v))
|
||||
@@ -374,6 +378,144 @@ static void build(void)
|
||||
O(MUL_STEP); O(UNEXT);
|
||||
O(DROP); O(DROP); O(PUSH_A); O(SEMI);
|
||||
|
||||
/* : = xor 0= ; 5.5 */
|
||||
w_equal = v4_asm_label(&as);
|
||||
O(XOR); CALL(w_zequal); O(SEMI);
|
||||
|
||||
/* : < 2DUP xor 0< IF drop 0< ELSE - 0< THEN ; 5.5 */
|
||||
w_less = v4_asm_label(&as);
|
||||
O(OVER); O(OVER); O(XOR); CALL(w_zless);
|
||||
ref = v4_asm_branch_fwd(&as, V4_OP_IF);
|
||||
O(DROP); O(DROP); CALL(w_zless);
|
||||
{
|
||||
v4_asm_ref j = v4_asm_branch_fwd(&as, V4_OP_JUMP);
|
||||
v4_asm_resolve(&as, ref, v4_asm_label(&as));
|
||||
O(DROP); CALL(w_minus); CALL(w_zless);
|
||||
v4_asm_resolve(&as, j, v4_asm_label(&as));
|
||||
}
|
||||
O(SEMI);
|
||||
|
||||
/* : D< ROT 2DUP = IF 2DROP U< ELSE SWAP < NIP NIP THEN ; 5.7 */
|
||||
w_dless = v4_asm_label(&as);
|
||||
CALL(w_rot); O(OVER); O(OVER); CALL(w_equal);
|
||||
ref = v4_asm_branch_fwd(&as, V4_OP_IF);
|
||||
O(DROP); O(DROP); O(DROP); CALL(w_uless);
|
||||
{
|
||||
v4_asm_ref j = v4_asm_branch_fwd(&as, V4_OP_JUMP);
|
||||
v4_asm_resolve(&as, ref, v4_asm_label(&as));
|
||||
O(DROP); CALL(w_swap); CALL(w_less); CALL(w_nip); CALL(w_nip);
|
||||
v4_asm_resolve(&as, j, v4_asm_label(&as));
|
||||
}
|
||||
O(SEMI);
|
||||
|
||||
/* : 2SWAP ROT push ROT pop ; 5.7, call-free
|
||||
* with ROT (push SWAP pop SWAP) and SWAP (over push push drop pop pop)
|
||||
* written in line:
|
||||
* push over push push drop pop pop pop over push push drop pop pop
|
||||
* push
|
||||
* push over push push drop pop pop pop over push push drop pop pop
|
||||
* pop ; */
|
||||
#define ROT_INLINE() do { O(PUSH); O(OVER); O(PUSH); O(PUSH); O(DROP); O(RPOP); O(RPOP); \
|
||||
O(RPOP); O(OVER); O(PUSH); O(PUSH); O(DROP); O(RPOP); O(RPOP); } while (0)
|
||||
w_2swap = v4_asm_label(&as);
|
||||
ROT_INLINE(); O(PUSH); ROT_INLINE(); O(RPOP); O(SEMI);
|
||||
#undef ROT_INLINE
|
||||
|
||||
/* : 2OVER push push 2DUP pop pop 2SWAP ; 5.7 */
|
||||
w_2over = v4_asm_label(&as);
|
||||
O(PUSH); O(PUSH); O(OVER); O(OVER); O(RPOP); O(RPOP); CALL(w_2swap); O(SEMI);
|
||||
|
||||
/* : (D<) ( d1 d2 -- d1 d2 flag ) 5.7, call-free
|
||||
* Signed d1 < d2, leaving both doubles. Copies of ah and bh go on top;
|
||||
* if they differ, flag comes from them, else from al U< bl. In every
|
||||
* case the tested value has its top bit set exactly when d1 < d2:
|
||||
* highs, signs differ: ah (d1 < d2 iff ah < 0)
|
||||
* highs, signs agree: ah - bh (cannot overflow)
|
||||
* lows, top bits differ: bl (al U< bl iff bl's is set)
|
||||
* lows, top bits agree: al - bl
|
||||
* x - y with y on top is `push inv pop + inv`.
|
||||
*
|
||||
* dup push push over pop al ah bl ah bh R: bh
|
||||
* over over xor if TIE
|
||||
* drop over over xor -if HS
|
||||
* drop drop jump S1 al ah bl ah
|
||||
* HS: drop push inv pop + inv al ah bl ah-bh
|
||||
* S1: -if N1 drop -1 jump D1 N1: drop 0
|
||||
* D1: pop over push push drop pop pop ; al ah bl bh f
|
||||
* TIE: drop drop drop al ah bl R: bh
|
||||
* dup push push over pop al ah al bl R: bh bl
|
||||
* over over xor -if LS
|
||||
* drop push drop pop jump S2 al ah bl
|
||||
* LS: drop push inv pop + inv al ah al-bl
|
||||
* S2: -if N2 drop -1 jump D2 N2: drop 0
|
||||
* D2: pop pop al ah f bl bh
|
||||
* push over push push drop pop pop pop over push push drop pop pop ; */
|
||||
{
|
||||
v4_asm_ref tie, hs, s1a, n1, d1, ls, s2a, n2, d2;
|
||||
w_dltkeep = v4_asm_label(&as);
|
||||
O(DUP); O(PUSH); O(PUSH); O(OVER); O(RPOP);
|
||||
O(OVER); O(OVER); O(XOR);
|
||||
tie = v4_asm_branch_fwd(&as, V4_OP_IF);
|
||||
O(DROP); O(OVER); O(OVER); O(XOR);
|
||||
hs = v4_asm_branch_fwd(&as, V4_OP_MINUS_IF);
|
||||
O(DROP); O(DROP);
|
||||
s1a = v4_asm_branch_fwd(&as, V4_OP_JUMP);
|
||||
v4_asm_resolve(&as, hs, v4_asm_label(&as));
|
||||
O(DROP); O(PUSH); O(INV); O(RPOP); O(ADD); O(INV);
|
||||
v4_asm_resolve(&as, s1a, v4_asm_label(&as));
|
||||
n1 = v4_asm_branch_fwd(&as, V4_OP_MINUS_IF);
|
||||
O(DROP); LIT(-1);
|
||||
d1 = v4_asm_branch_fwd(&as, V4_OP_JUMP);
|
||||
v4_asm_resolve(&as, n1, v4_asm_label(&as));
|
||||
O(DROP); LIT(0);
|
||||
v4_asm_resolve(&as, d1, v4_asm_label(&as));
|
||||
O(RPOP); O(OVER); O(PUSH); O(PUSH); O(DROP); O(RPOP); O(RPOP); O(SEMI);
|
||||
|
||||
v4_asm_resolve(&as, tie, v4_asm_label(&as));
|
||||
O(DROP); O(DROP); O(DROP);
|
||||
O(DUP); O(PUSH); O(PUSH); O(OVER); O(RPOP);
|
||||
O(OVER); O(OVER); O(XOR);
|
||||
ls = v4_asm_branch_fwd(&as, V4_OP_MINUS_IF);
|
||||
O(DROP); O(PUSH); O(DROP); O(RPOP);
|
||||
s2a = v4_asm_branch_fwd(&as, V4_OP_JUMP);
|
||||
v4_asm_resolve(&as, ls, v4_asm_label(&as));
|
||||
O(DROP); O(PUSH); O(INV); O(RPOP); O(ADD); O(INV);
|
||||
v4_asm_resolve(&as, s2a, v4_asm_label(&as));
|
||||
n2 = v4_asm_branch_fwd(&as, V4_OP_MINUS_IF);
|
||||
O(DROP); LIT(-1);
|
||||
d2 = v4_asm_branch_fwd(&as, V4_OP_JUMP);
|
||||
v4_asm_resolve(&as, n2, v4_asm_label(&as));
|
||||
O(DROP); LIT(0);
|
||||
v4_asm_resolve(&as, d2, v4_asm_label(&as));
|
||||
O(RPOP); O(RPOP);
|
||||
O(PUSH); O(OVER); O(PUSH); O(PUSH); O(DROP); O(RPOP); O(RPOP);
|
||||
O(RPOP); O(OVER); O(PUSH); O(PUSH); O(DROP); O(RPOP); O(RPOP); O(SEMI);
|
||||
}
|
||||
|
||||
/* : DMAX ( d1 d2 -- d3 ) (D<) if L drop push push drop drop pop pop ;
|
||||
* L: drop drop drop ; 5.7 */
|
||||
w_dmax = v4_asm_label(&as);
|
||||
CALL(w_dltkeep);
|
||||
ref = v4_asm_branch_fwd(&as, V4_OP_IF);
|
||||
O(DROP); O(PUSH); O(PUSH); O(DROP); O(DROP); O(RPOP); O(RPOP); O(SEMI);
|
||||
v4_asm_resolve(&as, ref, v4_asm_label(&as));
|
||||
O(DROP); O(DROP); O(DROP); O(SEMI);
|
||||
|
||||
/* : DMIN ( d1 d2 -- d3 ) (D<) if L drop drop drop ;
|
||||
* L: drop push push drop drop pop pop ; 5.7 */
|
||||
w_dmin = v4_asm_label(&as);
|
||||
CALL(w_dltkeep);
|
||||
ref = v4_asm_branch_fwd(&as, V4_OP_IF);
|
||||
O(DROP); O(DROP); O(DROP); O(SEMI);
|
||||
v4_asm_resolve(&as, ref, v4_asm_label(&as));
|
||||
O(DROP); O(PUSH); O(PUSH); O(DROP); O(DROP); O(RPOP); O(RPOP); O(SEMI);
|
||||
|
||||
/* Q.> as 5.26 gives it, SWAP D<, and with 2SWAP. */
|
||||
w_qgt_doc = v4_asm_label(&as);
|
||||
CALL(w_swap); CALL(w_dless); O(SEMI);
|
||||
w_qgt = v4_asm_label(&as);
|
||||
CALL(w_2swap); CALL(w_dless); O(SEMI);
|
||||
|
||||
CHECK(v4_asm_ok(&as), "foundation words assemble");
|
||||
}
|
||||
|
||||
@@ -564,6 +706,27 @@ static uint64_t asr64(uint64_t x, unsigned k)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* The k results, given bottom first, then the canary under them. */
|
||||
static int leftk(const v4_cell *want, unsigned k)
|
||||
{
|
||||
while (k-- > 0)
|
||||
if (v4_dstack_pop(&n.ds) != want[k]) return 0;
|
||||
return v4_dstack_pop(&n.ds) == CANARY;
|
||||
}
|
||||
|
||||
/* Signed double a < b, in C. */
|
||||
static int dlt(v4_ucell al, v4_ucell ah, v4_ucell bl, v4_ucell bh)
|
||||
{
|
||||
if (ah != bh) return (v4_cell)ah < (v4_cell)bh;
|
||||
return al < bl;
|
||||
}
|
||||
|
||||
/* Q48.16 as v4 reads it (D-8: signed). */
|
||||
static int64_t qs(uint64_t q)
|
||||
{
|
||||
return (q >> 63) ? -(int64_t)(~q) - 1 : (int64_t)q;
|
||||
}
|
||||
|
||||
/* Stack headroom. Run `word` with `dfill` marked cells under the canary and
|
||||
* `rfill` marked cells under its return address, and report whether the
|
||||
* results, the canary and every marked cell come back intact. The F18 stacks
|
||||
@@ -575,7 +738,7 @@ static uint64_t asr64(uint64_t x, unsigned k)
|
||||
static int fits(v4_cell word, unsigned argc, const v4_cell *arg,
|
||||
unsigned nres, unsigned dfill, unsigned rfill)
|
||||
{
|
||||
v4_cell want[4];
|
||||
v4_cell want[V4_DATA_DEPTH];
|
||||
unsigned i;
|
||||
|
||||
v4_dstack_reset(&n.ds);
|
||||
@@ -583,6 +746,7 @@ static int fits(v4_cell word, unsigned argc, const v4_cell *arg,
|
||||
v4_exec_reset(&es);
|
||||
for (i = 0; i < argc; i++) v4_dstack_push(&n.ds, arg[i]);
|
||||
if (v4_test_call(&n, &es, &h, word, 100000) <= 0) return 0;
|
||||
if (nres > V4_DATA_DEPTH) return 0;
|
||||
for (i = 0; i < nres; i++) want[i] = v4_dstack_pop(&n.ds);
|
||||
|
||||
v4_dstack_reset(&n.ds);
|
||||
@@ -934,6 +1098,82 @@ int main(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* <, =, D<, 2SWAP, 2OVER, DMAX and DMIN as written in section 5, against C. */
|
||||
for (unsigned i = 0; i < NVEC; i++)
|
||||
for (unsigned j = 0; j < NVEC; j++) {
|
||||
v4_cell a = vec[i], b = vec[j];
|
||||
CHECK(call(w_less, 2, a, b, 0) && left1(FLAG(a < b)), "< [%u,%u]", i, j);
|
||||
CHECK(call(w_equal, 2, a, b, 0) && left1(FLAG(a == b)), "= [%u,%u]", i, j);
|
||||
for (unsigned k = 0; k < NVEC; k++)
|
||||
for (unsigned l = 0; l < NVEC; l++) {
|
||||
v4_cell c = vec[k], d = vec[l];
|
||||
int lt = dlt((v4_ucell)a, (v4_ucell)b, (v4_ucell)c, (v4_ucell)d);
|
||||
v4_cell sw[4], ov[6], mx[2], mn[2];
|
||||
sw[0] = c; sw[1] = d; sw[2] = a; sw[3] = b;
|
||||
ov[0] = a; ov[1] = b; ov[2] = c; ov[3] = d; ov[4] = a; ov[5] = b;
|
||||
mx[0] = lt ? c : a; mx[1] = lt ? d : b;
|
||||
mn[0] = lt ? a : c; mn[1] = lt ? b : d;
|
||||
CHECK(call4(w_dless, a, b, c, d) && left1(FLAG(lt)), "D< [%u,%u,%u,%u]", i, j, k, l);
|
||||
{
|
||||
v4_cell kp[5];
|
||||
kp[0] = a; kp[1] = b; kp[2] = c; kp[3] = d; kp[4] = FLAG(lt);
|
||||
CHECK(call4(w_dltkeep, a, b, c, d) && leftk(kp, 5), "(D<) [%u,%u,%u,%u]", i, j, k, l);
|
||||
}
|
||||
CHECK(call4(w_2swap, a, b, c, d) && leftk(sw, 4), "2SWAP [%u,%u,%u,%u]", i, j, k, l);
|
||||
CHECK(call4(w_2over, a, b, c, d) && leftk(ov, 6), "2OVER [%u,%u,%u,%u]", i, j, k, l);
|
||||
CHECK(call4(w_dmax, a, b, c, d) && leftk(mx, 2), "DMAX [%u,%u,%u,%u]", i, j, k, l);
|
||||
CHECK(call4(w_dmin, a, b, c, d) && leftk(mn, 2), "DMIN [%u,%u,%u,%u]", i, j, k, l);
|
||||
}
|
||||
}
|
||||
|
||||
/* Q.=, Q.<, Q.>, Q.0=, Q.MAX, Q.MIN (section 5.26: D=, D<, 2SWAP D<, D0=,
|
||||
* DMAX, DMIN) on Q values, signed (D-8). v3 compares Q values unsigned,
|
||||
* so v3 agrees only when both have the same sign; that is checked too. */
|
||||
{
|
||||
static const uint64_t qc[] = {
|
||||
0, 1, 0x8000u, 0x10000u, 0x18000u,
|
||||
(uint64_t)0 - 0x10000u, (uint64_t)0 - 1u, (uint64_t)0 - 0x8000u,
|
||||
0xFFFFFFFFu, 0x100000000u, (uint64_t)0 - 0x100000000u,
|
||||
((uint64_t)1 << 63) - 1u, (uint64_t)1 << 63
|
||||
};
|
||||
const unsigned nc = sizeof qc / sizeof qc[0];
|
||||
uint64_t x = 0x2545F4914F6CDD1Du;
|
||||
unsigned doc_wrong = 0, doc_runs = 0;
|
||||
|
||||
for (unsigned i = 0; i < nc * nc + 20000u; i++) {
|
||||
uint64_t a, b;
|
||||
v4_cell al, ah, bl, bh, w[2];
|
||||
int lt, gt, eq;
|
||||
if (i < nc * nc) { a = qc[i / nc]; b = qc[i % nc]; }
|
||||
else {
|
||||
x ^= x << 13; x ^= x >> 7; x ^= x << 17; a = x;
|
||||
x ^= x << 13; x ^= x >> 7; x ^= x << 17; b = x;
|
||||
if (i & 1u) b = a; /* ties */
|
||||
if (i & 2u) b ^= (uint64_t)1 << (x >> 58); /* near */
|
||||
}
|
||||
qcells(a, &al, &ah);
|
||||
qcells(b, &bl, &bh);
|
||||
lt = qs(a) < qs(b); gt = qs(a) > qs(b); eq = a == b;
|
||||
|
||||
CHECK(call4(w_dequal, al, ah, bl, bh) && left1(FLAG(eq)), "Q.= [%u]", i);
|
||||
CHECK(call4(w_dless, al, ah, bl, bh) && left1(FLAG(lt)), "Q.< [%u]", i);
|
||||
CHECK(call4(w_qgt, al, ah, bl, bh) && left1(FLAG(gt)), "Q.> [%u]", i);
|
||||
CHECK(call(w_d0equal, 2, al, ah, 0) && left1(FLAG(a == 0)), "Q.0= [%u]", i);
|
||||
w[0] = lt ? bl : al; w[1] = lt ? bh : ah;
|
||||
CHECK(call4(w_dmax, al, ah, bl, bh) && leftk(w, 2), "Q.MAX [%u]", i);
|
||||
w[0] = lt ? al : bl; w[1] = lt ? ah : bh;
|
||||
CHECK(call4(w_dmin, al, ah, bl, bh) && leftk(w, 2), "Q.MIN [%u]", i);
|
||||
if ((a >> 63) == (b >> 63)) { /* v3 agrees here */
|
||||
CHECK(lt == (a < b) && gt == (a > b), "Q compare v3 [%u]", i);
|
||||
}
|
||||
|
||||
/* Q.> as section 5.26 writes it, SWAP D<: count wrong answers. */
|
||||
doc_runs++;
|
||||
if (!(call4(w_qgt_doc, al, ah, bl, bh) && left1(FLAG(gt)))) doc_wrong++;
|
||||
}
|
||||
printf(" Q.> as written (SWAP D<): %u wrong of %u\n", doc_wrong, doc_runs);
|
||||
}
|
||||
|
||||
/* Stack headroom of the two longest definitions. */
|
||||
{
|
||||
static const v4_cell umstar_args[][4] = {
|
||||
@@ -983,6 +1223,21 @@ int main(void)
|
||||
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");
|
||||
static const v4_cell cmp_args[][4] = {
|
||||
{ 0, 0, 0, 0 }, { 1, 0, 2, 0 }, { 2, 0, 1, 0 }, { 0, -1, 0, 1 }, { 0, 1, 0, -1 },
|
||||
{ 0, 5, 0, 7 }, { 0, 7, 0, 5 }, { -1, 0, 1, 0 }, { 1, 0, -1, 0 }
|
||||
};
|
||||
headroom("<", w_less, 2, two_args, 5, 1, &dh, &rh);
|
||||
headroom("=", w_equal, 2, two_args, 5, 1, &dh, &rh);
|
||||
headroom("D<", w_dless, 4, cmp_args, 9, 1, &dh, &rh);
|
||||
headroom("(D<)", w_dltkeep, 4, cmp_args, 9, 5, &dh, &rh);
|
||||
headroom("2SWAP", w_2swap, 4, cmp_args, 9, 4, &dh, &rh);
|
||||
headroom("2OVER", w_2over, 4, cmp_args, 9, 6, &dh, &rh);
|
||||
headroom("DMAX", w_dmax, 4, cmp_args, 9, 2, &dh, &rh);
|
||||
CHECK(dh >= 2 && rh >= 2, "DMAX leaves room");
|
||||
headroom("DMIN", w_dmin, 4, cmp_args, 9, 2, &dh, &rh);
|
||||
CHECK(dh >= 2 && rh >= 2, "DMIN leaves room");
|
||||
headroom("Q.> (2SWAP D<)", w_qgt, 4, cmp_args, 9, 1, &dh, &rh);
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user