boot2

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

commit 64fe1f05c2f3c3f5bb2bd94d993f909fd8a20a88
parent c13c79ba665e09e3a39490cb97ba27698904d1bb
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon, 27 Apr 2026 10:46:09 -0700

cc: merge parse+e2e tests into tests/cc/

Diffstat:
MMakefile | 25++++++++++++-------------
Mcc/README.md | 12++++++------
Mdocs/CC-CONTRACTS.md | 21++++++++++-----------
Ddocs/CC-HEAP-REWIND.md | 139-------------------------------------------------------------------------------
Mdocs/CC-INTERNALS.md | 25+++++++++++--------------
Ddocs/CC-KITCHEN-SINK-PUNCHLIST.md | 268-------------------------------------------------------------------------------
Mdocs/CC-PUNCHLIST.md | 126+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mscripts/boot-run-tests.sh | 34++++++++++++++--------------------
Mscripts/run-tests.sh | 7+++----
Dtests/cc-parse/_run-parse.scm | 46----------------------------------------------
Rtests/cc-parse/00-empty-main.c -> tests/cc/00-empty-main.c | 0
Rtests/cc-e2e/01-kitchen-sink.expected-exit -> tests/cc/00-empty-main.expected-exit | 0
Rtests/cc-e2e/00-return-argc.c -> tests/cc/00-return-argc.c | 0
Rtests/cc-e2e/00-return-argc.expected-exit -> tests/cc/00-return-argc.expected-exit | 0
Rtests/cc-e2e/01-kitchen-sink.c -> tests/cc/01-kitchen-sink.c | 0
Rtests/cc-e2e/02-arith.expected-exit -> tests/cc/01-kitchen-sink.expected-exit | 0
Rtests/cc-parse/01-return-argc.c -> tests/cc/01-return-argc.c | 0
Rtests/cc-parse/01-return-argc.expected-exit -> tests/cc/01-return-argc.expected-exit | 0
Rtests/cc-parse/02-add-const.c -> tests/cc/02-add-const.c | 0
Rtests/cc-parse/02-add-const.expected-exit -> tests/cc/02-add-const.expected-exit | 0
Rtests/cc-e2e/02-arith.c -> tests/cc/02-arith.c | 0
Rtests/cc-e2e/03-compound.expected-exit -> tests/cc/02-arith.expected-exit | 0
Rtests/cc-e2e/03-compound.c -> tests/cc/03-compound.c | 0
Rtests/cc-e2e/04-inc-dec.expected-exit -> tests/cc/03-compound.expected-exit | 0
Rtests/cc-parse/03-local-assign.c -> tests/cc/03-local-assign.c | 0
Rtests/cc-parse/03-local-assign.expected-exit -> tests/cc/03-local-assign.expected-exit | 0
Rtests/cc-parse/04-if-else.c -> tests/cc/04-if-else.c | 0
Rtests/cc-parse/04-if-else.expected-exit -> tests/cc/04-if-else.expected-exit | 0
Rtests/cc-e2e/04-inc-dec.c -> tests/cc/04-inc-dec.c | 0
Rtests/cc-e2e/05-logical.expected-exit -> tests/cc/04-inc-dec.expected-exit | 0
Rtests/cc-e2e/05-logical.c -> tests/cc/05-logical.c | 0
Rtests/cc-e2e/06-ternary.expected-exit -> tests/cc/05-logical.expected-exit | 0
Rtests/cc-parse/05-while-break.c -> tests/cc/05-while-break.c | 0
Rtests/cc-parse/05-while-break.expected-exit -> tests/cc/05-while-break.expected-exit | 0
Rtests/cc-parse/06-call-no-args.c -> tests/cc/06-call-no-args.c | 0
Rtests/cc-parse/06-call-no-args.expected-exit -> tests/cc/06-call-no-args.expected-exit | 0
Rtests/cc-e2e/06-ternary.c -> tests/cc/06-ternary.c | 0
Rtests/cc-e2e/07-compare.expected-exit -> tests/cc/06-ternary.expected-exit | 0
Rtests/cc-parse/07-call-with-args.c -> tests/cc/07-call-with-args.c | 0
Rtests/cc-parse/07-call-with-args.expected-exit -> tests/cc/07-call-with-args.expected-exit | 0
Rtests/cc-e2e/07-compare.c -> tests/cc/07-compare.c | 0
Rtests/cc-e2e/08-pointer.expected-exit -> tests/cc/07-compare.expected-exit | 0
Rtests/cc-parse/08-pointer-deref.c -> tests/cc/08-pointer-deref.c | 0
Rtests/cc-parse/08-pointer-deref.expected-exit -> tests/cc/08-pointer-deref.expected-exit | 0
Rtests/cc-e2e/08-pointer.c -> tests/cc/08-pointer.c | 0
Rtests/cc-e2e/09-array.expected-exit -> tests/cc/08-pointer.expected-exit | 0
Rtests/cc-parse/09-address-of.c -> tests/cc/09-address-of.c | 0
Rtests/cc-parse/09-address-of.expected-exit -> tests/cc/09-address-of.expected-exit | 0
Rtests/cc-e2e/09-array.c -> tests/cc/09-array.c | 0
Rtests/cc-e2e/10-array-2d.expected-exit -> tests/cc/09-array.expected-exit | 0
Rtests/cc-e2e/10-array-2d.c -> tests/cc/10-array-2d.c | 0
Rtests/cc-e2e/11-struct.expected-exit -> tests/cc/10-array-2d.expected-exit | 0
Rtests/cc-parse/10-typedef.c -> tests/cc/10-typedef.c | 0
Rtests/cc-parse/10-typedef.expected-exit -> tests/cc/10-typedef.expected-exit | 0
Rtests/cc-e2e/11-struct.c -> tests/cc/11-struct.c | 0
Rtests/cc-e2e/12-struct-ptr.expected-exit -> tests/cc/11-struct.expected-exit | 0
Rtests/cc-parse/11-two-params.c -> tests/cc/11-two-params.c | 0
Rtests/cc-parse/11-two-params.expected-exit -> tests/cc/11-two-params.expected-exit | 0
Rtests/cc-parse/12-comparison.c -> tests/cc/12-comparison.c | 0
Rtests/cc-parse/12-comparison.expected-exit -> tests/cc/12-comparison.expected-exit | 0
Rtests/cc-e2e/12-struct-ptr.c -> tests/cc/12-struct-ptr.c | 0
Rtests/cc-e2e/13-call.expected-exit -> tests/cc/12-struct-ptr.expected-exit | 0
Rtests/cc-e2e/13-call.c -> tests/cc/13-call.c | 0
Rtests/cc-e2e/14-recursion.expected-exit -> tests/cc/13-call.expected-exit | 0
Rtests/cc-parse/13-while-continue.c -> tests/cc/13-while-continue.c | 0
Rtests/cc-parse/13-while-continue.expected-exit -> tests/cc/13-while-continue.expected-exit | 0
Rtests/cc-parse/14-mul-paren.c -> tests/cc/14-mul-paren.c | 0
Rtests/cc-parse/14-mul-paren.expected-exit -> tests/cc/14-mul-paren.expected-exit | 0
Rtests/cc-e2e/14-recursion.c -> tests/cc/14-recursion.c | 0
Rtests/cc-e2e/15-variadic.expected-exit -> tests/cc/14-recursion.expected-exit | 0
Rtests/cc-parse/15-char-arith.c -> tests/cc/15-char-arith.c | 0
Rtests/cc-parse/15-char-arith.expected-exit -> tests/cc/15-char-arith.expected-exit | 0
Rtests/cc-e2e/15-variadic.c -> tests/cc/15-variadic.c | 0
Rtests/cc-e2e/16-fn-ptr.expected-exit -> tests/cc/15-variadic.expected-exit | 0
Rtests/cc-e2e/16-fn-ptr.c -> tests/cc/16-fn-ptr.c | 0
Rtests/cc-e2e/17-apply.expected-exit -> tests/cc/16-fn-ptr.expected-exit | 0
Rtests/cc-parse/16-short-arith.c -> tests/cc/16-short-arith.c | 0
Rtests/cc-parse/16-short-arith.expected-exit -> tests/cc/16-short-arith.expected-exit | 0
Rtests/cc-e2e/17-apply.c -> tests/cc/17-apply.c | 0
Rtests/cc-e2e/18-ptr-recursion.expected-exit -> tests/cc/17-apply.expected-exit | 0
Rtests/cc-parse/17-int-arith.c -> tests/cc/17-int-arith.c | 0
Rtests/cc-parse/17-int-arith.expected-exit -> tests/cc/17-int-arith.expected-exit | 0
Rtests/cc-e2e/18-ptr-recursion.c -> tests/cc/18-ptr-recursion.c | 0
Rtests/cc-e2e/19-static.expected-exit -> tests/cc/18-ptr-recursion.expected-exit | 0
Rtests/cc-parse/18-sext-narrow.c -> tests/cc/18-sext-narrow.c | 0
Rtests/cc-parse/18-sext-narrow.expected-exit -> tests/cc/18-sext-narrow.expected-exit | 0
Rtests/cc-e2e/19-static.c -> tests/cc/19-static.c | 0
Rtests/cc-e2e/20-switch.expected-exit -> tests/cc/19-static.expected-exit | 0
Rtests/cc-parse/19-zext-narrow.c -> tests/cc/19-zext-narrow.c | 0
Rtests/cc-parse/19-zext-narrow.expected-exit -> tests/cc/19-zext-narrow.expected-exit | 0
Rtests/cc-parse/20-promote-sign.c -> tests/cc/20-promote-sign.c | 0
Rtests/cc-parse/20-promote-sign.expected-exit -> tests/cc/20-promote-sign.expected-exit | 0
Rtests/cc-e2e/20-switch.c -> tests/cc/20-switch.c | 0
Rtests/cc-e2e/21-goto.expected-exit -> tests/cc/20-switch.expected-exit | 0
Rtests/cc-e2e/21-goto.c -> tests/cc/21-goto.c | 0
Rtests/cc-e2e/22-loops.expected-exit -> tests/cc/21-goto.expected-exit | 0
Rtests/cc-parse/21-preinc.c -> tests/cc/21-preinc.c | 0
Rtests/cc-parse/21-preinc.expected-exit -> tests/cc/21-preinc.expected-exit | 0
Rtests/cc-e2e/22-loops.c -> tests/cc/22-loops.c | 0
Rtests/cc-e2e/23-strings.expected-exit -> tests/cc/22-loops.expected-exit | 0
Rtests/cc-parse/22-postinc.c -> tests/cc/22-postinc.c | 0
Rtests/cc-parse/22-postinc.expected-exit -> tests/cc/22-postinc.expected-exit | 0
Rtests/cc-parse/23-cmpd-simple.c -> tests/cc/23-cmpd-simple.c | 0
Rtests/cc-parse/23-cmpd-simple.expected-exit -> tests/cc/23-cmpd-simple.expected-exit | 0
Rtests/cc-e2e/23-strings.c -> tests/cc/23-strings.c | 0
Rtests/cc-e2e/24-globals.expected-exit -> tests/cc/23-strings.expected-exit | 0
Rtests/cc-parse/24-cmpd-ptr.c -> tests/cc/24-cmpd-ptr.c | 0
Rtests/cc-parse/24-cmpd-ptr.expected-exit -> tests/cc/24-cmpd-ptr.expected-exit | 0
Rtests/cc-e2e/24-globals.c -> tests/cc/24-globals.c | 0
Rtests/cc-e2e/25-sizeof.expected-exit -> tests/cc/24-globals.expected-exit | 0
Rtests/cc-parse/25-deref-postinc.c -> tests/cc/25-deref-postinc.c | 0
Rtests/cc-parse/25-deref-postinc.expected-exit -> tests/cc/25-deref-postinc.expected-exit | 0
Rtests/cc-e2e/25-sizeof.c -> tests/cc/25-sizeof.c | 0
Rtests/cc-e2e/26-enum.expected-exit -> tests/cc/25-sizeof.expected-exit | 0
Rtests/cc-e2e/26-enum.c -> tests/cc/26-enum.c | 0
Rtests/cc-e2e/27-void-call.expected-exit -> tests/cc/26-enum.expected-exit | 0
Rtests/cc-parse/26-sizeof-expr.c -> tests/cc/26-sizeof-expr.c | 0
Rtests/cc-parse/26-sizeof-expr.expected-exit -> tests/cc/26-sizeof-expr.expected-exit | 0
Rtests/cc-parse/27-sizeof-types.c -> tests/cc/27-sizeof-types.c | 0
Rtests/cc-parse/27-sizeof-types.expected-exit -> tests/cc/27-sizeof-types.expected-exit | 0
Rtests/cc-e2e/27-void-call.c -> tests/cc/27-void-call.c | 0
Rtests/cc-e2e/28-cast.expected-exit -> tests/cc/27-void-call.expected-exit | 0
Rtests/cc-e2e/28-cast.c -> tests/cc/28-cast.c | 0
Rtests/cc-e2e/29-void-ptr.expected-exit -> tests/cc/28-cast.expected-exit | 0
Rtests/cc-parse/28-ternary.c -> tests/cc/28-ternary.c | 0
Rtests/cc-parse/28-ternary.expected-exit -> tests/cc/28-ternary.expected-exit | 0
Rtests/cc-parse/29-land.c -> tests/cc/29-land.c | 0
Rtests/cc-parse/29-land.expected-exit -> tests/cc/29-land.expected-exit | 0
Rtests/cc-e2e/29-void-ptr.c -> tests/cc/29-void-ptr.c | 0
Rtests/cc-e2e/30-comma.expected-exit -> tests/cc/29-void-ptr.expected-exit | 0
Rtests/cc-e2e/30-comma.c -> tests/cc/30-comma.c | 0
Rtests/cc-e2e/31-addr-array.expected-exit -> tests/cc/30-comma.expected-exit | 0
Rtests/cc-parse/30-lor.c -> tests/cc/30-lor.c | 0
Rtests/cc-parse/30-lor.expected-exit -> tests/cc/30-lor.expected-exit | 0
Rtests/cc-e2e/31-addr-array.c -> tests/cc/31-addr-array.c | 0
Rtests/cc-e2e/32-local-struct-desig.expected-exit -> tests/cc/31-addr-array.expected-exit | 0
Rtests/cc-parse/31-comma.c -> tests/cc/31-comma.c | 0
Rtests/cc-parse/31-comma.expected-exit -> tests/cc/31-comma.expected-exit | 0
Rtests/cc-e2e/32-local-struct-desig.c -> tests/cc/32-local-struct-desig.c | 0
Rtests/cc-parse/00-empty-main.expected-exit -> tests/cc/32-local-struct-desig.expected-exit | 0
Rtests/cc-parse/36-struct-load.c -> tests/cc/36-struct-load.c | 0
Rtests/cc-parse/36-struct-load.expected-exit -> tests/cc/36-struct-load.expected-exit | 0
Rtests/cc-parse/37-struct-store.c -> tests/cc/37-struct-store.c | 0
Rtests/cc-parse/37-struct-store.expected-exit -> tests/cc/37-struct-store.expected-exit | 0
Rtests/cc-parse/38-arrow.c -> tests/cc/38-arrow.c | 0
Rtests/cc-parse/38-arrow.expected-exit -> tests/cc/38-arrow.expected-exit | 0
Rtests/cc-parse/39-struct-nested.c -> tests/cc/39-struct-nested.c | 0
Rtests/cc-parse/39-struct-nested.expected-exit -> tests/cc/39-struct-nested.expected-exit | 0
Rtests/cc-parse/40-array-index.c -> tests/cc/40-array-index.c | 0
Rtests/cc-parse/40-array-index.expected-exit -> tests/cc/40-array-index.expected-exit | 0
Rtests/cc-parse/41-array-2d.c -> tests/cc/41-array-2d.c | 0
Rtests/cc-parse/41-array-2d.expected-exit -> tests/cc/41-array-2d.expected-exit | 0
Rtests/cc-parse/42-struct-fn-arg.c -> tests/cc/42-struct-fn-arg.c | 0
Rtests/cc-parse/42-struct-fn-arg.expected-exit -> tests/cc/42-struct-fn-arg.expected-exit | 0
Rtests/cc-parse/43-array-param-decay.c -> tests/cc/43-array-param-decay.c | 0
Rtests/cc-parse/43-array-param-decay.expected-exit -> tests/cc/43-array-param-decay.expected-exit | 0
Rtests/cc-parse/49-init-scalar-global.c -> tests/cc/49-init-scalar-global.c | 0
Rtests/cc-parse/49-init-scalar-global.expected-exit -> tests/cc/49-init-scalar-global.expected-exit | 0
Rtests/cc-parse/50-init-addr.c -> tests/cc/50-init-addr.c | 0
Rtests/cc-parse/50-init-addr.expected-exit -> tests/cc/50-init-addr.expected-exit | 0
Rtests/cc-parse/51-init-array-list.c -> tests/cc/51-init-array-list.c | 0
Rtests/cc-parse/51-init-array-list.expected-exit -> tests/cc/51-init-array-list.expected-exit | 0
Rtests/cc-parse/52-init-array-str.c -> tests/cc/52-init-array-str.c | 0
Rtests/cc-parse/52-init-array-str.expected-exit -> tests/cc/52-init-array-str.expected-exit | 0
Rtests/cc-parse/53-init-struct-pos.c -> tests/cc/53-init-struct-pos.c | 0
Rtests/cc-parse/53-init-struct-pos.expected-exit -> tests/cc/53-init-struct-pos.expected-exit | 0
Rtests/cc-parse/54-init-struct-desig.c -> tests/cc/54-init-struct-desig.c | 0
Rtests/cc-parse/54-init-struct-desig.expected-exit -> tests/cc/54-init-struct-desig.expected-exit | 0
Rtests/cc-parse/55-init-local-array.c -> tests/cc/55-init-local-array.c | 0
Rtests/cc-parse/55-init-local-array.expected-exit -> tests/cc/55-init-local-array.expected-exit | 0
Rtests/cc-parse/56-init-local-struct.c -> tests/cc/56-init-local-struct.c | 0
Rtests/cc-parse/56-init-local-struct.expected-exit -> tests/cc/56-init-local-struct.expected-exit | 0
Rtests/cc-parse/57-block-static.c -> tests/cc/57-block-static.c | 0
Rtests/cc-parse/57-block-static.expected-exit -> tests/cc/57-block-static.expected-exit | 0
Rtests/cc-parse/58-fnptr-tab.c -> tests/cc/58-fnptr-tab.c | 0
Rtests/cc-parse/58-fnptr-tab.expected-exit -> tests/cc/58-fnptr-tab.expected-exit | 0
Rtests/cc-parse/63-do-while.c -> tests/cc/63-do-while.c | 0
Rtests/cc-parse/63-do-while.expected-exit -> tests/cc/63-do-while.expected-exit | 0
Rtests/cc-parse/64-for-decl.c -> tests/cc/64-for-decl.c | 0
Rtests/cc-parse/64-for-decl.expected-exit -> tests/cc/64-for-decl.expected-exit | 0
Rtests/cc-parse/65-switch.c -> tests/cc/65-switch.c | 0
Rtests/cc-parse/65-switch.expected-exit -> tests/cc/65-switch.expected-exit | 0
Rtests/cc-parse/66-goto.c -> tests/cc/66-goto.c | 0
Rtests/cc-parse/66-goto.expected-exit -> tests/cc/66-goto.expected-exit | 0
Rtests/cc-parse/67-vararg-call.c -> tests/cc/67-vararg-call.c | 0
Rtests/cc-parse/67-vararg-call.expected-exit -> tests/cc/67-vararg-call.expected-exit | 0
Rtests/cc-parse/68-main-noret.c -> tests/cc/68-main-noret.c | 0
Rtests/cc-parse/68-main-noret.expected-exit -> tests/cc/68-main-noret.expected-exit | 0
Rtests/cc-parse/69-multi-fn.c -> tests/cc/69-multi-fn.c | 0
Rtests/cc-parse/69-multi-fn.expected-exit -> tests/cc/69-multi-fn.expected-exit | 0
Rtests/cc-parse/71-fnptr-call.c -> tests/cc/71-fnptr-call.c | 0
Rtests/cc-parse/71-fnptr-call.expected-exit -> tests/cc/71-fnptr-call.expected-exit | 0
Rtests/cc-parse/72-enum-const.c -> tests/cc/72-enum-const.c | 0
Rtests/cc-parse/72-enum-const.expected-exit -> tests/cc/72-enum-const.expected-exit | 0
Rtests/cc-parse/73-voidptr-impl.c -> tests/cc/73-voidptr-impl.c | 0
Rtests/cc-parse/73-voidptr-impl.expected-exit -> tests/cc/73-voidptr-impl.expected-exit | 0
Rtests/cc-parse/74-call-narrow.c -> tests/cc/74-call-narrow.c | 0
Rtests/cc-parse/74-call-narrow.expected-exit -> tests/cc/74-call-narrow.expected-exit | 0
Rtests/cc-parse/75-ptr-cmp.c -> tests/cc/75-ptr-cmp.c | 0
Rtests/cc-parse/75-ptr-cmp.expected-exit -> tests/cc/75-ptr-cmp.expected-exit | 0
Rtests/cc-parse/76-vararg-recv.c -> tests/cc/76-vararg-recv.c | 0
Rtests/cc-parse/76-vararg-recv.expected-exit -> tests/cc/76-vararg-recv.expected-exit | 0
Rtests/cc-parse/77-flex-array.c -> tests/cc/77-flex-array.c | 0
Rtests/cc-parse/77-flex-array.expected-exit -> tests/cc/77-flex-array.expected-exit | 0
Rtests/cc-parse/78-voidptr-arith.c -> tests/cc/78-voidptr-arith.c | 0
Rtests/cc-parse/78-voidptr-arith.expected-exit -> tests/cc/78-voidptr-arith.expected-exit | 0
Rtests/cc-parse/79-vararg-deep.c -> tests/cc/79-vararg-deep.c | 0
Rtests/cc-parse/79-vararg-deep.expected-exit -> tests/cc/79-vararg-deep.expected-exit | 0
Rtests/cc-parse/80-addr-array.c -> tests/cc/80-addr-array.c | 0
Rtests/cc-parse/80-addr-array.expected-exit -> tests/cc/80-addr-array.expected-exit | 0
Rtests/cc-parse/81-void-call.c -> tests/cc/81-void-call.c | 0
Rtests/cc-parse/81-void-call.expected-exit -> tests/cc/81-void-call.expected-exit | 0
212 files changed, 118 insertions(+), 585 deletions(-)

