boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

18-gc-deep-list.scm (2367B)


      1 ;; Mark-stack overflow stress: force the
      2 ;; `mark_push → mark_push_recurse → mark_value` tail-branch fallback.
      3 ;;
      4 ;; `mark_stack` holds 512 entries (see `:mark_stack` in lisp.M1).
      5 ;; Overflow requires a DFS frontier wider than 512 at some point during
      6 ;; the mark phase. The cleanest trigger is a single live vector with
      7 ;; >512 element slots: `mark_value_vector` pushes each slot in one burst,
      8 ;; so the stack fills in-place and every further push routes through
      9 ;; `mark_push_recurse`.
     10 ;;
     11 ;; (LISP-GC.md originally conjectured that a deep *list* would overflow;
     12 ;; inspection of the mark order shows it won't — `mark_value_pair`
     13 ;; pushes cdr first and car second, so the DFS immediately consumes
     14 ;; the car side and leaves at most one leftover cdr per level. A flat
     15 ;; list never exceeds stack depth ~2. A large vector does. Keep the
     16 ;; filename for continuity with the step-5 plan in LISP-GC.md.)
     17 ;;
     18 ;; Bug this test pinned down: on aarch64/riscv64, `mark_push_recurse`
     19 ;; originally did `CALL mark_value ; RET`. Those arches' native CALL
     20 ;; writes the return address to a link register rather than pushing it,
     21 ;; so the inner CALL clobbered the incoming LR and the outer RET
     22 ;; branched to itself. The routine now tail-branches via
     23 ;; `li_br &mark_value ; b`, which lets mark_value return directly to
     24 ;; mark_push's caller. This test exercises that path across all three
     25 ;; arches.
     26 ;;
     27 ;; Plan:
     28 ;; 1. Allocate a 490-slot global vector. The mark stack is 512 entries
     29 ;;    and root-walking has contributed a handful of pending pair
     30 ;;    children before the vector is popped, so pushing 490 more
     31 ;;    elements crosses the threshold mid-vector.
     32 ;; 2. Fill with distinct fixnums so a mis-swept vector corrupts the
     33 ;;    checksum.
     34 ;; 3. Churn the pair arena to force repeated GCs while the vector
     35 ;;    stays globally reachable — each GC re-marks the vector and
     36 ;;    re-hits the overflow path.
     37 ;; 4. Sum the vector. Expected: 0 + 1 + … + 489 = 490 * 489 / 2 = 119805.
     38 
     39 (define v (make-vector 490 0))
     40 
     41 (define fill
     42   (lambda (i)
     43     (if (= i 490)
     44         0
     45         (begin (vector-set! v i i) (fill (+ i 1))))))
     46 (fill 0)
     47 
     48 (define churn
     49   (lambda (n)
     50     (if (= n 0)
     51         0
     52         (begin (cons n n) (churn (- n 1))))))
     53 (churn 2000)
     54 
     55 (define sum
     56   (lambda (i acc)
     57     (if (= i 490)
     58         acc
     59         (sum (+ i 1) (+ acc (vector-ref v i))))))
     60 
     61 (sum 0 0)