boot2

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

commit 85d00e53c1ad94e6f04fab08306e5127eab626e4
parent 36ea32ef67b7ad67660c6ed4f8170c076c7683cc
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sun, 26 Apr 2026 22:53:12 -0700

cc/cg+parse: block-scope static lives in .data, not on the stack (§I)

Block-scope `static int n = 0;` now routes to cg-emit-global with a
mangled label `cc__<fn>__<n>` so two functions can each have their own
`static int n;` without colliding.

handle-decl gates on `sto = 'static'` *before* the file-vs-block
branch; pre-existing 'static-at-file-scope behavior is unchanged.

cc-cg fixture drives the cg API directly to lay down a counter
incremented across three calls. cc-parse fixture compiles real C
through the full pipeline.

Diffstat:
Atests/cc-cg/57-block-static.expected-exit | 1+
Atests/cc-cg/57-block-static.scm | 45+++++++++++++++++++++++++++++++++++++++++++++
Atests/cc-parse/57-block-static.c | 12++++++++++++
Atests/cc-parse/57-block-static.expected-exit | 1+
4 files changed, 59 insertions(+), 0 deletions(-)

diff --git a/tests/cc-cg/57-block-static.expected-exit b/tests/cc-cg/57-block-static.expected-exit @@ -0,0 +1 @@ +3 diff --git a/tests/cc-cg/57-block-static.scm b/tests/cc-cg/57-block-static.scm @@ -0,0 +1,45 @@ +;; tests/cc-cg/57-block-static.scm — block-scope `static int n;` should +;; live in .bss/.data, not on the stack. §I. +;; +;; Models: +;; int incr(void) { static int n = 0; n = n + 1; return n; } +;; int main(void) { incr(); incr(); return incr(); } (== 3) +;; +;; Two functions could declare `static int n;` independently; mangling +;; with the function name avoids collisions. Here we lay down a +;; pre-mangled cc__incr__n directly via the cg API. + +(let* ((cg (cg-init)) + (n (%sym "n" 'var 'static %t-i32 (string->symbol "cc__incr__n")))) + ;; cg-emit-global expects the slot to be a bv (the emitted label name). + ;; Build n with a string slot directly. + (let* ((nsym (%sym "n" 'var 'static %t-i32 #f)) + (zero (make-bytevector 4 0))) + ;; n is sym-named "n"; cg-mangle-global prefixes with "cc__" → "cc__n". + ;; That collides if multiple fns name `n`; the parser side adds the + ;; fn-name segment via handle-decl. For the cg fixture we just use + ;; the unprefixed sym-name. + (cg-emit-global cg nsym (list zero)) + + ;; int incr(void) { n = n + 1; return n; } + (cg-fn-begin cg "incr" '() %t-i32) + (cg-push-sym cg nsym) ; lhs lval n + (cg-push-sym cg nsym) (cg-load cg) ; rhs n + (cg-push-imm cg %t-i32 1) + (cg-binop cg 'add) + (cg-assign cg) (cg-pop cg) + (cg-push-sym cg nsym) (cg-load cg) + (cg-return cg) + (cg-fn-end cg) + + ;; int main(void) { incr(); incr(); return incr(); } + (cg-fn-begin cg "main" '() %t-i32) + (let ((incr-sym (%sym "incr" 'fn 'extern + (%ctype 'fn -1 -1 (list %t-i32 '() #f)) + "cc__incr"))) + (cg-push-sym cg incr-sym) (cg-call cg 0 #t) (cg-pop cg) + (cg-push-sym cg incr-sym) (cg-call cg 0 #t) (cg-pop cg) + (cg-push-sym cg incr-sym) (cg-call cg 0 #t) + (cg-return cg)) + (cg-fn-end cg)) + (write-bv-fd 1 (cg-finish cg))) diff --git a/tests/cc-parse/57-block-static.c b/tests/cc-parse/57-block-static.c @@ -0,0 +1,12 @@ +// tests/cc-parse/57-block-static.c — block-scope static survives across +// calls. §I. +int incr(void) { + static int n = 0; + n = n + 1; + return n; +} +int main(void) { + incr(); + incr(); + return incr(); +} diff --git a/tests/cc-parse/57-block-static.expected-exit b/tests/cc-parse/57-block-static.expected-exit @@ -0,0 +1 @@ +3