diff --git a/Makefile b/Makefile @@ -255,15 +255,15 @@ TEST_SCHEME1_DEPS := $(foreach a,$(TEST_ARCHES), \ build/$(a)/.image build/$(a)/tools/M0 build/$(a)/m1pp build/$(a)/scheme1) # cc-* suites: scheme1 + m1pp cover everything. cc-util / cc-lex / -# cc-pp byte-diff their pure transformations; cc-cg / cc-parse / -# cc-e2e compile the emitted P1pp through the P1pp toolchain (which -# m1pp drives) and run the resulting ELF. cc.scm is only needed by -# cc-e2e (it invokes the catm'd compiler against a .c fixture); the -# rest catm their own per-suite layer list. +# cc-pp byte-diff their pure transformations; cc-cg / cc compile the +# emitted P1pp through the P1pp toolchain (which m1pp drives) and run +# the resulting ELF. cc.scm is only needed by the cc suite (it invokes +# the catm'd compiler against a .c fixture); the rest catm their own +# per-suite layer list. TEST_CC_UNIT_DEPS := $(foreach a,$(TEST_ARCHES), \ build/$(a)/.image build/$(a)/tools/M0 build/$(a)/m1pp build/$(a)/scheme1) -TEST_CC_E2E_DEPS := $(TEST_CC_UNIT_DEPS) \ +TEST_CC_DEPS := $(TEST_CC_UNIT_DEPS) \ $(foreach a,$(TEST_ARCHES),build/$(a)/cc/cc.scm) test: @@ -275,8 +275,7 @@ ifeq ($(SUITE),) @$(MAKE) --no-print-directory test SUITE=cc-lex @$(MAKE) --no-print-directory test SUITE=cc-pp @$(MAKE) --no-print-directory test SUITE=cc-cg - @$(MAKE) --no-print-directory test SUITE=cc-parse - @$(MAKE) --no-print-directory test SUITE=cc-e2e + @$(MAKE) --no-print-directory test SUITE=cc else ifeq ($(SUITE),m1pp) @$(MAKE) --no-print-directory $(TEST_M1PP_DEPS) sh scripts/run-tests.sh --suite=m1pp $(if $(ARCH_FILTER),--arch=$(ARCH_FILTER)) @@ -286,12 +285,12 @@ else ifeq ($(SUITE),p1) else ifeq ($(SUITE),scheme1) @$(MAKE) --no-print-directory $(TEST_SCHEME1_DEPS) sh scripts/run-tests.sh --suite=scheme1 $(if $(ARCH_FILTER),--arch=$(ARCH_FILTER)) -else ifeq ($(filter $(SUITE),cc-util cc-lex cc-pp cc-cg cc-parse),$(SUITE)) +else ifeq ($(filter $(SUITE),cc-util cc-lex cc-pp cc-cg),$(SUITE)) @$(MAKE) --no-print-directory $(TEST_CC_UNIT_DEPS) sh scripts/run-tests.sh --suite=$(SUITE) $(if $(ARCH_FILTER),--arch=$(ARCH_FILTER)) -else ifeq ($(SUITE),cc-e2e) - @$(MAKE) --no-print-directory $(TEST_CC_E2E_DEPS) - sh scripts/run-tests.sh --suite=cc-e2e $(if $(ARCH_FILTER),--arch=$(ARCH_FILTER)) +else ifeq ($(SUITE),cc) + @$(MAKE) --no-print-directory $(TEST_CC_DEPS) + sh scripts/run-tests.sh --suite=cc $(if $(ARCH_FILTER),--arch=$(ARCH_FILTER)) else - @echo "unknown SUITE='$(SUITE)' (m1pp | p1 | scheme1 | cc-util | cc-lex | cc-pp | cc-cg | cc-parse | cc-e2e)" >&2; exit 2 + @echo "unknown SUITE='$(SUITE)' (m1pp | p1 | scheme1 | cc-util | cc-lex | cc-pp | cc-cg | cc)" >&2; exit 2 endif diff --git a/cc/README.md b/cc/README.md @@ -40,8 +40,9 @@ Run via the existing `boot-run-scheme1.sh` wrapper, which prepends ## Status Modules filled in past the scaffold; parser drives all six cg sections -and cc-e2e has its phase-1 milestone fixture (`tests/cc-e2e/00-return-argc.c`). -The cg punch list (per `docs/CC-INTERNALS.md`) is the active edge. +and the cc suite has its phase-1 milestone fixture +(`tests/cc/00-return-argc.c`). The cg punch list (per +`docs/CC-INTERNALS.md`) is the active edge. ## Workflow @@ -50,10 +51,10 @@ change lands in this order: 1. cc-cg fixture (red). 2. cg implementation (green). -3. cc-parse fixture (red). +3. cc fixture (red). 4. parse implementation (green). -Both cc-cg and cc-parse are runtime-validating: the harness builds the +Both cc-cg and cc are runtime-validating: the harness builds the emitted P1pp into a real ELF, runs it, and asserts exit code / stdout. No P1pp-text goldens. @@ -63,5 +64,4 @@ No P1pp-text goldens. - `tests/cc-lex/` — lexer token-stream goldens - `tests/cc-pp/` — preprocessor token-stream goldens - `tests/cc-cg/` — direct cg API → ELF → run; assert runtime -- `tests/cc-parse/` — full pipeline via .c → ELF → run; assert runtime -- `tests/cc-e2e/` — same shape as cc-parse for full-envelope fixtures +- `tests/cc/` — full pipeline via .c → ELF → run; assert runtime diff --git a/docs/CC-CONTRACTS.md b/docs/CC-CONTRACTS.md @@ -166,7 +166,7 @@ Two test shapes coexist: - **Pure-transformation suites** (`cc-lex`, `cc-pp`) byte-diff a Scheme-readable serialization (§2.1). -- **Codegen / language suites** (`cc-cg`, `cc-parse`, `cc-e2e`) +- **Codegen / language suites** (`cc-cg`, `cc`) compile-and-run the emitted program and assert runtime behavior (§2.2). No P1pp-text goldens. @@ -210,7 +210,7 @@ Trailing whitespace and `;`-comments in the golden file are ignored. ### 2.2 Runtime fixture format -Every cc-cg / cc-parse / cc-e2e fixture compiles to a runnable program +Every cc-cg / cc fixture compiles to a runnable program whose runtime behavior is the assertion. Two sibling files describe the expectation: @@ -228,12 +228,11 @@ Fixture-source conventions: `(write-bv-fd 1 (cg-finish cg))`. The fixture must define every symbol it references — `cg-call`s into externs are out of scope for this suite (no libc linkage). -- **cc-parse** (`<name>.c`): a complete C translation unit including a - `main` that returns the asserted value. The full lex+pp+parse+cg - pipeline runs against it. -- **cc-e2e** (`<name>.c`): same shape as cc-parse; reserved for - fixtures that exercise the full toolchain envelope (e.g. multi-TU - pre-flatten, libc linkage) once those land. +- **cc** (`<name>.c`): a complete C translation unit including a + `main` that returns the asserted value. The full compiler driver + (`cc/cc.scm`: lex+pp+parse+cg) runs against it. Holds both + feature-targeted drills and full-envelope scenarios; once multi-TU + / libc linkage land, those fixtures live here too. Negative tests (compiler is supposed to die) set `expected-exit` to a non-zero value and may rely on the diagnostic-prefix check in §2.3 @@ -533,12 +532,12 @@ through: - **e2e**: link with arch backend + libp1pp; run as native ELF; exit code matches argc. -Acceptance test: `tests/cc-e2e/00-return-argc.c` exists, the make +Acceptance test: `tests/cc/00-return-argc.c` exists, the make target builds it, and: ``` -$ ./tests/cc-e2e/build/00-return-argc ; echo $? → 1 -$ ./tests/cc-e2e/build/00-return-argc a b ; echo $? → 3 +$ ./tests/cc/build/00-return-argc ; echo $? → 1 +$ ./tests/cc/build/00-return-argc a b ; echo $? → 3 ``` When this test passes on aarch64, amd64, and riscv64, phase 1 is diff --git a/docs/CC-HEAP-REWIND.md b/docs/CC-HEAP-REWIND.md @@ -1,139 +0,0 @@ -# cc heap-mark/rewind refactor - -Applies the A→B→C arena pattern from -`tests/scheme1/93-heap-mark-rewind.scm` to cut intermediate heap usage in -parse + cg. - -## Summary of changes - -### `cc/util.scm` — `buf` is now fixed-size byte storage - -- `buf` record: `(storage offset cap)`. `storage` is a single bytevector - allocated at construction; `offset` advances on each push; `cap` is the - bound. No chunks list, no per-push cons cell. -- API: - - `make-buf` / `make-buf/cap N` — allocate. - - `buf-push! b bv` — `bytevector-copy!` `bv`'s bytes into `storage[offset..]`. - - `buf-flush b` — slice `[0, offset)` as a fresh bv. - - `buf-reset! b` — set `offset` to 0 (reuse storage). - - `buf-drain! dst src` — copy `src`'s used bytes into `dst`, reset `src`. -- Caps live in one block at the top of the buf section. Each is `2^k − 1` - to defeat scheme1's `bv_capacity_for` (rounds up to the smallest power - of two **strictly greater** — asking for `2^k` consumes `2^(k+1)`). - Total fixed pre-allocation ≈ 12.27 MiB. - -### `cc/data.scm` — split cg state - -- `cg-globals` is now **user-visible globals only** (the things - `cg-emit-global` / `cg-emit-extern` add). Identity-stable except when - user code adds a global — exactly the signal `parse-fn-body`'s - rewind-safety check needs. -- `cg-fn-meta` (new) holds **transient per-fn state**: `%fn-name`, - `%fn-ret-slot`, `%fn-ret-type`, `%fn-vararg-first-slot`, - `%indirect-slots`, switch-case lists. Reset on every `cg-fn-begin/v`. -- `cg-in-fn?` (new) discriminates "currently inside a function body" - for `%cg-emit-buf`'s dispatch (fn-buf vs cg-text). - -### `cc/cg.scm` — pre-allocated bufs, no per-fn `bv-cat` - -- `cg-init` pre-allocates **all five** bufs (`text`, `data`, `bss`, - `fn-buf`, `prologue-buf`) once. -- `cg-fn-begin/v` resets fn-buf/prologue-buf via `buf-reset!` (no - allocation) and resets `cg-fn-meta` to `'()`. -- `cg-fn-end` writes header bytes directly into `cg-text` via - `buf-push!`, then drains `prologue-buf` and `fn-buf` into `cg-text` - via `buf-drain!`. **No fn-block bv-cat.** -- `%cg-fn-set!` / `%cg-fn-get` now read/write `cg-fn-meta` instead of - piggy-backing on `cg-globals`. `cg-switch-case` and `cg-switch-end` - updated to match. - -### `cc/parse.scm` — A→B→C around per-function parse - -- `parse-fn-body` is the arena boundary (B): - 1. Hoist the recursive scope-bind! out so the fn's outer-scope entry - is **pre-mark** (survives rewind). - 2. Snapshot identities of `cg-globals` / `cg-str-pool` / `ps-typedefs` - / `ps-tags`; take `(heap-mark)`. - 3. Call `%parse-fn-body-inner` (C) — does the real parse + cg work, - mutating bufs by byte writes only. - 4. If all four snapshots are still `eq?`, clear `cg-fn-meta` (drops - dangling alist before rewind) and `(heap-rewind! mark)`. - 5. Otherwise (the body added a string literal / block-static / typedef - / tag), keep allocations — pay full heap cost only for these fns. - -### Net effect - -Per-function scratch (vstack opnds, intermediate bvs, ctype scratch, -scope frames, switch-case alists, fn-meta) is reclaimed cleanly between -functions. Rewind verified firing on every fn in `00-return-argc` and -`01-kitchen-sink`. - -## kitchen-sink test failure (resolved) - -The kitchen-sink runtime segfault flagged here turned out to be two -independent bugs, neither caused by this refactor. Both are documented, -diagnosed, and fixed in `docs/CC-KITCHEN-SINK-PUNCHLIST.md`: - -1. **Out-of-order designated initializer for global structs** - (`%parse-init-struct-list` in `cc/parse.scm`) — latent - pre-existing bug; cc-cg/cc-parse coverage only hit forward-order - designators. -2. **Duplicate global loop-tag labels across functions** - (`cg-fn-begin/v` resetting `cg-label-ctr` in `cc/cg.scm`) — - `%loop_tag` emits single-colon (global) labels, so two - functions both using `L0` produced collisions that hex2 - resolved arbitrarily. Made the counter monotonic across the TU. - -Disregard the "Where to look first" investigation list below; the -specific items it suggested were not the cause. With both fixes -applied, all 32 cc-e2e fixtures pass on aarch64 (the kitchen-sink -plus 30 individual per-feature fixtures, plus the original -00-return-argc). - -### Where to look first - -1. **Compare cg output vs. the pre-refactor implementation on a small - test input that reaches all kitchen-sink features.** The most likely - regression class is "cg-fn-end now emits bytes in a different order - or skips a piece." Diff the `.P1pp` for any cc-cg fixture before/after - the refactor — if any byte differs, the bug is in `cg-fn-end`. - -2. **`cg-fn-end` byte ordering** (`cc/cg.scm:362`). The original used a - single `bv-cat` of `(so-macro "%fn(...{\n" prologue body ret-block "})\n")`. - The new version emits the same pieces via individual `buf-push!` - plus two `buf-drain!`s. Confirm the emitted byte sequence is - identical, particularly: - - Newlines / whitespace around the `__SO` macro. - - Order: prologue **before** body (it is now — line 395 vs 396). - - Ret-block placement (after body, before closing `})\n`). - -3. **`cg-in-fn?-set! cg #f` placement in `cg-fn-end`** (`cc/cg.scm:381`). - It's set to `#f` BEFORE the trailing `buf-push!`es, but those pushes - target `tb` (= `cg-text`) directly, not via `%cg-emit-buf`, so the - flag should not matter. Verify nothing inside the trailing - `(%cg-slot-expr cg ret-slot)` path takes the `%cg-emit-buf` route - (it shouldn't — `%cg-slot-expr` is pure). - -4. **Switch case lists.** `cg-switch-case` was rerouted from - `cg-globals` to `cg-fn-meta`. `cc-parse/65-switch` passes, but - kitchen-sink may exercise multi-switch patterns or nested switches. - Look at `cg-switch-end`'s reverse + emit loop and confirm the case - ordering hasn't flipped (`cc/cg.scm:1102-1107`). - -5. **Buf overflow silently truncating output.** If any cap is too small - for kitchen-sink, `buf-push!` calls `die` — a clean error, not a - segfault. So this isn't the segfault cause, but worth confirming - `cc/util.scm` didn't fall through silently. Tag `die` with a printf - to be sure. - -6. **Run the produced binary under a tracer** (`gdb`, `strace`) to - localize the fault. The segfault address will point at either: - - A bogus label resolution (cg ordering / scope-leak issue), or - - A wrong frame-size / slot-offset (the `__SO` macro expansion path). - -### Quickest narrowing experiment - -Compile a kitchen-sink-derived fixture that uses **only one** kitchen-sink -feature at a time (just statics, just switch, just sizeof, etc.) and see -which one segfaults. The kitchen-sink C source's `test_*` helpers map -1:1 to features. diff --git a/docs/CC-INTERNALS.md b/docs/CC-INTERNALS.md @@ -47,13 +47,13 @@ When adding any new codegen-touching feature, **always** in this order: At this point the test fails — that's the spec. 2. **Implement the cg primitive(s).** Add or fix the API surface in `cc/cg.scm`. Iterate until `make test SUITE=cc-cg` passes. -3. **cc-parse fixture next.** Write a `tests/cc-parse/<n>-name.c` that +3. **cc fixture next.** Write a `tests/cc/<n>-name.c` that exercises the same feature from the C source side, with a `main` that exits with a known value. Add `.expected-exit` / `.expected`. Test fails — that's the spec for the parser. 4. **Implement the parse changes.** Wire C syntax through `cc/parse.scm` using the cg primitives from step 2. Iterate until - `make test SUITE=cc-parse` passes. + `make test SUITE=cc` passes. This sequencing keeps the parser↔cg seam honest: cg is validated by runtime behavior on direct API calls before parse is even touched, so @@ -580,7 +580,7 @@ diffs the program's exit code against `<name>.expected-exit` (default Fixtures must therefore emit a complete, runnable program — including any callees they invoke via `cg-call`. Direct-cg tests that depend on -external symbols (`extern abs`, `extern foo`) belong in cc-e2e once +external symbols (`extern abs`, `extern foo`) belong in cc once real `libc` linkage exists; they don't fit cc-cg. The contract this suite locks in is **semantic**, not syntactic: cg @@ -682,7 +682,7 @@ opnd. Statements that don't consume the value follow with `cg-pop`. ### Test plan -`tests/cc-parse/` is a runtime-validating suite. Each fixture is a +`tests/cc/` is a runtime-validating suite. Each fixture is a `.c` source file containing a complete, runnable program — `main` must exit with a known value. The harness runs the full lex+pp+parse+real-cg pipeline, assembles the emitted P1pp through the @@ -721,7 +721,7 @@ unrecognized flags die. ## Test infrastructure Five test trees. The upstream layers (lex, pp) byte-diff their pure -transformations; the downstream layers (cg, parse, e2e) all +transformations; the downstream layers (cg, cc) all **compile-and-run** the emitted P1pp and assert runtime behavior — exit code by default, optional stdout match. This split keeps the byte-diff brittleness out of every layer where there's a real "is this code @@ -735,15 +735,12 @@ correct" question to ask. - `tests/cc-cg/` — `.scm` driver calls cg APIs directly to emit a complete program; harness builds + runs the P1pp; asserts exit code / stdout. (See cg.scm §Test plan.) -- `tests/cc-parse/` — `.c` fixture goes through full lex+pp+parse+cg; - harness builds + runs; asserts exit code / stdout. (See parse.scm - §Test plan.) -- `tests/cc-e2e/` — same shape as cc-parse, but reserved for fixtures - exercising the full toolchain envelope (multi-file inputs, libc - linkage, etc.) once those are in scope. Today both cc-parse and - cc-e2e build the same way; the distinction is fixture *intent*. - -`tests/cc-cg/` is the codegen contract. `tests/cc-parse/` is the C- +- `tests/cc/` — `.c` fixture goes through the full compiler driver + (`cc/cc.scm`: lex+pp+parse+cg); harness builds + runs; asserts exit + code / stdout. (See parse.scm §Test plan.) Holds both feature drills + (parser / cg coverage) and full-envelope scenarios. + +`tests/cc-cg/` is the codegen contract. `tests/cc/` is the C- language contract. Per §Feature workflow, every feature lands a cg fixture first (which must fail before cg is touched), and a parse fixture second (which must fail before parse is touched). diff --git a/docs/CC-KITCHEN-SINK-PUNCHLIST.md b/docs/CC-KITCHEN-SINK-PUNCHLIST.md @@ -1,268 +0,0 @@ -# cc kitchen-sink punch list - -Status of `tests/cc-e2e/01-kitchen-sink.c` and the per-test fixtures -(`tests/cc-e2e/02-arith.c` through `tests/cc-e2e/31-addr-array.c`). -Companion to `docs/CC-HEAP-REWIND.md`. - -**Current status: all 32 cc-e2e fixtures pass on aarch64.** The two -bugs found during this debug session are documented below and have -both been fixed in `cc/parse.scm` and `cc/cg.scm`. - -## How to (re)build cc and run a single fixture - -```sh -# 1. Build prerequisites once (image, M0/catm, m1pp, scheme1). -make scheme1 ARCH=aarch64 -make m1pp ARCH=aarch64 - -# 2. Rebuild build/aarch64/cc/cc.scm whenever any cc/*.scm or -# scheme1/prelude.scm changes. (The Makefile target depends on -# every CC_SRC, but the catm itself runs in the container.) -make cc ARCH=aarch64 -# Or, by hand (matches what `make cc` does): -podman run --rm --pull=never --platform linux/arm64 \ - --tmpfs /tmp:size=512M -e ARCH=aarch64 \ - -v "$(pwd)":/work -w /work boot2-busybox:aarch64 \ - build/aarch64/tools/catm build/aarch64/cc/cc.scm \ - scheme1/prelude.scm cc/util.scm cc/data.scm cc/lex.scm \ - cc/pp.scm cc/cg.scm cc/parse.scm cc/main.scm - -# 3. Run one cc-e2e fixture by name. -sh scripts/run-tests.sh --suite=cc-e2e --arch=aarch64 09-array - -# 4. Or, do the three pipeline stages by hand: -ARCH=aarch64 -SRC=tests/cc-e2e/09-array.c -OUT=build/$ARCH/cc-e2e/09-array -mkdir -p "$OUT" - -# cc: .c -> .P1pp -podman run --rm --pull=never --platform linux/arm64 \ - --tmpfs /tmp:size=512M -e ARCH=$ARCH \ - -v "$(pwd)":/work -w /work boot2-busybox:$ARCH \ - sh -c "build/$ARCH/scheme1 build/$ARCH/cc/cc.scm $SRC $OUT/09-array.P1pp" - -# m1pp + M0 + hex2 + ELF wrap: .P1pp -> ELF -podman run --rm --pull=never --platform linux/arm64 \ - --tmpfs /tmp:size=512M -e ARCH=$ARCH \ - -v "$(pwd)":/work -w /work boot2-busybox:$ARCH \ - sh scripts/boot-build-p1pp.sh "$OUT/09-array.P1pp" "$OUT/09-array" - -# Run inside the per-arch container. -podman run --rm --pull=never --platform linux/arm64 \ - --tmpfs /tmp:size=512M -e ARCH=$ARCH \ - -v "$(pwd)":/work -w /work boot2-busybox:$ARCH \ - "./$OUT/09-array" -echo "exit=$?" -``` - -Intermediate artifacts for the m1pp/M0/hex2 chain land in -`build/$ARCH/.work/<fixture-name>/` (`combined.M1pp`, `expanded.M1`, -`prog.hex2`, `linked.hex2`). Inspect those when a runtime failure looks -like a backend codegen issue rather than a cc bug. - -## Pitfall encountered during this debug session - -`build/$ARCH/cc/cc.scm` is the catm of `scheme1/prelude.scm` plus all -`cc/*.scm`. The Make target rebuilds it when any source file changes, -but **if you bypass Make and edit `cc/*.scm` or `scheme1/prelude.scm` -by hand, you must re-run `make cc` (or the explicit catm above) before -the next test run.** Otherwise cc.scm holds the *old* prelude/util ABI -(e.g. 3-arg `sys-read`) while `build/$ARCH/scheme1` exposes the new -one (4-arg), and the compiler crashes with a SIGBUS or behaves -nondeterministically. - -The run-tests.sh runner silently swallows compile output and only -prints `cc compile failed`, so the symptom looks like an intermittent -crash even though it is a deterministic stale-input problem. If you -see the cc compiler dying with SIGSEGV/SIGBUS that wasn't dying -yesterday, rebuild cc.scm first. - -## Per-fixture status (aarch64) - -All passing as of the fixes below. Each individual fixture is a -self-contained excerpt of `01-kitchen-sink.c` covering one feature. -`main` checks the relevant `test_*` return value and exits 0 on match, -1 on mismatch. - -| # | Fixture | Coverage | -|----|------------------------|----------| -| 02 | `02-arith.c` | + - * / % << >> & \| ^ ! ~ unary- | -| 03 | `03-compound.c` | += -= *= /= %= <<= >>= &= \|= ^= | -| 04 | `04-inc-dec.c` | x++ ++x x-- --x as expressions | -| 05 | `05-logical.c` | && \|\| short-circuit | -| 06 | `06-ternary.c` | `cond ? then : else` | -| 07 | `07-compare.c` | == != < > <= >= | -| 08 | `08-pointer.c` | * & **pp | -| 09 | `09-array.c` | local 1-D array + indexed sum | -| 10 | `10-array-2d.c` | local 2-D array + nested loop | -| 11 | `11-struct.c` | local struct positional init | -| 12 | `12-struct-ptr.c` | global struct designated init `{.y=5,.x=7}` (Bug 1 regression) | -| 13 | `13-call.c` | int(int,int) call | -| 14 | `14-recursion.c` | recursive factorial | -| 15 | `15-variadic.c` | va_list + __builtin_va_{start,arg,end} | -| 16 | `16-fn-ptr.c` | global fn-ptr table + indirect call | -| 17 | `17-apply.c` | fn-ptr param + struct-ptr param via `->` | -| 18 | `18-ptr-recursion.c` | pointer-arith + recursion + global-array decay | -| 19 | `19-static.c` | block-scope `static int n = 0` | -| 20 | `20-switch.c` | switch + case fall-through + default | -| 21 | `21-goto.c` | backward `goto` to a labelled stmt | -| 22 | `22-loops.c` | do-while + while + continue | -| 23 | `23-strings.c` | string literal walked via `*s` | -| 24 | `24-globals.c` | scalar + bss + array + char[] globals | -| 25 | `25-sizeof.c` | `sizeof` on int/char/struct/array | -| 26 | `26-enum.c` | enum constants with explicit value | -| 27 | `27-void-call.c` | void fn with int* out param | -| 28 | `28-cast.c` | narrowing cast + sign-extend on re-widen | -| 29 | `29-void-ptr.c` | void* round-trip cast | -| 30 | `30-comma.c` | comma operator inside an initializer | -| 31 | `31-addr-array.c` | &arr → T(*)[N] | -| 01 | `01-kitchen-sink.c` | every fixture above in one TU | - -## Bugs found and fixed - -### Bug 1 — out-of-order designated initializer for global structs (latent, pre-existing) - -**Symptom.** A global struct initialized with designators in non-declaration -order produced an oversized data section, with each designator emitting -a full struct's worth of bytes: - -```c -struct Point { int x; int y; }; /* sizeof == 8 */ -struct Point g_pt2 = { .y = 5, .x = 7 }; /* should emit 8 bytes: 7,0,0,0, 5,0,0,0 */ -``` - -emitted 16 bytes (`0,0,0,0, 5,0,0,0, 7,0,0,0, 0,0,0,0`). Reads of -`g_pt2.x` and `g_pt2.y` then saw the wrong values. - -**Where.** `cc/parse.scm:%parse-init-struct-list`. The original -implementation tracked a `filled` cursor that only moved forward. -When `.x` arrived after `.y`, the target offset (0) was *less* than -the current `filled` (8), so the "pad if pad-bytes > 0" branch was -false and the new value got appended after the previous one rather -than overwriting at offset 0. The closing `rbrace` then padded to the -declared size, doubling the data section. - -**Fix.** Switched to a sort-by-offset model. Each entry is now -collected as `(abs-offset . piece-list)` in source order; at the -closing brace `%merge-init-entries` stable-sorts by offset and emits -pad pieces in any gaps and at the tail. Preserves `(label-ref . _)` -pieces (e.g. designators that take an `&foo` value) — they are not -merged into a flat bv. New helpers: `%piece-bytesize`, -`%pieces-bytesize`, `%merge-init-entries`, -`%init-stable-sort-by-offset`. (`cc/parse.scm` ~line 519.) - -The `cc-cg/54-init-struct-desig` and `cc-parse/54-init-struct-desig` -fixtures only exercised forward designators (`{ .b = 7 }`), which the -original logic happened to handle, so the cc-cg/cc-parse suites had -not been catching this. - -The local-struct path `%parse-init-local-struct-list` writes each -field to its absolute slot offset directly, so it handles -out-of-order designators correctly. It does have a related quirk: -it does not zero a designator-skipped field (e.g. `{.y = 5}` leaves -`x` uninitialized in the local). Worth tracking separately if -strict-C zero-init for partial designators becomes important. - -### Bug 2 — duplicate global loop-tag labels across functions (refactor regression in 53e1036 / earlier — predates the heap-rewind change) - -**Symptom.** Any C source with two or more functions each containing a -loop produced a binary that crashed at runtime, even when only one of -the functions was called. Minimal repro: - -```c -int test_array(void) { - int a[5] = { 1, 2, 3, 4, 5 }; - int sum = 0; int i; - for (i = 0; i < 5; i = i + 1) sum = sum + a[i]; - return sum; -} -int test_array_2d(void) { /* never called */ - int m[2][3] = { {1,2,3}, {4,5,6} }; - int sum = 0; int i; int j; - for (i = 0; i < 2; i = i + 1) - for (j = 0; j < 3; j = j + 1) - sum = sum + m[i][j]; - return sum; -} -int main(int argc, char **argv) { - if (test_array() != 15) return 8; - return 0; -} -``` - -`test_array`'s emitted P1pp was byte-identical between this build and -the (passing) build with `test_array_2d` removed — so the cc-emitted -text wasn't itself wrong. The bug was in label resolution downstream. - -**Where.** `cc/cg.scm:%cg-fresh-loop-tag` produces names like `L0`, -`L1`, … from `cg-label-ctr`, which `cg-fn-begin/v` was resetting to 0 -at the start of every function body. The libp1pp `%loop_tag(tag, body)` -macro (`P1/P1pp.P1pp:loop_tag`) emits **single-colon** labels: - -``` -%macro loop_tag(tag, body) - : ## tag ## _top - body - %b(& ## tag ## _top) - : ## tag ## _end -%endm -``` - -Single-colon labels are **global** in m1pp; only `::label` definitions -(and `&::label` references) get rewritten under the active scope stack. -So two functions both using `L0` produced two `:L0_top` and two -`:L0_end` definitions in `expanded.M1`. The hex2 step then resolved -`%break(L0)` / `%continue(L0)` references against whichever -definition it picked — corrupting control flow, often jumping into the -wrong function and corrupting the return-address slot. - -The `cg-switch-end`-emitted `sw_disp_<tag>` dispatch label is also -single-colon and inherits the same tag, so the switch dispatcher -suffered the same collision. - -**Why cc-cg / cc-parse fixtures didn't catch it.** Almost every cc-cg -and cc-parse fixture is a single-function program (only `main`). -`cc-cg/64-switch` and `cc-parse/65-switch` define one switch in -`main`. `cc-parse/69-multi-fn` has multiple functions but no loops, -so no loop-tag labels. None of those exercise two functions that both -emit `%loop_tag` / `%break` / `%continue`. - -**Fix.** Stop resetting `cg-label-ctr` at `cg-fn-begin/v`. The counter -is now monotonic across the whole translation unit, so every -`%cg-fresh-loop-tag` returns a name that has not been used before — -no two functions can produce the same `L<N>`, and the hex2 step has a -unique definition for every reference. Three lines of comment in -`cc/cg.scm:cg-fn-begin/v` explain the constraint so the reset doesn't -drift back. - -The other label-counter consumer in cg, `%cg-fresh-lbl` (used by -`cg-switch-case`, `cg-switch-default`, the goto/labelled-stmt path, -and various intermediate jumps), all emit `::lbl_<N>` / -`::user_<name>` (double-colon, scoped) so their per-function counters -were never colliding. The shared monotonic counter doesn't change -that — it just makes them more globally unique than they need to be, -which is fine. - -## Verification - -```sh -make cc ARCH=aarch64 -sh scripts/run-tests.sh --suite=cc-e2e --arch=aarch64 # 32/32 PASS -sh scripts/run-tests.sh --suite=cc-cg --arch=aarch64 # 48/48 PASS -sh scripts/run-tests.sh --suite=cc-parse --arch=aarch64 # 68/68 PASS -``` - -amd64 / riscv64 not re-verified after the fixes (the bugs and fixes -are arch-independent — both live in cc/parse.scm and cc/cg.scm — but -should be confirmed before relying on the kitchen-sink as a -cross-arch baseline). - -## Glossary of failure-class exit codes - -| exit | meaning | -|------|---------| -| 0 | pass (or `test_X()` returned 0; no test does so by design) | -| 1 | `main`'s `if (test_X() != EXPECTED) return 1;` fired — wrong value, not a crash | -| 135 | killed by SIGBUS — usually a misaligned access or a stale cc.scm/scheme1 ABI mismatch | -| 139 | killed by SIGSEGV — bad pointer, return-address corruption, etc. | diff --git a/docs/CC-PUNCHLIST.md b/docs/CC-PUNCHLIST.md @@ -7,24 +7,22 @@ is the implementation checklist against that surface. ## Conventions -- Every item has up to three runtime-validated fixtures: +- Every item has up to two runtime-validated fixtures: - **cg**: `tests/cc-cg/<n>-name.scm` — drives the cg API directly. - - **parse**: `tests/cc-parse/<n>-name.c` — exercises the same shape - via real C. - - **e2e**: `tests/cc-e2e/<n>-name.c` — only for capabilities that - stress the full envelope (driver, multi-fn, libc). -- Acceptance: `make test SUITE=cc-cg` (then cc-parse, then cc-e2e) - green on all three arches. The runner asserts `.expected-exit` - (default `0`) and `.expected` stdout (default empty). -- Land cg work + cg fixture in one PR; parse work + parse fixture in + - **cc**: `tests/cc/<n>-name.c` — exercises the same shape via real + C through the full compiler driver. Use the same bucket for + full-envelope scenarios (driver, multi-fn, libc) once those land. +- Acceptance: `make test SUITE=cc-cg` (then cc) green on all three + arches. The runner asserts `.expected-exit` (default `0`) and + `.expected` stdout (default empty). +- Land cg work + cg fixture in one PR; cc fixture + parse work in the next. Don't block on parse to start cg. -- Pick the next free `<n>` per suite. cc-cg + cc-parse currently end - at 14; cc-e2e at 00. +- Pick the next free `<n>` per suite. - Status legend: `[ ]` red · `[~]` partial · `[x]` green. ## Already green -cc-cg 00–14 + cc-parse 00–14 + cc-e2e 00 cover: empty fn, `return` +cc-cg 00–14 + cc 00–14 cover: empty fn, `return` with const/param, two-param fn, i64 binops, locals + assign, `if` / `if-else`, `while` with `break` / `continue`, direct calls (0..5 args, with stack staging), string literal interning, file-scope @@ -41,7 +39,7 @@ upstream of nearly everything else. Land this first. - [x] **`char` (8-bit) load/store via lval** - cg: `cc-cg/15-char-roundtrip.scm` — two adjacent 1-byte slots; stores must not bleed across slots → exit 1 on equality check. - - parse: `cc-parse/15-char-arith.c` — same shape from C. + - cc: `cc/15-char-arith.c` — same shape from C. - Done: added `%cg-emit-{ld,st}{,-slot}-typed` helpers that dispatch on `ctype-size = 1` to `%lb`/`%sb`; `%cg-load-opnd-into`, `cg-load`, `cg-assign` thread the lval ctype. `i8` loads also @@ -50,7 +48,7 @@ upstream of nearly everything else. Land this first. - [x] **`short` (16-bit) load/store via lval** - cg: `cc-cg/16-short-roundtrip.scm` - - parse: `cc-parse/16-short-arith.c` + - cc: `cc/16-short-arith.c` - Done: byte-decomposed dispatch in the typed helpers — store low byte then `%shri` 8 + store high byte; load two bytes + `%shli` 8 + `%or`. `i16` sign-extends via `shli`/`sari` 48. Helpers may @@ -58,7 +56,7 @@ upstream of nearly everything else. Land this first. - [x] **`int` (32-bit) load/store via lval** - cg: `cc-cg/17-int-roundtrip.scm` - - parse: `cc-parse/17-int-arith.c` + - cc: `cc/17-int-arith.c` - Done: byte-decomposed dispatch generalised to N bytes via `%cg-emit-{ld,st}N-bytes`; size-4 routes through 4× `%lb`/`%sb` with shift+OR / shri-shift. `i32` sign-extends via `shli`/`sari` @@ -68,7 +66,7 @@ upstream of nearly everything else. Land this first. - [x] **Signed narrowing keeps sign on re-widen** - cg: `cc-cg/18-sext-narrow.scm` — `(int)(char)-3 == -3` → exit 1. - - parse: `cc-parse/18-sext-narrow.c` + - cc: `cc/18-sext-narrow.c` - Done: `cg-cast`'s narrowing branch now `shli`/`sari`'s for signed narrow targets (i8/i16/i32) instead of masking, so the slot holds the canonical sign-extended 64-bit form. The widening cast back @@ -76,7 +74,7 @@ upstream of nearly everything else. Land this first. - [x] **Unsigned narrowing zero-extends** - cg: `cc-cg/19-zext-narrow.scm` — `(unsigned)(unsigned char)-3 == 253`. - - parse: `cc-parse/19-zext-narrow.c` + - cc: `cc/19-zext-narrow.c` - Done: pre-existing mask-on-unsigned-narrow path was already correct; fixture locks the contrast with §A.4 in (same source, same chain shape, divergent result via target signedness). @@ -84,7 +82,7 @@ upstream of nearly everything else. Land this first. - [x] **Integer promotion preserves sign across operations** - cg: `cc-cg/20-promote-sign.scm` — `signed char x=-1; ((int)x)+2 == 1` via 64-bit comparison so a non-canonical 0x101 result fails. - - parse: `cc-parse/20-promote-sign.c` + - cc: `cc/20-promote-sign.c` - Done: load-side sign-extension from §A.1 already canonicalises the slot, so `cg-promote`'s relabel-only path is correct. Fixture locks the invariant in. @@ -99,32 +97,32 @@ primitive to capture the old rval before the store. See - [x] **Pre-`++` / pre-`--`** - cg: `cc-cg/21-preinc.scm` — `int x = 5; ++x; return x;` → exit 6. - - parse: `cc-parse/21-preinc.c` + - cc: `cc/21-preinc.c` - Done: parser dups lhs lval, loads, +1, assigns; pops result. - [x] **Post-`++` / post-`--` returns old value** - cg: `cc-cg/22-postinc.scm` — `int x=5; int y=x++; return x*10+y;` → exit 65. - - parse: `cc-parse/22-postinc.c` + - cc: `cc/22-postinc.c` - Done: `cg-postinc` / `cg-postdec` primitives composed of two dup+load passes — one to capture the old rval (which lives in a never-reused spill slot), one to compute the +1/-1 store. - [x] **Compound assignment on simple lval (`+= -= *= /= %= <<= >>= &= ^= |=`)** - cg: `cc-cg/23-cmpd-simple.scm` — `int x=7; x+=3; return x;` → exit 10. - - parse: `cc-parse/23-cmpd-simple.c` — one of every op family. + - cc: `cc/23-cmpd-simple.c` — one of every op family. - Done: parser uses `cg-dup` + `cg-load` + rhs + arith-conv + binop + assign; the `cg-take-addr`/`cg-push-deref` indirection is gone. - [x] **Compound assignment through pointer** - cg: `cc-cg/24-cmpd-ptr.scm` — `int x=7; int *p=&x; *p+=3; return x;` - - parse: `cc-parse/24-cmpd-ptr.c` + - cc: `cc/24-cmpd-ptr.c` - Done: same parser sequence; `cg-push-deref`'s indirect-slot lval composes correctly with `cg-dup` + `cg-assign`. - [x] **`*p++` walking an array** - cg: `cc-cg/25-deref-postinc.scm` — walks a 3-element span via *p++. - - parse: `cc-parse/25-deref-postinc.c` + - cc: `cc/25-deref-postinc.c` - Done: composes B.2 (post-inc on a ptr lval; pointer scaling falls out of `cg-binop add`'s ptr branch) with `*p` deref. Also fixed `cg-arith-conv` to skip the relabel when one operand is ptr/arr @@ -134,13 +132,13 @@ primitive to capture the old rval before the store. See ### C. `sizeof` - [x] **`sizeof e` returns the type's actual size** - - parse: `cc-parse/26-sizeof-expr.c` — `int x; return sizeof x;` → 4. + - cc: `cc/26-sizeof-expr.c` — `int x; return sizeof x;` → 4. - Done: parser peeks `(opnd-type (cg-top …))`, takes its `ctype-size`, pops, pushes `imm u64 size`. Both forms (`sizeof e` and `sizeof(e)`) updated. - [x] **`sizeof` over struct, array, pointer, char** - - parse: `cc-parse/27-sizeof-types.c` — sum of `char`, `short`, + - cc: `cc/27-sizeof-types.c` — sum of `char`, `short`, `int`, `long`, `int*`, `int[5]`, `struct S{int a; int b;}` → 51. - Done: the type form already returned `ctype-size ty`; this fixture just locks the answer in. @@ -149,7 +147,7 @@ primitive to capture the old rval before the store. See - [x] **Struct member load** - cg: `cc-cg/36-struct-load.scm` — two-int struct, fields at 0 and 4. - - parse: `cc-parse/36-struct-load.c` + - cc: `cc/36-struct-load.c` - Done: added `cg-push-field cg fname` (cg.scm). Pops struct/union lval, looks up `fname` in `ctype-ext`'s `(tag complete? fields)`. Three input cases: direct frame lval shifts the slot offset; @@ -160,20 +158,20 @@ primitive to capture the old rval before the store. See - [x] **Struct member store** - cg: `cc-cg/37-struct-store.scm` — three u8 fields, distinct multipliers in the readback to isolate per-field width. - - parse: `cc-parse/37-struct-store.c` + - cc: `cc/37-struct-store.c` - Done: cg-push-field from §D.1 plus the width-aware store path from §A.1. No new primitive. - [x] **Pointer-to-struct (`p->x`)** - cg: `cc-cg/38-arrow.scm` - - parse: `cc-parse/38-arrow.c` + - cc: `cc/38-arrow.c` - Done: arrow arm in `parse-postfix-rest` calls rval! (loads ptr), cg-push-deref (struct lval through ptr), then cg-push-field. Indirect-frame branch of cg-push-field (added in §D.1) handles the deref-result struct lval correctly. - [x] **Nested struct access (`s.inner.x`, `s->inner.x`)** - - parse: `cc-parse/39-struct-nested.c` + - cc: `cc/39-struct-nested.c` - Done: cg-push-field pushes a new lval whose ctype is the field's type; if that's a struct, a subsequent `.x` chains naturally. The fixture exercises both s.inner.x (direct frame) and @@ -181,7 +179,7 @@ primitive to capture the old rval before the store. See - [x] **Array element access at non-zero index** - cg: `cc-cg/40-array-index.scm` - - parse: `cc-parse/40-array-index.c` + - cc: `cc/40-array-index.c` - Done: cg-load on an arr-typed lval delegates to cg-decay-array, pushing a ptr-rval to the first element. Existing `cg-binop add` pointer-arithmetic path scales by the pointee size, so `a + i` @@ -190,7 +188,7 @@ primitive to capture the old rval before the store. See T* (not (T[N])*) so `&a[0]` stays consistent. - [x] **Multi-dim arrays** - - parse: `cc-parse/41-array-2d.c` + - cc: `cc/41-array-2d.c` - Done: fixed `parse-decl-suf-cont` to apply suffixes right-to-left (innermost first) so `int a[2][3]` produces `arr (arr int 3) 2`, not `arr (arr int 2) 3`. Same fix in the @@ -198,7 +196,7 @@ primitive to capture the old rval before the store. See + ptr arithmetic from §D.5 then handles the rest. - [x] **Struct passed by pointer to a function** - - parse: `cc-parse/42-struct-fn-arg.c` — passes `&s` to `sum2`, + - cc: `cc/42-struct-fn-arg.c` — passes `&s` to `sum2`, callee returns `p->x + p->y`. - Done: composes §D.1/§D.3 (cg-push-field, arrow access) and the pre-existing param/call/return wiring. No new primitive. @@ -214,74 +212,74 @@ accepts an init bv but is never given one. - [x] **Scalar global with constant initializer** - cg: `cc-cg/49-init-scalar-global.scm` - - parse: `cc-parse/49-init-scalar-global.c` + - cc: `cc/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` + - cc: `cc/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` + - cc: `cc/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` + - cc: `cc/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` + - cc: `cc/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` + - cc: `cc/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` + - cc: `cc/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` + - cc: `cc/56-init-local-struct.c` - Done: same per-field store sequence; designated form supported. ### F. Control flow extensions - [x] **`do { } while (e);`** - cg: `cc-cg/63-do-while.scm` - - parse: `cc-parse/63-do-while.c` + - cc: `cc/63-do-while.c` - Done: composes existing `cg-loop` + `cg-if` + `cg-break`; fixture-only. - [x] **`for (init; cond; step)` with declaration in `init`** - - parse: `cc-parse/64-for-decl.c` + - cc: `cc/64-for-decl.c` - Done: existing `parse-for-stmt` exercised end-to-end. - [x] **`switch / case / default` with fall-through** - cg: `cc-cg/64-switch.scm` — three cases falling through to default. - - parse: `cc-parse/65-switch.c` + - cc: `cc/65-switch.c` - Done: validated the existing `swctx` machinery in cg. - [x] **`goto` / labelled statement (forward and backward)** - cg: `cc-cg/65-goto.scm` - - parse: `cc-parse/66-goto.c` + - cc: `cc/66-goto.c` - Done: replaced the `cg-break` hack in `parse-goto-stmt`. Added `cg-emit-label cg name-bv` (drops `::user_<name>`) and `cg-goto cg name-bv` (emits `%b(&::user_<name>)`). @@ -293,7 +291,7 @@ accepts an init bv but is never given one. - [x] **Variadic call: per-arg default-promote** - cg: `cc-cg/66-vararg-call.scm` - - parse: `cc-parse/67-vararg-call.c` + - cc: `cc/67-vararg-call.c` - Done: parser inspects fn type at `parse-call-args`; for arg index ≥ named-arg count, emits `cg-promote` per CC.md §Implicit conversions. Fixed-arg index emits `cg-cast` to declared param @@ -301,7 +299,7 @@ accepts an init bv but is never given one. - [x] **Variadic receive: `__builtin_va_start/arg/end`** - cg: `cc-cg/69-vararg-recv.scm` — sums N int-typed variadic args. - - parse: `cc-parse/76-vararg-recv.c` + - cc: `cc/76-vararg-recv.c` - Done: added `cg-va-start cg`, `cg-va-arg cg ctype`, `cg-va-end cg` (each pops ap-lval from vstack); `cg-fn-begin/v` reserves a 16-slot incoming-arg window: indices @@ -311,7 +309,7 @@ accepts an init bv but is never given one. parse-primary; `parse-fn-body` threads the fn ctype's variadic? flag. Bundled `cc/headers/stdarg.h` aliases `va_list`/`va_start`/ `va_arg`/`va_end` to the builtins. Lock-in fixture - `cc-parse/79-vararg-deep.c` exercises 1 named + 6 variadic args. + `cc/79-vararg-deep.c` exercises 1 named + 6 variadic args. Limit: 15 variadic args after the named ones; bump `VARARG_WINDOW` (currently 16) in `cg-fn-begin/v` to extend. @@ -325,21 +323,21 @@ responsible for arranging compatible types in the two branches. - [x] **Ternary `?:` leaves exactly one rval** - cg: `cc-cg/28-ternary.scm` — `c ? 7 : 9` with c=1 → exit 7. - - parse: `cc-parse/28-ternary.c` + - cc: `cc/28-ternary.c` - Done: parser swaps `cg-ifelse` for `cg-ifelse-merge` in the `qmark` arm of `parse-binary-rhs`; both branches push their parsed rval directly. - [x] **`&&` short-circuit leaves exactly one i32 rval** - cg: `cc-cg/29-land.scm` - - parse: `cc-parse/29-land.c` + - cc: `cc/29-land.c` - Done: parser injects `cg-cast %t-bool` then `cg-cast %t-i32` on the rhs side so the merged result is i32 ∈ {0,1}; the else-arm pushes `%t-i32 0`. - [x] **`||` short-circuit leaves exactly one i32 rval** - cg: `cc-cg/30-lor.scm` - - parse: `cc-parse/30-lor.c` + - cc: `cc/30-lor.c` - Done: mirrors §H.2 with the bool-cast on the else-arm and a constant `%t-i32 1` in the then-arm. @@ -347,7 +345,7 @@ responsible for arranging compatible types in the two branches. - [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` + - cc: `cc/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. @@ -355,7 +353,7 @@ responsible for arranging compatible types in the two branches. ### J. Driver / envelope - [x] **Entry stub forwards `argc` / `argv` to `main`** - - e2e: `cc-e2e/00-return-argc` (already green; locked in). + - cc: `cc/00-return-argc` (already green; locked in). - Done: P1's program-entry contract delivers `a0=argc`, `a1=argv` at `p1_main` (P1.md §Program Entry). `%call` doesn't clobber a0/a1, so the existing fall-through stub `%fn(p1_main, 16, @@ -363,13 +361,13 @@ responsible for arranging compatible types in the two branches. `cg-finish`. - [x] **`int main()` falling off the end returns 0** - - parse: `cc-parse/68-main-noret.c` — `int main(){}` → exit 0. + - cc: `cc/68-main-noret.c` — `int main(){}` → exit 0. - Done: `cg-fn-begin` now zero-inits the ret slot in the prologue when the return type isn't void, so falling through to `::ret` reads back a defined 0 instead of relying on kernel zero-fill. - [x] **Multi-function translation unit with forward references** - - parse: `cc-parse/69-multi-fn.c` + - cc: `cc/69-multi-fn.c` - Done: forward declaration `int helper(int x);` binds an extern fn sym up-front so `parse-primary` finds it before the definition appears. @@ -377,7 +375,7 @@ responsible for arranging compatible types in the two branches. ### K. Expressions and conversions - [x] **Comma operator (`a, b` as expression)** - - parse: `cc-parse/31-comma.c` — `int a; int b; (a=1, b=2); return a + b*10;` + - cc: `cc/31-comma.c` — `int a; int b; (a=1, b=2); return a + b*10;` → exit 21. - Done: added `(comma . (1 . 2))` to `%binop-bp` (left-assoc, below `assign`'s 4/3 so `parse-call-args ps 4` still won't slurp it as a @@ -386,32 +384,32 @@ responsible for arranging compatible types in the two branches. - [x] **Function-pointer call** - cg: `cc-cg/67-fnptr-call.scm` — push a fn-typed sym, spill to a frame slot, reload, call. - - parse: `cc-parse/71-fnptr-call.c` — `int (*fp)(int) = f; return fp(7);` + - cc: `cc/71-fnptr-call.c` — `int (*fp)(int) = f; return fp(7);` → exit 21. - Done: exercises `cg-call`'s `%callr(t0)` branch; verified return-type extraction walks `ptr → fn → ret` correctly. - [x] **Enum constant in expressions** - - parse: `cc-parse/72-enum-const.c` — `enum E { A=1, B=10, C }; + - cc: `cc/72-enum-const.c` — `enum E { A=1, B=10, C }; return A+B+C;` → exit 22. - Done: locked in the existing `parse-primary` `'enum-const` branch. - [x] **`void *` ↔ `T *` implicit conversion (no cast required)** - - parse: `cc-parse/73-voidptr-impl.c` — `void *p; int x=42; p=&x; + - cc: `cc/73-voidptr-impl.c` — `void *p; int x=42; p=&x; int *q=p; return *q;` → exit 42. - Done: cg-cast's `to-kind = 'ptr` clause is relabel-only between any pointer types; `cg-assign` drives the cast each direction. - [x] **Implicit narrowing of fixed-arg call arguments to declared param type** - - parse: `cc-parse/74-call-narrow.c` — `int f(unsigned char x){return x;} + - cc: `cc/74-call-narrow.c` — `int f(unsigned char x){return x;} int main(){ return f(258); }` → exit 2. - Done: `parse-call-args` now emits `cg-cast` per fixed arg to the declared param type (variadic args promoted via §G.1). - [x] **Pointer comparison is unsigned** - cg: `cc-cg/68-ptr-cmp.scm` — verifies `%ifelse_ltu` dispatch. - - parse: `cc-parse/75-ptr-cmp.c` — `int a[2]; return &a[1] > &a[0];` + - cc: `cc/75-ptr-cmp.c` — `int a[2]; return &a[1] > &a[0];` → exit 1. - Done: `cg-binop`'s `lt/le/gt/ge` dispatch already picks the unsigned variant for ptr/arr operands. Fixtures lock it in. @@ -419,7 +417,7 @@ responsible for arranging compatible types in the two branches. ### L. Aggregates round 2 - [x] **Flexible array member as last struct field** - - parse: `cc-parse/77-flex-array.c` — backs `struct s { int n; int data[]; };` + - cc: `cc/77-flex-array.c` — backs `struct s { int n; int data[]; };` with an `int[]` buffer, casts to `struct s *`, indexes through `p->data[i]`. tcc.c's `Sym` / `TokenSym` rely on this. - Done: parser already accepted `T name[]` (no size) via the existing @@ -434,7 +432,7 @@ responsible for arranging compatible types in the two branches. become rvals before the bit-cast). - [x] **`T[]` in parameter position decays to `T *`** - - parse: `cc-parse/43-array-param-decay.c` — `int sum(int a[], int n) + - cc: `cc/43-array-param-decay.c` — `int sum(int a[], int n) { ... s+=a[i]; ... } sum(xs,4)` over `{1,2,3,4}` → exit 10. - Done: `parse-fn-params` rewrites arr→ptr (and fn→ptr) before slot allocation, so cg sees an 8-byte ptr slot and the callee's @@ -443,7 +441,7 @@ responsible for arranging compatible types in the two branches. - [x] **Array of function pointers initialized with named functions** - cg: `cc-cg/58-fnptr-tab.scm` - - parse: `cc-parse/58-fnptr-tab.c` + - cc: `cc/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 @@ -454,7 +452,7 @@ responsible for arranging compatible types in the two branches. The CC.md milestones gate on contiguous blocks above. Each lights up once its dependencies are green: -- [ ] **Self-test sweep** (cc-e2e mirroring tests/scheme1) — depends on §A, +- [ ] **Self-test sweep** (cc fixtures mirroring tests/scheme1) — depends on §A, §B, §C, §F, §H. - [ ] **Hand-written hello-world ELF** — depends on §G, §I, §J + a string-formatting libc surface. diff --git a/scripts/boot-run-tests.sh b/scripts/boot-run-tests.sh @@ -14,7 +14,7 @@ ## host preflights lint and passes the explicit kept list down. ## ## Env: ARCH=aarch64|amd64|riscv64 -## Usage: boot-run-tests.sh --suite=<m1pp|p1|scheme1|cc-util|cc-lex|cc-pp|cc-cg|cc-parse|cc-e2e> [name ...] +## Usage: boot-run-tests.sh --suite=<m1pp|p1|scheme1|cc-util|cc-lex|cc-pp|cc-cg|cc> [name ...] set -eu @@ -35,7 +35,7 @@ while [ "$#" -gt 0 ]; do done case "$SUITE" in - m1pp|p1|scheme1|cc-util|cc-lex|cc-pp|cc-cg|cc-parse|cc-e2e) ;; + m1pp|p1|scheme1|cc-util|cc-lex|cc-pp|cc-cg|cc) ;; "") echo "$0: --suite required" >&2; exit 2 ;; *) echo "$0: unknown suite '$SUITE'" >&2; exit 2 ;; esac @@ -371,38 +371,33 @@ run_cc_cg_suite() { "scheme1/prelude.scm cc/util.scm cc/data.scm cc/cg.scm" 0 } -run_cc_parse_suite() { - _cc_runtime_suite cc-parse c \ - "scheme1/prelude.scm cc/util.scm cc/data.scm cc/lex.scm cc/pp.scm cc/cg.scm cc/parse.scm tests/cc-parse/_run-parse.scm" 1 -} - -run_cc_e2e_suite() { - [ -n "$NAMES" ] || NAMES=$(discover tests/cc-e2e c) +run_cc_suite() { + [ -n "$NAMES" ] || NAMES=$(discover tests/cc c) for name in $NAMES; do - src=tests/cc-e2e/$name.c + src=tests/cc/$name.c [ -e "$src" ] || { echo " SKIP $name (no .c)"; continue; } - if [ -e tests/cc-e2e/$name.expected ]; then - expout=$(cat tests/cc-e2e/$name.expected) + if [ -e tests/cc/$name.expected ]; then + expout=$(cat tests/cc/$name.expected) else expout= fi - if [ -e tests/cc-e2e/$name.expected-exit ]; then - expexit=$(cat tests/cc-e2e/$name.expected-exit) + if [ -e tests/cc/$name.expected-exit ]; then + expexit=$(cat tests/cc/$name.expected-exit) else expexit=0 fi - outdir=build/$ARCH/cc-e2e/$name + outdir=build/$ARCH/cc/$name p1pp=$outdir/$name.P1pp elf=$outdir/$name mkdir -p "$outdir" if ! build/$ARCH/scheme1 build/$ARCH/cc/cc.scm "$src" "$p1pp" \ >/dev/null 2>&1; then - report "[$ARCH] cc-e2e/$name" FAIL + report "[$ARCH] cc/$name" FAIL echo " cc compile failed" continue fi if ! sh scripts/boot-build-p1pp.sh "$p1pp" "$elf" >/dev/null 2>&1; then - report "[$ARCH] cc-e2e/$name" FAIL + report "[$ARCH] cc/$name" FAIL sh scripts/boot-build-p1pp.sh "$p1pp" "$elf" 2>&1 \ | sed 's/^/ /' >&2 || true continue @@ -414,7 +409,7 @@ run_cc_e2e_suite() { act_exit=$? fi act_out=$(cat "$tmp"); rm -f "$tmp" - _cc_check "[$ARCH] cc-e2e/$name" "$expout" "$expexit" "$act_out" "$act_exit" + _cc_check "[$ARCH] cc/$name" "$expout" "$expexit" "$act_out" "$act_exit" done } @@ -426,6 +421,5 @@ case "$SUITE" in cc-lex) run_cc_lex_suite ;; cc-pp) run_cc_pp_suite ;; cc-cg) run_cc_cg_suite ;; - cc-parse) run_cc_parse_suite ;; - cc-e2e) run_cc_e2e_suite ;; + cc) run_cc_suite ;; esac diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh @@ -24,8 +24,7 @@ ## cc-lex tests/cc-lex/<name>.c — lex pipeline byte-diff. ## cc-pp tests/cc-pp/<name>.c — pp pipeline byte-diff (+ .scm). ## cc-cg tests/cc-cg/<name>.scm — cg emit -> P1pp -> ELF -> run. -## cc-parse tests/cc-parse/<name>.c — full pipeline -> ELF -> run. -## cc-e2e tests/cc-e2e/<name>.c — cc -> P1pp -> ELF -> run. +## cc tests/cc/<name>.c — cc -> P1pp -> ELF -> run. ## ## All three arches by default; --arch restricts to one. ## @@ -51,8 +50,8 @@ while [ "$#" -gt 0 ]; do done case "$SUITE" in - m1pp|p1|scheme1|cc-util|cc-lex|cc-pp|cc-cg|cc-parse|cc-e2e) ;; - "") echo "$0: --suite required (m1pp | p1 | scheme1 | cc-util | cc-lex | cc-pp | cc-cg | cc-parse | cc-e2e)" >&2; exit 2 ;; + m1pp|p1|scheme1|cc-util|cc-lex|cc-pp|cc-cg|cc) ;; + "") echo "$0: --suite required (m1pp | p1 | scheme1 | cc-util | cc-lex | cc-pp | cc-cg | cc)" >&2; exit 2 ;; *) echo "$0: unknown suite '$SUITE'" >&2; exit 2 ;; esac diff --git a/tests/cc-parse/_run-parse.scm b/tests/cc-parse/_run-parse.scm @@ -1,46 +0,0 @@ -;; tests/cc-parse/_run-parse.scm — driver for cc-parse .c fixtures. -;; -;; argv[0] = scheme1 binary path -;; argv[1] = combined source (assembled by the test runner via catm) -;; argv[2] = .c fixture path -;; -;; Runs the full lex+pp+parse+cg pipeline against the fixture and -;; writes the emitted P1pp text to stdout. Same plumbing as cc-main, -;; minus the -D/-o flag handling. - -(define (%basename path) - (let* ((n (bytevector-length path))) - (let loop ((i (- n 1))) - (cond - ((< i 0) path) - ((= (bytevector-u8-ref path i) 47) - (bv-slice path (+ i 1) n)) - (else (loop (- i 1))))))) - -(define (%run-parse path) - (let ((op (open-input path))) - (if (not (car op)) - (begin - (write-bv-fd 2 "run-parse: cannot open ") - (write-bv-fd 2 path) - (write-bv-fd 2 NL-BV) - (sys-exit 2)) - (let* ((src (slurp-fd (port-fd (cdr op)))) - (file (%basename path)) - (toks (lex-tokenize src file)) - (expanded (pp-expand toks '())) - (cg (cg-init)) - (ps (make-pstate expanded cg))) - (sys-close (port-fd (cdr op))) - (parse-translation-unit ps) - (write-bv-fd 1 (cg-finish cg)) - (sys-exit 0))))) - -(let ((args (argv))) - (cond - ((null? args) (sys-exit 2)) - ((null? (cdr args)) (sys-exit 2)) - ((null? (cdr (cdr args))) - (write-bv-fd 2 "run-parse: missing fixture path\n") - (sys-exit 2)) - (else (%run-parse (car (cdr (cdr args))))))) diff --git a/tests/cc-parse/00-empty-main.c b/tests/cc/00-empty-main.c diff --git a/tests/cc-e2e/01-kitchen-sink.expected-exit b/tests/cc/00-empty-main.expected-exit diff --git a/tests/cc-e2e/00-return-argc.c b/tests/cc/00-return-argc.c diff --git a/tests/cc-e2e/00-return-argc.expected-exit b/tests/cc/00-return-argc.expected-exit diff --git a/tests/cc-e2e/01-kitchen-sink.c b/tests/cc/01-kitchen-sink.c diff --git a/tests/cc-e2e/02-arith.expected-exit b/tests/cc/01-kitchen-sink.expected-exit diff --git a/tests/cc-parse/01-return-argc.c b/tests/cc/01-return-argc.c diff --git a/tests/cc-parse/01-return-argc.expected-exit b/tests/cc/01-return-argc.expected-exit diff --git a/tests/cc-parse/02-add-const.c b/tests/cc/02-add-const.c diff --git a/tests/cc-parse/02-add-const.expected-exit b/tests/cc/02-add-const.expected-exit diff --git a/tests/cc-e2e/02-arith.c b/tests/cc/02-arith.c diff --git a/tests/cc-e2e/03-compound.expected-exit b/tests/cc/02-arith.expected-exit diff --git a/tests/cc-e2e/03-compound.c b/tests/cc/03-compound.c diff --git a/tests/cc-e2e/04-inc-dec.expected-exit b/tests/cc/03-compound.expected-exit diff --git a/tests/cc-parse/03-local-assign.c b/tests/cc/03-local-assign.c diff --git a/tests/cc-parse/03-local-assign.expected-exit b/tests/cc/03-local-assign.expected-exit diff --git a/tests/cc-parse/04-if-else.c b/tests/cc/04-if-else.c diff --git a/tests/cc-parse/04-if-else.expected-exit b/tests/cc/04-if-else.expected-exit diff --git a/tests/cc-e2e/04-inc-dec.c b/tests/cc/04-inc-dec.c diff --git a/tests/cc-e2e/05-logical.expected-exit b/tests/cc/04-inc-dec.expected-exit diff --git a/tests/cc-e2e/05-logical.c b/tests/cc/05-logical.c diff --git a/tests/cc-e2e/06-ternary.expected-exit b/tests/cc/05-logical.expected-exit diff --git a/tests/cc-parse/05-while-break.c b/tests/cc/05-while-break.c diff --git a/tests/cc-parse/05-while-break.expected-exit b/tests/cc/05-while-break.expected-exit diff --git a/tests/cc-parse/06-call-no-args.c b/tests/cc/06-call-no-args.c diff --git a/tests/cc-parse/06-call-no-args.expected-exit b/tests/cc/06-call-no-args.expected-exit diff --git a/tests/cc-e2e/06-ternary.c b/tests/cc/06-ternary.c diff --git a/tests/cc-e2e/07-compare.expected-exit b/tests/cc/06-ternary.expected-exit diff --git a/tests/cc-parse/07-call-with-args.c b/tests/cc/07-call-with-args.c diff --git a/tests/cc-parse/07-call-with-args.expected-exit b/tests/cc/07-call-with-args.expected-exit diff --git a/tests/cc-e2e/07-compare.c b/tests/cc/07-compare.c diff --git a/tests/cc-e2e/08-pointer.expected-exit b/tests/cc/07-compare.expected-exit diff --git a/tests/cc-parse/08-pointer-deref.c b/tests/cc/08-pointer-deref.c diff --git a/tests/cc-parse/08-pointer-deref.expected-exit b/tests/cc/08-pointer-deref.expected-exit diff --git a/tests/cc-e2e/08-pointer.c b/tests/cc/08-pointer.c diff --git a/tests/cc-e2e/09-array.expected-exit b/tests/cc/08-pointer.expected-exit diff --git a/tests/cc-parse/09-address-of.c b/tests/cc/09-address-of.c diff --git a/tests/cc-parse/09-address-of.expected-exit b/tests/cc/09-address-of.expected-exit diff --git a/tests/cc-e2e/09-array.c b/tests/cc/09-array.c diff --git a/tests/cc-e2e/10-array-2d.expected-exit b/tests/cc/09-array.expected-exit diff --git a/tests/cc-e2e/10-array-2d.c b/tests/cc/10-array-2d.c diff --git a/tests/cc-e2e/11-struct.expected-exit b/tests/cc/10-array-2d.expected-exit diff --git a/tests/cc-parse/10-typedef.c b/tests/cc/10-typedef.c diff --git a/tests/cc-parse/10-typedef.expected-exit b/tests/cc/10-typedef.expected-exit diff --git a/tests/cc-e2e/11-struct.c b/tests/cc/11-struct.c diff --git a/tests/cc-e2e/12-struct-ptr.expected-exit b/tests/cc/11-struct.expected-exit diff --git a/tests/cc-parse/11-two-params.c b/tests/cc/11-two-params.c diff --git a/tests/cc-parse/11-two-params.expected-exit b/tests/cc/11-two-params.expected-exit diff --git a/tests/cc-parse/12-comparison.c b/tests/cc/12-comparison.c diff --git a/tests/cc-parse/12-comparison.expected-exit b/tests/cc/12-comparison.expected-exit diff --git a/tests/cc-e2e/12-struct-ptr.c b/tests/cc/12-struct-ptr.c diff --git a/tests/cc-e2e/13-call.expected-exit b/tests/cc/12-struct-ptr.expected-exit diff --git a/tests/cc-e2e/13-call.c b/tests/cc/13-call.c diff --git a/tests/cc-e2e/14-recursion.expected-exit b/tests/cc/13-call.expected-exit diff --git a/tests/cc-parse/13-while-continue.c b/tests/cc/13-while-continue.c diff --git a/tests/cc-parse/13-while-continue.expected-exit b/tests/cc/13-while-continue.expected-exit diff --git a/tests/cc-parse/14-mul-paren.c b/tests/cc/14-mul-paren.c diff --git a/tests/cc-parse/14-mul-paren.expected-exit b/tests/cc/14-mul-paren.expected-exit diff --git a/tests/cc-e2e/14-recursion.c b/tests/cc/14-recursion.c diff --git a/tests/cc-e2e/15-variadic.expected-exit b/tests/cc/14-recursion.expected-exit diff --git a/tests/cc-parse/15-char-arith.c b/tests/cc/15-char-arith.c diff --git a/tests/cc-parse/15-char-arith.expected-exit b/tests/cc/15-char-arith.expected-exit diff --git a/tests/cc-e2e/15-variadic.c b/tests/cc/15-variadic.c diff --git a/tests/cc-e2e/16-fn-ptr.expected-exit b/tests/cc/15-variadic.expected-exit diff --git a/tests/cc-e2e/16-fn-ptr.c b/tests/cc/16-fn-ptr.c diff --git a/tests/cc-e2e/17-apply.expected-exit b/tests/cc/16-fn-ptr.expected-exit diff --git a/tests/cc-parse/16-short-arith.c b/tests/cc/16-short-arith.c diff --git a/tests/cc-parse/16-short-arith.expected-exit b/tests/cc/16-short-arith.expected-exit diff --git a/tests/cc-e2e/17-apply.c b/tests/cc/17-apply.c diff --git a/tests/cc-e2e/18-ptr-recursion.expected-exit b/tests/cc/17-apply.expected-exit diff --git a/tests/cc-parse/17-int-arith.c b/tests/cc/17-int-arith.c diff --git a/tests/cc-parse/17-int-arith.expected-exit b/tests/cc/17-int-arith.expected-exit diff --git a/tests/cc-e2e/18-ptr-recursion.c b/tests/cc/18-ptr-recursion.c diff --git a/tests/cc-e2e/19-static.expected-exit b/tests/cc/18-ptr-recursion.expected-exit diff --git a/tests/cc-parse/18-sext-narrow.c b/tests/cc/18-sext-narrow.c diff --git a/tests/cc-parse/18-sext-narrow.expected-exit b/tests/cc/18-sext-narrow.expected-exit diff --git a/tests/cc-e2e/19-static.c b/tests/cc/19-static.c diff --git a/tests/cc-e2e/20-switch.expected-exit b/tests/cc/19-static.expected-exit diff --git a/tests/cc-parse/19-zext-narrow.c b/tests/cc/19-zext-narrow.c diff --git a/tests/cc-parse/19-zext-narrow.expected-exit b/tests/cc/19-zext-narrow.expected-exit diff --git a/tests/cc-parse/20-promote-sign.c b/tests/cc/20-promote-sign.c diff --git a/tests/cc-parse/20-promote-sign.expected-exit b/tests/cc/20-promote-sign.expected-exit diff --git a/tests/cc-e2e/20-switch.c b/tests/cc/20-switch.c diff --git a/tests/cc-e2e/21-goto.expected-exit b/tests/cc/20-switch.expected-exit diff --git a/tests/cc-e2e/21-goto.c b/tests/cc/21-goto.c diff --git a/tests/cc-e2e/22-loops.expected-exit b/tests/cc/21-goto.expected-exit diff --git a/tests/cc-parse/21-preinc.c b/tests/cc/21-preinc.c diff --git a/tests/cc-parse/21-preinc.expected-exit b/tests/cc/21-preinc.expected-exit diff --git a/tests/cc-e2e/22-loops.c b/tests/cc/22-loops.c diff --git a/tests/cc-e2e/23-strings.expected-exit b/tests/cc/22-loops.expected-exit diff --git a/tests/cc-parse/22-postinc.c b/tests/cc/22-postinc.c diff --git a/tests/cc-parse/22-postinc.expected-exit b/tests/cc/22-postinc.expected-exit diff --git a/tests/cc-parse/23-cmpd-simple.c b/tests/cc/23-cmpd-simple.c diff --git a/tests/cc-parse/23-cmpd-simple.expected-exit b/tests/cc/23-cmpd-simple.expected-exit diff --git a/tests/cc-e2e/23-strings.c b/tests/cc/23-strings.c diff --git a/tests/cc-e2e/24-globals.expected-exit b/tests/cc/23-strings.expected-exit diff --git a/tests/cc-parse/24-cmpd-ptr.c b/tests/cc/24-cmpd-ptr.c diff --git a/tests/cc-parse/24-cmpd-ptr.expected-exit b/tests/cc/24-cmpd-ptr.expected-exit diff --git a/tests/cc-e2e/24-globals.c b/tests/cc/24-globals.c diff --git a/tests/cc-e2e/25-sizeof.expected-exit b/tests/cc/24-globals.expected-exit diff --git a/tests/cc-parse/25-deref-postinc.c b/tests/cc/25-deref-postinc.c diff --git a/tests/cc-parse/25-deref-postinc.expected-exit b/tests/cc/25-deref-postinc.expected-exit diff --git a/tests/cc-e2e/25-sizeof.c b/tests/cc/25-sizeof.c diff --git a/tests/cc-e2e/26-enum.expected-exit b/tests/cc/25-sizeof.expected-exit diff --git a/tests/cc-e2e/26-enum.c b/tests/cc/26-enum.c diff --git a/tests/cc-e2e/27-void-call.expected-exit b/tests/cc/26-enum.expected-exit diff --git a/tests/cc-parse/26-sizeof-expr.c b/tests/cc/26-sizeof-expr.c diff --git a/tests/cc-parse/26-sizeof-expr.expected-exit b/tests/cc/26-sizeof-expr.expected-exit diff --git a/tests/cc-parse/27-sizeof-types.c b/tests/cc/27-sizeof-types.c diff --git a/tests/cc-parse/27-sizeof-types.expected-exit b/tests/cc/27-sizeof-types.expected-exit diff --git a/tests/cc-e2e/27-void-call.c b/tests/cc/27-void-call.c diff --git a/tests/cc-e2e/28-cast.expected-exit b/tests/cc/27-void-call.expected-exit diff --git a/tests/cc-e2e/28-cast.c b/tests/cc/28-cast.c diff --git a/tests/cc-e2e/29-void-ptr.expected-exit b/tests/cc/28-cast.expected-exit diff --git a/tests/cc-parse/28-ternary.c b/tests/cc/28-ternary.c diff --git a/tests/cc-parse/28-ternary.expected-exit b/tests/cc/28-ternary.expected-exit diff --git a/tests/cc-parse/29-land.c b/tests/cc/29-land.c diff --git a/tests/cc-parse/29-land.expected-exit b/tests/cc/29-land.expected-exit diff --git a/tests/cc-e2e/29-void-ptr.c b/tests/cc/29-void-ptr.c diff --git a/tests/cc-e2e/30-comma.expected-exit b/tests/cc/29-void-ptr.expected-exit diff --git a/tests/cc-e2e/30-comma.c b/tests/cc/30-comma.c diff --git a/tests/cc-e2e/31-addr-array.expected-exit b/tests/cc/30-comma.expected-exit diff --git a/tests/cc-parse/30-lor.c b/tests/cc/30-lor.c diff --git a/tests/cc-parse/30-lor.expected-exit b/tests/cc/30-lor.expected-exit diff --git a/tests/cc-e2e/31-addr-array.c b/tests/cc/31-addr-array.c diff --git a/tests/cc-e2e/32-local-struct-desig.expected-exit b/tests/cc/31-addr-array.expected-exit diff --git a/tests/cc-parse/31-comma.c b/tests/cc/31-comma.c diff --git a/tests/cc-parse/31-comma.expected-exit b/tests/cc/31-comma.expected-exit diff --git a/tests/cc-e2e/32-local-struct-desig.c b/tests/cc/32-local-struct-desig.c diff --git a/tests/cc-parse/00-empty-main.expected-exit b/tests/cc/32-local-struct-desig.expected-exit diff --git a/tests/cc-parse/36-struct-load.c b/tests/cc/36-struct-load.c diff --git a/tests/cc-parse/36-struct-load.expected-exit b/tests/cc/36-struct-load.expected-exit diff --git a/tests/cc-parse/37-struct-store.c b/tests/cc/37-struct-store.c diff --git a/tests/cc-parse/37-struct-store.expected-exit b/tests/cc/37-struct-store.expected-exit diff --git a/tests/cc-parse/38-arrow.c b/tests/cc/38-arrow.c diff --git a/tests/cc-parse/38-arrow.expected-exit b/tests/cc/38-arrow.expected-exit diff --git a/tests/cc-parse/39-struct-nested.c b/tests/cc/39-struct-nested.c diff --git a/tests/cc-parse/39-struct-nested.expected-exit b/tests/cc/39-struct-nested.expected-exit diff --git a/tests/cc-parse/40-array-index.c b/tests/cc/40-array-index.c diff --git a/tests/cc-parse/40-array-index.expected-exit b/tests/cc/40-array-index.expected-exit diff --git a/tests/cc-parse/41-array-2d.c b/tests/cc/41-array-2d.c diff --git a/tests/cc-parse/41-array-2d.expected-exit b/tests/cc/41-array-2d.expected-exit diff --git a/tests/cc-parse/42-struct-fn-arg.c b/tests/cc/42-struct-fn-arg.c diff --git a/tests/cc-parse/42-struct-fn-arg.expected-exit b/tests/cc/42-struct-fn-arg.expected-exit diff --git a/tests/cc-parse/43-array-param-decay.c b/tests/cc/43-array-param-decay.c diff --git a/tests/cc-parse/43-array-param-decay.expected-exit b/tests/cc/43-array-param-decay.expected-exit diff --git a/tests/cc-parse/49-init-scalar-global.c b/tests/cc/49-init-scalar-global.c diff --git a/tests/cc-parse/49-init-scalar-global.expected-exit b/tests/cc/49-init-scalar-global.expected-exit diff --git a/tests/cc-parse/50-init-addr.c b/tests/cc/50-init-addr.c diff --git a/tests/cc-parse/50-init-addr.expected-exit b/tests/cc/50-init-addr.expected-exit diff --git a/tests/cc-parse/51-init-array-list.c b/tests/cc/51-init-array-list.c diff --git a/tests/cc-parse/51-init-array-list.expected-exit b/tests/cc/51-init-array-list.expected-exit diff --git a/tests/cc-parse/52-init-array-str.c b/tests/cc/52-init-array-str.c diff --git a/tests/cc-parse/52-init-array-str.expected-exit b/tests/cc/52-init-array-str.expected-exit diff --git a/tests/cc-parse/53-init-struct-pos.c b/tests/cc/53-init-struct-pos.c diff --git a/tests/cc-parse/53-init-struct-pos.expected-exit b/tests/cc/53-init-struct-pos.expected-exit diff --git a/tests/cc-parse/54-init-struct-desig.c b/tests/cc/54-init-struct-desig.c diff --git a/tests/cc-parse/54-init-struct-desig.expected-exit b/tests/cc/54-init-struct-desig.expected-exit diff --git a/tests/cc-parse/55-init-local-array.c b/tests/cc/55-init-local-array.c diff --git a/tests/cc-parse/55-init-local-array.expected-exit b/tests/cc/55-init-local-array.expected-exit diff --git a/tests/cc-parse/56-init-local-struct.c b/tests/cc/56-init-local-struct.c diff --git a/tests/cc-parse/56-init-local-struct.expected-exit b/tests/cc/56-init-local-struct.expected-exit diff --git a/tests/cc-parse/57-block-static.c b/tests/cc/57-block-static.c diff --git a/tests/cc-parse/57-block-static.expected-exit b/tests/cc/57-block-static.expected-exit diff --git a/tests/cc-parse/58-fnptr-tab.c b/tests/cc/58-fnptr-tab.c diff --git a/tests/cc-parse/58-fnptr-tab.expected-exit b/tests/cc/58-fnptr-tab.expected-exit diff --git a/tests/cc-parse/63-do-while.c b/tests/cc/63-do-while.c diff --git a/tests/cc-parse/63-do-while.expected-exit b/tests/cc/63-do-while.expected-exit diff --git a/tests/cc-parse/64-for-decl.c b/tests/cc/64-for-decl.c diff --git a/tests/cc-parse/64-for-decl.expected-exit b/tests/cc/64-for-decl.expected-exit diff --git a/tests/cc-parse/65-switch.c b/tests/cc/65-switch.c diff --git a/tests/cc-parse/65-switch.expected-exit b/tests/cc/65-switch.expected-exit diff --git a/tests/cc-parse/66-goto.c b/tests/cc/66-goto.c diff --git a/tests/cc-parse/66-goto.expected-exit b/tests/cc/66-goto.expected-exit diff --git a/tests/cc-parse/67-vararg-call.c b/tests/cc/67-vararg-call.c diff --git a/tests/cc-parse/67-vararg-call.expected-exit b/tests/cc/67-vararg-call.expected-exit diff --git a/tests/cc-parse/68-main-noret.c b/tests/cc/68-main-noret.c diff --git a/tests/cc-parse/68-main-noret.expected-exit b/tests/cc/68-main-noret.expected-exit diff --git a/tests/cc-parse/69-multi-fn.c b/tests/cc/69-multi-fn.c diff --git a/tests/cc-parse/69-multi-fn.expected-exit b/tests/cc/69-multi-fn.expected-exit diff --git a/tests/cc-parse/71-fnptr-call.c b/tests/cc/71-fnptr-call.c diff --git a/tests/cc-parse/71-fnptr-call.expected-exit b/tests/cc/71-fnptr-call.expected-exit diff --git a/tests/cc-parse/72-enum-const.c b/tests/cc/72-enum-const.c diff --git a/tests/cc-parse/72-enum-const.expected-exit b/tests/cc/72-enum-const.expected-exit diff --git a/tests/cc-parse/73-voidptr-impl.c b/tests/cc/73-voidptr-impl.c diff --git a/tests/cc-parse/73-voidptr-impl.expected-exit b/tests/cc/73-voidptr-impl.expected-exit diff --git a/tests/cc-parse/74-call-narrow.c b/tests/cc/74-call-narrow.c diff --git a/tests/cc-parse/74-call-narrow.expected-exit b/tests/cc/74-call-narrow.expected-exit diff --git a/tests/cc-parse/75-ptr-cmp.c b/tests/cc/75-ptr-cmp.c diff --git a/tests/cc-parse/75-ptr-cmp.expected-exit b/tests/cc/75-ptr-cmp.expected-exit diff --git a/tests/cc-parse/76-vararg-recv.c b/tests/cc/76-vararg-recv.c diff --git a/tests/cc-parse/76-vararg-recv.expected-exit b/tests/cc/76-vararg-recv.expected-exit diff --git a/tests/cc-parse/77-flex-array.c b/tests/cc/77-flex-array.c diff --git a/tests/cc-parse/77-flex-array.expected-exit b/tests/cc/77-flex-array.expected-exit diff --git a/tests/cc-parse/78-voidptr-arith.c b/tests/cc/78-voidptr-arith.c diff --git a/tests/cc-parse/78-voidptr-arith.expected-exit b/tests/cc/78-voidptr-arith.expected-exit diff --git a/tests/cc-parse/79-vararg-deep.c b/tests/cc/79-vararg-deep.c diff --git a/tests/cc-parse/79-vararg-deep.expected-exit b/tests/cc/79-vararg-deep.expected-exit diff --git a/tests/cc-parse/80-addr-array.c b/tests/cc/80-addr-array.c diff --git a/tests/cc-parse/80-addr-array.expected-exit b/tests/cc/80-addr-array.expected-exit diff --git a/tests/cc-parse/81-void-call.c b/tests/cc/81-void-call.c diff --git a/tests/cc-parse/81-void-call.expected-exit b/tests/cc/81-void-call.expected-exit