boot2

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

commit d37ddf24e923eb2c2d63d881d7ebe46ea1ba4cdb
parent 632c4d8ec06b73f40521e0d8eca6db45250bee49
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sun, 26 Apr 2026 22:43:05 -0700

cc/cg+parse: int main() falling off end returns 0 (§J.2)

cg-fn-begin now emits a `%li(t0,0); %st(t0,sp,ret-slot)` prologue
write whenever the return type isn't void. Falling through to ::ret
without an explicit `return` now reads back a defined 0 instead of
relying on kernel zero-fill.

Also documents the §J.1 entry stub in cg-finish: P1 delivers
a0=argc, a1=argv, and %call leaves them untouched, so the stub just
falls through to cc__main.

Diffstat:
Mcc/cg.scm | 18++++++++++++++++--
Atests/cc-parse/68-main-noret.c | 3+++
Atests/cc-parse/68-main-noret.expected-exit | 1+
3 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/cc/cg.scm b/cc/cg.scm @@ -237,8 +237,14 @@ (%cg (make-buf) (make-buf) (make-buf) '() 0 0 '() '() #f #f 0)) (define (cg-finish cg) + ;; Entry stub. P1's program-entry contract (docs/P1.md §Program Entry) + ;; delivers argc in a0 and argv in a1 at p1_main. %call doesn't + ;; clobber a0/a1, so falling straight through to cc__main forwards + ;; them unchanged. The 16-byte frame is just enough for %enter's + ;; saved-fp/lr to fit; cc__main builds its own frame on top. + ;; (CC-CONTRACTS §J.1, §5.4.) (let ((stub (bv-cat (list - "# entry stub\n" + "# entry stub: forwards argc=a0, argv=a1 to cc__main\n" "%fn(p1_main, 16, {\n" "%call(&cc__main)\n" "})\n")))) @@ -261,7 +267,15 @@ (%cg-fn-set! cg '%fn-ret-type return-type) (%cg-fn-set! cg '%indirect-slots '()) (let ((ret-slot (cg-alloc-slot cg 8 8))) - (%cg-fn-set! cg '%fn-ret-slot ret-slot)) + (%cg-fn-set! cg '%fn-ret-slot ret-slot) + ;; Zero-init the ret slot so `int main() { }` (no explicit return) + ;; falls through to ::ret with a defined 0 value (CC.md §J.2). + (cond + ((not (eq? (ctype-kind return-type) 'void)) + (buf-push! (cg-prologue-buf cg) + (bv-cat (list "%li(t0, 0)\n" + "%st(t0, sp, " + (%cg-slot-expr cg ret-slot) ")\n")))))) ;; params per CC-CONTRACTS §3.1: list of (name-bv . ctype). We ;; return an alist (name-bv . sym) the parser binds into scope. (let walk ((ps params) (idx 0) (out '())) diff --git a/tests/cc-parse/68-main-noret.c b/tests/cc-parse/68-main-noret.c @@ -0,0 +1,3 @@ +/* §J.2 — int main() falling off the end returns 0. */ +int main(void) { +} diff --git a/tests/cc-parse/68-main-noret.expected-exit b/tests/cc-parse/68-main-noret.expected-exit @@ -0,0 +1 @@ +0