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:
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