feat(v4.0.0): UM* corrections after the loop; return headroom 4 -> 6

UM* parked its three corrections (c_hi, c_lo, t0) on the return stack
before the +* loop, leaving its caller 4 return entries. Now u1 and u2
wait on the data stack under the loop -- +* touches only T, S and A --
and the corrections are made after it, with -if in place of 0< calls.
The return stack holds the loop count, then at most two temporaries.
The loop count is pushed before s is made, so the data stack never holds
more than four cells: data headroom is unchanged at 6.

Headroom (data cells under args / return entries): 6/4 -> 6/6.
Q.*, which calls UM* four times, goes from 2/1 to 2/2.

Same checks as before at 32- and 64-bit cells, optimised and
ASan+UBSan; breaking any of the four corrections fails 4900+ checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-02 23:31:52 -04:00
co-authored by Claude Opus 5.5
parent ab3b708540
commit 54e1cdafb1
2 changed files with 70 additions and 35 deletions
+51 -25
View File
@@ -134,31 +134,57 @@ static void build(void)
O(DROP); CALL(w_minus); CALL(w_zless); O(SEMI);
/* : UM* ( u1 u2 -- ulo uhi ) section 4, D-3
* over 0< over and push R: m1 ? u2 : 0
* over over 0< and 1 and pop + push R: c_hi
* over over and 1 and push R: c_hi c_lo
* over 1 and NEGATE over 2/ and push R: c_hi c_lo t0
* a! 2/ pop s t0 A: u2
* 31 FOR +* UNEXT s hi A: lo
* NIP a 2* pop + SWAP lo' hi
* 2* a 0< NEGATE + pop + ; lo' hi'
* NEGATE is placed in line. 31 is the cell width less one; the loop body
* is the start of its own word so that unext restarts it. */
w_umstar = v4_asm_label(&as);
O(OVER); CALL(w_zless); O(OVER); O(AND); O(PUSH);
O(OVER); O(OVER); CALL(w_zless); O(AND); LIT(1); O(AND);
O(RPOP); O(ADD); O(PUSH);
O(OVER); O(OVER); O(AND); LIT(1); O(AND); O(PUSH);
O(OVER); LIT(1); O(AND); O(INV); LIT(1); O(ADD);
O(OVER); O(TWO_SLASH); O(AND); O(PUSH);
O(BANG_A); O(TWO_SLASH); O(RPOP);
LIT(V4_CELL_BITS - 1); O(PUSH);
(void)v4_asm_label(&as);
O(MUL_STEP); O(UNEXT);
CALL(w_nip);
O(PUSH_A); O(TWO_STAR); O(RPOP); O(ADD); CALL(w_swap);
O(TWO_STAR); O(PUSH_A); CALL(w_zless); O(INV); LIT(1); O(ADD); O(ADD);
O(RPOP); O(ADD); O(SEMI);
* over 1 and inv 1 + over 2/ and u1 u2 t0 t0 = u2 2/ if u1 odd
* over a! 31 push R: loop count (FOR's push, early)
* push over 2/ pop u1 u2 s t0 A: u2, s = u1 2/
* L: +* unext u1 u2 s hi A: lo
* push drop pop u1 u2 hi
* 2* a -if L0 drop 1 + jump L1 L0: drop L1: push R: 2hi + top bit of lo
* over -if L2 drop dup jump L3 L2: drop 0 L3: push R: + (u1<0 ? u2 : 0)
* dup -if L4 drop over 1 and jump L5 L4: drop 0 L5: u1 u2 (u2<0 ? u1&1 : 0)
* pop + pop + push u1 u2 R: hi'
* and 1 and a 2* + pop ; lo' hi'
* u1 and u2 wait on the data stack under the loop (+* touches only T, S
* and A), so the corrections are made afterwards and the return stack
* holds at most two temporaries. 31 is the cell width less one, pushed
* before s is made so the data stack never holds more than four; the loop
* body is the start of its own word so that unext restarts it. */
{
v4_asm_ref l0, l1, l2, l3, l4, l5;
w_umstar = v4_asm_label(&as);
O(OVER); LIT(1); O(AND); O(INV); LIT(1); O(ADD); O(OVER); O(TWO_SLASH); O(AND);
O(OVER); O(BANG_A); LIT(V4_CELL_BITS - 1); O(PUSH);
O(PUSH); O(OVER); O(TWO_SLASH); O(RPOP);
(void)v4_asm_label(&as);
O(MUL_STEP); O(UNEXT);
O(PUSH); O(DROP); O(RPOP);
O(TWO_STAR); O(PUSH_A);
l0 = v4_asm_branch_fwd(&as, V4_OP_MINUS_IF);
O(DROP); LIT(1); O(ADD);
l1 = v4_asm_branch_fwd(&as, V4_OP_JUMP);
v4_asm_resolve(&as, l0, v4_asm_label(&as));
O(DROP);
v4_asm_resolve(&as, l1, v4_asm_label(&as));
O(PUSH);
O(OVER);
l2 = v4_asm_branch_fwd(&as, V4_OP_MINUS_IF);
O(DROP); O(DUP);
l3 = v4_asm_branch_fwd(&as, V4_OP_JUMP);
v4_asm_resolve(&as, l2, v4_asm_label(&as));
O(DROP); LIT(0);
v4_asm_resolve(&as, l3, v4_asm_label(&as));
O(PUSH);
O(DUP);
l4 = v4_asm_branch_fwd(&as, V4_OP_MINUS_IF);
O(DROP); O(OVER); LIT(1); O(AND);
l5 = v4_asm_branch_fwd(&as, V4_OP_JUMP);
v4_asm_resolve(&as, l4, v4_asm_label(&as));
O(DROP); LIT(0);
v4_asm_resolve(&as, l5, v4_asm_label(&as));
O(RPOP); O(ADD); O(RPOP); O(ADD); O(PUSH);
O(AND); LIT(1); O(AND); O(PUSH_A); O(TWO_STAR); O(ADD); O(RPOP);
O(SEMI);
}
/* : UM/MOD ( ulo uhi ud -- urem uquot ) section 4
* a! 31 FOR