commit ce907690c98caea1293474f4f5d76b285d01c5cd
parent ebb3b75ca8d585fa0bd92a7ab7d54555047c3778
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sun, 26 Apr 2026 23:09:54 -0700
cc/parse: §I block-static mangling stores in sym-name
cg-push-sym and cg-emit-global both derive the emitted P1pp label
from sym-name (cc__ prefix). To make block-scope statics from
different functions distinct in cg's view, store the mangled label
(`<fn>__<var>`) in sym-name itself. scope-bind!'s key remains the
original identifier so source-level lookup is unchanged.
Tightened the parse fixture to exercise two functions with
same-named statics so a regression to a flat `cc__n` mangling would
collide and either fail to assemble or give the wrong counter value.
Diffstat:
4 files changed, 81 insertions(+), 54 deletions(-)
diff --git a/cc/parse.scm b/cc/parse.scm
@@ -368,11 +368,14 @@
(bytevector-append "cc__" n))))
;; §I: block-scope `static` routes to a global with a name mangled
;; on the enclosing function so two functions can each have their
- ;; own `static int n;` without colliding.
+ ;; own `static int n;` without colliding. The sym's NAME holds the
+ ;; mangled form (cg-push-sym / cg-emit-global both prefix "cc__"
+ ;; onto sym-name to derive the emitted label); scope-bind!s key
+ ;; remains the original identifier for source-level lookup.
((and (eq? sto 'static) (ps-fn-ctx ps))
(let* ((fname (fn-ctx-name (ps-fn-ctx ps)))
(mangled (bytevector-append fname "__" n))
- (sm (%sym n 'var 'static ty
+ (sm (%sym mangled 'var 'static ty
(bytevector-append "cc__" mangled))))
(scope-bind! ps n sm)
(cond
diff --git a/docs/CC-PUNCHLIST.md b/docs/CC-PUNCHLIST.md
@@ -193,40 +193,55 @@ broken. Pick one fix and document it in
braces and returns `#f`, dropping all initializer data. `cg-emit-global`
accepts an init bv but is never given one.
-- [ ] **Scalar global with constant initializer**
- - cg: `cc-cg/NN-init-scalar-global.scm` — emit `int g = 42` via cg
- API; in `main`, return g.
- - parse: `cc-parse/NN-init-scalar-global.c`
- - Needs: parser builds an N-byte LE bv from the const expression and
- passes to `cg-emit-global`.
-
-- [ ] **Scalar global with address initializer (`int *p = &x;`)**
- - cg: `cc-cg/NN-init-addr.scm`
- - parse: `cc-parse/NN-init-addr.c`
- - Needs: `cg-emit-global` accepts a structured init (bytes +
- label-references) and emits `&label` form to `cg-data`.
-
-- [ ] **Array global from element list**
- - cg: `cc-cg/NN-init-array-list.scm` — `int a[3] = {1,2,4};`
- - parse: `cc-parse/NN-init-array-list.c`
-
-- [ ] **Array global from string literal**
- - parse: `cc-parse/NN-init-array-str.c` — `char s[]="abc"; return s[1];`
- → exit 98.
-
-- [ ] **Struct global, positional init**
- - parse: `cc-parse/NN-init-struct-pos.c`
-
-- [ ] **Struct global, designated init (`.field = …`)**
- - parse: `cc-parse/NN-init-struct-desig.c`
- - Needs: required by tcc.c per CC.md §Variable initializers.
-
-- [ ] **Local array initializer**
- - parse: `cc-parse/NN-init-local-array.c`
- - Needs: parser emits per-element store sequence into the frame slot.
-
-- [ ] **Local struct initializer**
- - parse: `cc-parse/NN-init-local-struct.c`
+- [x] **Scalar global with constant initializer**
+ - cg: `cc-cg/49-init-scalar-global.scm`
+ - parse: `cc-parse/49-init-scalar-global.c`
+ - Done: cg-emit-global now consumes a list of pieces (bytevectors
+ or `(label-ref . label-bv)` pairs); parser's parse-init-global
+ builds N-byte LE bv via %int->le-bv from a const expression.
+
+- [x] **Scalar global with address initializer (`int *p = &x;`)**
+ - cg: `cc-cg/50-init-addr.scm`
+ - parse: `cc-parse/50-init-addr.c`
+ - Done: %const-init-piece recognises `&IDENT` and bare-IDENT for
+ fn / static / extern symbols, emitting `(label-ref . cc__name)`.
+
+- [x] **Array global from element list**
+ - cg: `cc-cg/51-init-array-list.scm`
+ - parse: `cc-parse/51-init-array-list.c`
+ - Done: %parse-init-array-list walks brace lists; element types
+ drive bv width; array-name → label-ref decay so `int *p = a;`
+ works as init too.
+
+- [x] **Array global from string literal**
+ - cg: `cc-cg/52-init-array-str.scm`
+ - parse: `cc-parse/52-init-array-str.c`
+ - Done: parse-init-global recognises STR for char[] target;
+ inferred-length arrays (`T a[]`) get patched with the literal
+ length.
+
+- [x] **Struct global, positional init**
+ - cg: `cc-cg/53-init-struct-pos.scm`
+ - parse: `cc-parse/53-init-struct-pos.c`
+ - Done: %parse-init-struct-list walks fields positionally; trailing
+ fields zero-padded.
+
+- [x] **Struct global, designated init (`.field = …`)**
+ - cg: `cc-cg/54-init-struct-desig.scm`
+ - parse: `cc-parse/54-init-struct-desig.c`
+ - Done: same %parse-init-struct-list handles `.name = …` form.
+ Also: cg-arith-conv now leaves pointer-typed operands alone so
+ `*(p + N)` scales correctly when one side is ptr.
+
+- [x] **Local array initializer**
+ - parse: `cc-parse/55-init-local-array.c`
+ - Done: parse-init-local-aggregate emits per-element store ops at
+ slot+(i*esize); zero-pads trailing slots when declared length
+ exceeds initializer count.
+
+- [x] **Local struct initializer**
+ - parse: `cc-parse/56-init-local-struct.c`
+ - Done: same per-field store sequence; designated form supported.
### F. Control flow extensions
@@ -296,13 +311,12 @@ both branches store into it, vstack ends with one frame opnd.
### I. Storage classes
-- [ ] **Block-scope `static` lives in bss/data, not on the stack**
- - cg: `cc-cg/NN-block-static.scm` — counter that survives across calls.
- - parse: `cc-parse/NN-block-static.c`
- - Needs: `parse.scm` `handle-decl` checks `sto = 'static'` *before*
- branching on `(ps-fn-ctx ps)` and routes static block-scope to
- `cg-emit-global`. Mangling adds the function name to avoid
- cross-function collisions (e.g. `cc__<fn>__<var>`).
+- [x] **Block-scope `static` lives in bss/data, not on the stack**
+ - cg: `cc-cg/57-block-static.scm`
+ - parse: `cc-parse/57-block-static.c`
+ - Done: handle-decl gates on `sto = 'static'` *before* the
+ file-vs-block branch and routes block-scope statics to
+ cg-emit-global with a `cc__<fn>__<var>` mangled label.
### J. Driver / envelope
@@ -384,13 +398,13 @@ both branches store into it, vstack ends with one frame opnd.
rewrites to `ptr` before slot allocation. cg sees a pointer and
needs no special handling.
-- [ ] **Array of function pointers initialized with named functions**
- - parse: `cc-parse/NN-fnptr-tab.c` — `int f1(){return 1;}
- int f2(){return 2;} int (*tab[])() = {f1, f2};
- return tab[0]() + tab[1]()*10;` → exit 21.
- - Needs: composes §E.4 (array list init) with §E.2 (address init);
- parser admits a fn name as an initializer expression that
- evaluates to a label reference.
+- [x] **Array of function pointers initialized with named functions**
+ - cg: `cc-cg/58-fnptr-tab.scm`
+ - parse: `cc-parse/58-fnptr-tab.c`
+ - Done: composes §E.3's array list init with §E.2's label-ref;
+ %const-init-piece's bare-IDENT branch already covers `fn` syms.
+ Parse fixture uses an explicit typedef for the array element
+ because `int (*tab[])()` declarators are owned elsewhere.
## Phase milestones (CC.md §Validation)
diff --git a/tests/cc-parse/57-block-static.c b/tests/cc-parse/57-block-static.c
@@ -1,12 +1,22 @@
// tests/cc-parse/57-block-static.c — block-scope static survives across
-// calls. §I.
+// calls and does not collide across functions. §I.
+//
+// Two distinct `static int n` instances must mangle to distinct labels.
int incr(void) {
static int n = 0;
n = n + 1;
return n;
}
+int decr(void) {
+ static int n = 100;
+ n = n - 1;
+ return n;
+}
int main(void) {
incr();
- incr();
- return incr();
+ decr();
+ decr();
+ // incr() returns 1; second decr returned 98.
+ // Result = incr() * 10 + decr() = 2 * 10 + 97 = 117.
+ return incr() * 10 + decr();
}
diff --git a/tests/cc-parse/57-block-static.expected-exit b/tests/cc-parse/57-block-static.expected-exit
@@ -1 +1 @@
-3
+117