commit ed1a327c76414859d1bfaaf2dc037e11f7c7977e parent 3917becca3ca07243e9f5fe602981914c6880d30 Author: Ryan Sepassi <rsepassi@gmail.com> Date: Sun, 26 Apr 2026 20:53:04 -0700 cc tests: switch cc-cg/cc-parse to runtime-validating fixtures Drop all P1pp text goldens. Each fixture now compiles to a runnable program that the harness builds via boot-build-p1pp.sh, runs, and diffs exit code (.expected-exit, default 0) + optional stdout (.expected). cc-util/cc-lex/cc-pp keep their byte-diff form for pure transformations. Establishes the feature workflow in docs/CC-INTERNALS.md: cc-cg test + cg impl, then cc-parse test + parse impl. Each layer has its own runtime contract; cg can be refactored as long as behavior holds. Rewrites of fixtures that depended on text-shape only: - 08 split into terminating-break + decrementing-param continue. - 09, 13 define their callees in-TU (were extern, unlinkable). - 10 runtime-tests string-pool intern via pointer compare. - 12 dropped (bare cg-finish has no cc__main, unlinkable). - cc-parse fixtures grew runnable mains; helpers preserved where the test is about call/param shape. 15/15 cc-cg + 15/15 cc-parse pass on aarch64. Diffstat:
84 files changed, 402 insertions(+), 793 deletions(-)
diff --git a/Makefile b/Makefile @@ -253,15 +253,17 @@ TEST_P1_DEPS := $(foreach a,$(TEST_ARCHES), \ TEST_SCHEME1_DEPS := $(foreach a,$(TEST_ARCHES), \ build/$(a)/.image build/$(a)/tools/M0 build/$(a)/m1pp build/$(a)/scheme1) -# cc-* unit suites (lex / pp / cg / parse / util) just need scheme1 + -# the catm'd cc compiler source as artifacts. +# 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. TEST_CC_UNIT_DEPS := $(foreach a,$(TEST_ARCHES), \ - build/$(a)/.image build/$(a)/tools/M0 build/$(a)/m1pp build/$(a)/scheme1 \ - build/$(a)/cc/cc.scm) + build/$(a)/.image build/$(a)/tools/M0 build/$(a)/m1pp build/$(a)/scheme1) -# cc-e2e additionally needs the P1pp toolchain to assemble cc-emitted -# P1pp into native ELF. -TEST_CC_E2E_DEPS := $(TEST_CC_UNIT_DEPS) +TEST_CC_E2E_DEPS := $(TEST_CC_UNIT_DEPS) \ + $(foreach a,$(TEST_ARCHES),build/$(a)/cc/cc.scm) test: ifeq ($(SUITE),) diff --git a/cc/README.md b/cc/README.md @@ -37,14 +37,29 @@ Run via the existing `boot-run-scheme1.sh` wrapper, which prepends ## Status -Scaffolded. Every public function body is `(error "TBD: <name>")`. -Engineers fill in their assigned module; the contracts in -CC-CONTRACTS.md keep the interfaces stable. +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. + +## Workflow + +Per `docs/CC-INTERNALS.md` §Feature workflow, every codegen-touching +change lands in this order: + +1. cc-cg fixture (red). +2. cg implementation (green). +3. cc-parse fixture (red). +4. parse implementation (green). + +Both cc-cg and cc-parse are runtime-validating: the harness builds the +emitted P1pp into a real ELF, runs it, and asserts exit code / stdout. +No P1pp-text goldens. ## Tests -- `tests/cc-lex/` — lexer goldens (token serialization) -- `tests/cc-pp/` — preprocessor goldens -- `tests/cc-parse/` — parser → cg-trace mock -- `tests/cc-cg/` — direct cg API tests -- `tests/cc-e2e/` — `.c` → ELF → run +- `tests/cc-util/` — util helpers (Scheme unit tests) +- `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 diff --git a/docs/CC-CONTRACTS.md b/docs/CC-CONTRACTS.md @@ -162,8 +162,13 @@ symbols; the parser maps PUNCT → cg-op (e.g., `'plus` → `'add`, ## 2. Test serialization formats -All test goldens use Scheme-readable forms so they `diff` cleanly and -can be machine-parsed if useful. +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`) + compile-and-run the emitted program and assert runtime behavior + (§2.2). No P1pp-text goldens. ### 2.1 Token line format @@ -203,61 +208,36 @@ Example for `int main() { return 0; }` in `t.c`: Trailing whitespace and `;`-comments in the golden file are ignored. -### 2.2 cg-trace line format +### 2.2 Runtime fixture format -The cg-trace mock writes one Scheme list per cg call: +Every cc-cg / cc-parse / cc-e2e fixture compiles to a runnable program +whose runtime behavior is the assertion. Two sibling files describe +the expectation: ``` -(<call-name> <arg1> <arg2> ...) +<name>.expected-exit # decimal integer; default 0 if absent +<name>.expected # exact stdout match; default empty if absent ``` -`<call-name>` strips the `cg-` prefix (`cg-push-imm` → `push-imm`, -`cg-fn-begin` → `fn-begin`). - -Argument renderers, applied per-call: +Stdout and stderr are merged in the runner. The harness exits non-zero +if either expectation fails. -- **ctype** → a stable symbolic form: - - primitives: `void`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, - `i64`, `u64`, `bool` (the `kind` symbol verbatim). - - pointer: `(ptr <T>)`. - - array: `(arr <T> <N>)` where N is the length or `*` for - incomplete. - - function: `(fn <ret> (<param>...) <variadic?>)`. - - aggregates: `(struct <tag>)`, `(union <tag>)`, `(enum <tag>)`. -- **sym** → `(<name-bv> <kind-symbol>)`. Storage and slot are not - surfaced — they are implementation detail. -- **bv** → bytevector literal, as in §2.1. -- **fixnum** → decimal integer. -- **bool** → `#t` / `#f`. -- **op symbol** (binop/unop) → bare symbol. +Fixture-source conventions: -cg calls that take *thunks* (`cg-if`, `cg-ifelse`, `cg-loop`) emit a -matching open/close pair in the trace, with the body's calls in -between: - -``` -(if-begin) - ...body trace... -(if-end) - -(ifelse-begin) - ...then-trace... -(ifelse-mid) - ...else-trace... -(ifelse-end) - -(loop-begin) - ...head trace... - ...body trace, with (break <tag>) / (continue <tag>) using the - tag cg passed to body-thunk... -(loop-end) -``` +- **cc-cg** (`<name>.scm`): drives the cg API directly, ending in + `(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. -This is the canonical surface — `cg-if` *internally* uses a thunk, -but the trace exposes begin/mid/end markers so tests can read top-down. -The loop tag is allocated by cg (CC-CONTRACTS §3.3) and identifies -which loop a break/continue refers to; it does not appear in the -loop-begin / loop-end markers themselves. +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 +rather than asserting exact stdout. ### 2.3 Diagnostic format diff --git a/docs/CC-INTERNALS.md b/docs/CC-INTERNALS.md @@ -36,6 +36,30 @@ data ────────┼─► lex ──► pp ──► parse Cycles are forbidden. parse.scm calls cg.scm but never the reverse. +## Feature workflow + +When adding any new codegen-touching feature, **always** in this order: + +1. **cc-cg fixture first.** Write a `tests/cc-cg/<n>-name.scm` that drives + the cg API directly to emit a self-contained program whose runtime + exit code (and/or stdout) reflects the feature working. Add the + `.expected-exit` (default `0`) and optional `.expected` stdout file. + 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 + 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. + +This sequencing keeps the parser↔cg seam honest: cg is validated by +runtime behavior on direct API calls before parse is even touched, so +parse never papers over a cg bug. Steps 1+3 are the contract; steps 2+4 +are the implementation. + ## Conventions - **Naming**: every public function and accessor is prefixed by its @@ -542,20 +566,23 @@ beat seven.) (cg-alloc-slot cg bytes align) -> offset ; bumps frame-hi; returns aligned offset ``` -### Interaction-test mock +### Test plan -`tests/cc-parse/` uses a swap-in `cg-trace.scm` that replaces cg.scm. -It provides every public entry point above but each call appends a -record to a global trace list: +`tests/cc-cg/` is a runtime-validating suite. Each fixture is a `.scm` +program that drives the cg API directly, calls `cg-finish`, and writes +the resulting P1pp text to stdout. The harness assembles that P1pp +through the existing P1pp toolchain, runs the resulting ELF, and +diffs the program's exit code against `<name>.expected-exit` (default +`0`) and stdout against `<name>.expected` (default empty). -```scheme -(cg-trace-get) -> (list-of (op . args)) -``` +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 +real `libc` linkage exists; they don't fit cc-cg. -Parser tests run a fragment, snapshot the trace, diff against -`expected-trace`. This is the contract that lets parse and cg evolve -independently — as long as parse emits the same sequence of cg calls, -cg internals can change freely. +The contract this suite locks in is **semantic**, not syntactic: cg +emission can be refactored freely as long as the program still +computes the asserted result. ## parse.scm @@ -652,19 +679,17 @@ opnd. Statements that don't consume the value follow with `cg-pop`. ### Test plan -`tests/cc-parse/` uses the cg-trace mock. Each test: - -``` -input.c -- C fragment -expected-trace -- one cg call per line, e.g. - (cg-push-imm i32 42) - (cg-push-imm i32 7) - (cg-binop add) - ... -``` +`tests/cc-parse/` 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 +P1pp toolchain, runs the resulting ELF, and diffs exit code against +`<name>.expected-exit` (default `0`) and stdout against +`<name>.expected` (default empty). -The driver builds a token list (via the real lex+pp) and runs -`parse-translation-unit` against `cg-trace`. Diff fails the test. +The contract this suite locks in is that the *C source* compiles to a +program with the asserted runtime behavior. Per the §Feature workflow, +parse fixtures land *after* the corresponding cg fixture is green. ## main.scm @@ -692,24 +717,33 @@ unrecognized flags die. ## Test infrastructure -Three test trees, all using the same harness pattern as -`tests/scheme1/`: +Five test trees. The upstream layers (lex, pp) byte-diff their pure +transformations; the downstream layers (cg, parse, e2e) 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 +correct" question to ask. +- `tests/cc-util/` — direct unit tests of util helpers (Scheme-level). - `tests/cc-lex/` — feeds `.c` through `lex-tokenize`, diffs token - serialization. + serialization (per CC-CONTRACTS §2.1). - `tests/cc-pp/` — feeds tokens (or `.c`) through `pp-expand`, diffs token serialization. -- `tests/cc-parse/` — feeds `.c` through lex+pp+parse with the cg-trace - mock, diffs the trace. -- `tests/cc-cg/` — directly calls cg APIs (handwritten Scheme test - programs), diffs the resulting P1pp bytes. -- `tests/cc-e2e/` — tiny `.c` programs compiled all the way through - the toolchain to native executables, run, exit-code checked. - -`tests/cc-parse/` and `tests/cc-cg/` are the seam that lets parse and -cg evolve independently. Anyone changing parse can keep running until -the trace tests stay green. Anyone changing cg can keep running until -the cg tests stay green and the trace contract is honored. +- `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- +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). ## Out-of-scope here diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh @@ -316,10 +316,12 @@ _cc_arches() { } # _cc_unit_suite <suite-name> <expected-ext> <layer-list> -# Generic unit-suite runner for cc-util / cc-pp / cc-cg / cc-parse. -# Each fixture is `tests/<suite>/<name>.scm`; expected stdout in -# `tests/<suite>/<name>.<expected-ext>` (default empty); exit in +# Generic byte-diff unit-suite runner for cc-util and cc-pp's .scm +# form. Each fixture is `tests/<suite>/<name>.scm`; expected stdout +# in `tests/<suite>/<name>.<expected-ext>` (default empty); exit in # `tests/<suite>/<name>.expected-exit` (default 0). +# cc-cg and cc-parse use _cc_runtime_suite instead — they +# compile-and-run, asserting runtime behavior. _cc_unit_suite() { suite=$1; ext=$2; layers=$3 [ -n "$NAMES" ] || NAMES=$(discover tests/$suite scm) @@ -358,7 +360,7 @@ run_cc_util_suite() { } # _cc_pipeline_suite <suite-name> <expected-ext> <layers> -# Generic pipeline runner for cc-lex / cc-pp / cc-parse. +# Byte-diff pipeline runner for cc-lex and cc-pp's .c form. # Each fixture is tests/<suite>/<name>.c. `<layers>` ends with the # suite-specific _run-<phase>.scm driver, which the test runner passes # the fixture path to as argv[2]. Expected stdout in @@ -422,16 +424,97 @@ run_cc_pp_suite() { "scheme1/prelude.scm cc/util.scm cc/data.scm cc/pp.scm" } -# cc-cg: prelude + util + data + cg. Direct cg API exercises in .scm. +# _cc_runtime_suite <suite-name> <fixture-ext> <layers> [<fixture-as-arg?>] +# Runtime-validating runner for cc-cg and cc-parse. +# +# Each fixture is `tests/<suite>/<name>.<fixture-ext>`. The runner: +# 1. catms <layers> + (cg-driver) into a combined .scm in the +# container's tmpfs. +# 2. Runs scheme1 on it; captures stdout to a per-fixture .P1pp file. +# For cc-parse the .c fixture is passed as argv[2] to the driver +# (fixture-as-arg=1); for cc-cg the .scm fixture *is* the last +# layer (fixture-as-arg=0). +# 3. Assembles that .P1pp through boot-build-p1pp.sh into a native +# ELF. +# 4. Runs the ELF in the container; diffs exit code against +# <name>.expected-exit (default 0) and stdout against +# <name>.expected (default empty). +# +# Any pipeline failure (compile, assemble, run) is a FAIL with the +# first failing stage's stderr surfaced. +_cc_runtime_suite() { + suite=$1; fext=$2; layers=$3; arg_pass=${4:-0} + [ -n "$NAMES" ] || NAMES=$(discover tests/$suite "$fext") + for arch in $(_cc_arches); do + for name in $NAMES; do + fixture=tests/$suite/$name.$fext + [ -e "$fixture" ] || { echo " SKIP $name (no .$fext)"; continue; } + + expout=$([ -e tests/$suite/$name.expected ] \ + && cat tests/$suite/$name.expected || echo "") + expexit=$([ -e tests/$suite/$name.expected-exit ] \ + && cat tests/$suite/$name.expected-exit || echo 0) + + outdir=build/$arch/$suite/$name + p1pp=$outdir/$name.P1pp + elf=$outdir/$name + mkdir -p "$outdir" + + # Stage 1+2: catm + scheme1 → P1pp file. + if [ "$arg_pass" = "1" ]; then + # cc-parse: layers end with the driver; .c fixture is argv[2]. + cmd=" + build/$arch/tools/catm /tmp/cc-test.scm $layers + exec build/$arch/scheme1 /tmp/cc-test.scm $fixture + " + else + # cc-cg: the .scm fixture itself is the last layer. + cmd=" + build/$arch/tools/catm /tmp/cc-test.scm $layers $fixture + exec build/$arch/scheme1 /tmp/cc-test.scm + " + fi + if ! run_in_container "$arch" sh -c "$cmd" >"$p1pp" 2>/dev/null; then + report "[$arch] $suite/$name" FAIL + echo " cg emission failed:" + run_in_container "$arch" sh -c "$cmd" 2>&1 >/dev/null \ + | sed 's/^/ /' >&2 || true + continue + fi + + # Stage 3: P1pp → ELF. + if ! run_in_container "$arch" sh scripts/boot-build-p1pp.sh \ + "$p1pp" "$elf" >/dev/null 2>&1; then + report "[$arch] $suite/$name" FAIL + echo " P1pp assemble failed:" + run_in_container "$arch" sh scripts/boot-build-p1pp.sh \ + "$p1pp" "$elf" 2>&1 | sed 's/^/ /' >&2 || true + continue + fi + + # Stage 4: run + diff. + tmp=$(mktemp) + if run_in_container "$arch" "./$elf" >"$tmp" 2>&1; then + act_exit=0 + else + act_exit=$? + fi + act_out=$(cat "$tmp"); rm -f "$tmp" + _cc_check "[$arch] $suite/$name" "$expout" "$expexit" "$act_out" "$act_exit" + done + done +} + +# cc-cg: prelude + util + data + cg + fixture; emit P1pp; build; run. run_cc_cg_suite() { - _cc_unit_suite cc-cg expected \ - "scheme1/prelude.scm cc/util.scm cc/data.scm cc/cg.scm" + _cc_runtime_suite cc-cg scm \ + "scheme1/prelude.scm cc/util.scm cc/data.scm cc/cg.scm" 0 } -# cc-parse: full pipeline through real cg, P1pp text golden. +# cc-parse: full pipeline through real cg + driver; emit P1pp; build; run. run_cc_parse_suite() { - _cc_pipeline_suite cc-parse expected-p1pp \ - "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" + _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 } # cc-e2e: compile a .c through cc, assemble to ELF, run. diff --git a/tests/cc-cg/00-fn-empty.expected b/tests/cc-cg/00-fn-empty.expected @@ -1,15 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 16, { -%li(a0, 0) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-cg/00-fn-empty.expected-exit b/tests/cc-cg/00-fn-empty.expected-exit @@ -0,0 +1 @@ +0 diff --git a/tests/cc-cg/00-fn-empty.scm b/tests/cc-cg/00-fn-empty.scm @@ -1,9 +1,6 @@ ;; tests/cc-cg/00-fn-empty.scm — minimal direct-cg test. -;; Builds a function that just returns 0, snapshots the P1pp output. -;; -;; Run shape (TBD pending test runner): -;; scheme1 <prelude.scm + cc/*.scm + this file> -;; diff stdout 00-fn-empty.expected-p1pp +;; Models: int main(void) { return 0; } +;; Runtime: exits 0. (let ((cg (cg-init))) (cg-fn-begin cg "main" '() %t-i32) diff --git a/tests/cc-cg/01-return-imm.expected b/tests/cc-cg/01-return-imm.expected @@ -1,15 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 16, { -%li(a0, 42) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-cg/01-return-imm.expected-exit b/tests/cc-cg/01-return-imm.expected-exit @@ -0,0 +1 @@ +42 diff --git a/tests/cc-cg/02-one-param.expected b/tests/cc-cg/02-one-param.expected @@ -1,18 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 32, { -%st(a0, sp, (+ %main__SO 8)) -%ld(t0, sp, (+ %main__SO 8)) -%st(t0, sp, (+ %main__SO 16)) -%ld(a0, sp, (+ %main__SO 16)) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-cg/02-one-param.expected-exit b/tests/cc-cg/02-one-param.expected-exit @@ -0,0 +1 @@ +1 diff --git a/tests/cc-cg/03-two-params.expected b/tests/cc-cg/03-two-params.expected @@ -1,19 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 32, { -%st(a0, sp, (+ %main__SO 8)) -%st(a1, sp, (+ %main__SO 16)) -%ld(t0, sp, (+ %main__SO 8)) -%st(t0, sp, (+ %main__SO 24)) -%ld(a0, sp, (+ %main__SO 24)) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-cg/03-two-params.expected-exit b/tests/cc-cg/03-two-params.expected-exit @@ -0,0 +1 @@ +1 diff --git a/tests/cc-cg/04-binop-add.expected b/tests/cc-cg/04-binop-add.expected @@ -1,19 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 16, { -%li(a0, 3) -%li(a1, 4) -%add(t0, a0, a1) -%st(t0, sp, (+ %main__SO 8)) -%ld(a0, sp, (+ %main__SO 8)) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-cg/04-binop-add.expected-exit b/tests/cc-cg/04-binop-add.expected-exit @@ -0,0 +1 @@ +7 diff --git a/tests/cc-cg/05-load-binop-store.expected b/tests/cc-cg/05-load-binop-store.expected @@ -1,27 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 48, { -%st(a0, sp, (+ %main__SO 8)) -%ld(t0, sp, (+ %main__SO 8)) -%st(t0, sp, (+ %main__SO 16)) -%ld(a0, sp, (+ %main__SO 16)) -%li(a1, 5) -%add(t0, a0, a1) -%st(t0, sp, (+ %main__SO 24)) -%ld(a0, sp, (+ %main__SO 24)) -%st(a0, sp, (+ %main__SO 8)) -%st(a0, sp, (+ %main__SO 32)) -%ld(t0, sp, (+ %main__SO 8)) -%st(t0, sp, (+ %main__SO 40)) -%ld(a0, sp, (+ %main__SO 40)) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-cg/05-load-binop-store.expected-exit b/tests/cc-cg/05-load-binop-store.expected-exit @@ -0,0 +1 @@ +6 diff --git a/tests/cc-cg/06-if.expected b/tests/cc-cg/06-if.expected @@ -1,21 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 16, { -%li(t0, 1) -%if_nez(t0, { -%li(a0, 7) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -}) -%li(a0, 0) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-cg/06-if.expected-exit b/tests/cc-cg/06-if.expected-exit @@ -0,0 +1 @@ +7 diff --git a/tests/cc-cg/07-ifelse.expected b/tests/cc-cg/07-ifelse.expected @@ -1,22 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 16, { -%li(t0, 0) -%ifelse_nez(t0, { -%li(a0, 1) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -}, { -%li(a0, 2) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -}) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-cg/07-ifelse.expected-exit b/tests/cc-cg/07-ifelse.expected-exit @@ -0,0 +1 @@ +2 diff --git a/tests/cc-cg/08-while-break-continue.expected b/tests/cc-cg/08-while-break-continue.expected @@ -1,21 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 16, { -%loop_tag(L0, { -%li(t0, 1) -%if_eqz(t0, { %break(L0) }) -%continue(L0) -%break(L0) -}) -%li(a0, 0) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-cg/08-while-break-continue.scm b/tests/cc-cg/08-while-break-continue.scm @@ -1,21 +0,0 @@ -;; tests/cc-cg/08-while-break-continue.scm — loop with break and continue. -;; Models: while (n) { if (n) continue; break; } -;; Exercises cg-loop's tag return, cg-break, and cg-continue. - -(let ((cg (cg-init))) - (cg-fn-begin cg "main" '() %t-i32) - ;; cg-loop's body-thunk receives the tag (CC-CONTRACTS §3.3); we use - ;; it to issue one continue and one break. - (let ((tag (cg-loop - cg - ;; head: condition is a literal 1 (infinite-ish loop). - (lambda () (cg-push-imm cg %t-i32 1)) - ;; body: emit one continue + one break inline. - (lambda (tag) - (cg-continue cg tag) - (cg-break cg tag))))) - (cg-loop-end cg tag)) - (cg-push-imm cg %t-i32 0) - (cg-return cg) - (cg-fn-end cg) - (write-bv-fd 1 (cg-finish cg))) diff --git a/tests/cc-cg/08-while-break.expected-exit b/tests/cc-cg/08-while-break.expected-exit @@ -0,0 +1 @@ +5 diff --git a/tests/cc-cg/08-while-break.scm b/tests/cc-cg/08-while-break.scm @@ -0,0 +1,15 @@ +;; tests/cc-cg/08-while-break-continue.scm — loop with break only. +;; Models: int main(void) { while (1) { break; } return 5; } +;; Exercises cg-loop's tag return + cg-break with a terminating loop. + +(let ((cg (cg-init))) + (cg-fn-begin cg "main" '() %t-i32) + (let ((tag (cg-loop + cg + (lambda () (cg-push-imm cg %t-i32 1)) + (lambda (tag) (cg-break cg tag))))) + (cg-loop-end cg tag)) + (cg-push-imm cg %t-i32 5) + (cg-return cg) + (cg-fn-end cg) + (write-bv-fd 1 (cg-finish cg))) diff --git a/tests/cc-cg/08b-while-continue.expected-exit b/tests/cc-cg/08b-while-continue.expected-exit @@ -0,0 +1 @@ +9 diff --git a/tests/cc-cg/08b-while-continue.scm b/tests/cc-cg/08b-while-continue.scm @@ -0,0 +1,29 @@ +;; tests/cc-cg/08b-while-continue.scm — loop with continue + decrementing +;; param to ensure termination. +;; Models: int main(int x) { while (x) { x = x - 1; continue; } return 9; } +;; Run: with no argv, argc=1 → x=1 → body runs once, x becomes 0, +;; continue → head sees x=0 → exit loop → return 9. + +(let* ((cg (cg-init)) + (params (cg-fn-begin cg "main" + (list (cons "x" %t-i32)) + %t-i32)) + (x* (cdr (car params)))) + (let ((tag (cg-loop + cg + (lambda () + (cg-push-sym cg x*) (cg-load cg)) + (lambda (tag) + ;; x = x - 1 + (cg-push-sym cg x*) + (cg-push-sym cg x*) (cg-load cg) + (cg-push-imm cg %t-i32 1) + (cg-binop cg 'sub) + (cg-assign cg) + (cg-pop cg) + (cg-continue cg tag))))) + (cg-loop-end cg tag)) + (cg-push-imm cg %t-i32 9) + (cg-return cg) + (cg-fn-end cg) + (write-bv-fd 1 (cg-finish cg))) diff --git a/tests/cc-cg/09-call.expected b/tests/cc-cg/09-call.expected @@ -1,18 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 16, { -%li(a0, 3) -%call(&cc__abs) -%st(a0, sp, (+ %main__SO 8)) -%ld(a0, sp, (+ %main__SO 8)) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-cg/09-call.expected-exit b/tests/cc-cg/09-call.expected-exit @@ -0,0 +1 @@ +9 diff --git a/tests/cc-cg/09-call.scm b/tests/cc-cg/09-call.scm @@ -1,11 +1,28 @@ -;; tests/cc-cg/09-call.scm — call an external one-arg function. -;; Models: extern int abs(int); int main(void) { return abs(3); } +;; tests/cc-cg/09-call.scm — direct call to an in-TU function. +;; Models: int triple(int x) { return x + x + x; } +;; int main(void) { return triple(3); } +;; Exercises cg-call's direct-label path (%call(&cc__triple)) and the +;; one-arg ABI (a0 register-passed). -(let* ((cg (cg-init)) - (fn-ty (%ctype 'fn 8 8 (cons %t-i32 (cons (list %t-i32) #f)))) - (abs-sym (%sym "abs" 'fn 'extern fn-ty #f))) +(let* ((cg (cg-init)) + (triple-fnty (%ctype 'fn 8 8 + (cons %t-i32 (cons (list %t-i32) #f)))) + (triple-sym (%sym "triple" 'fn 'extern triple-fnty #f))) + ;; int triple(int x) { return x + x + x; } + (let* ((params (cg-fn-begin cg "triple" + (list (cons "x" %t-i32)) + %t-i32)) + (x* (cdr (car params)))) + (cg-push-sym cg x*) (cg-load cg) + (cg-push-sym cg x*) (cg-load cg) + (cg-binop cg 'add) + (cg-push-sym cg x*) (cg-load cg) + (cg-binop cg 'add) + (cg-return cg) + (cg-fn-end cg)) + ;; int main(void) { return triple(3); } (cg-fn-begin cg "main" '() %t-i32) - (cg-push-sym cg abs-sym) + (cg-push-sym cg triple-sym) (cg-push-imm cg %t-i32 3) (cg-call cg 1 #t) (cg-return cg) diff --git a/tests/cc-cg/10-string.expected b/tests/cc-cg/10-string.expected @@ -1,23 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 16, { -%li(a0, 0) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) - -:cc__str_0 -"hello" -!(0) - -:cc__str_1 -"world" -!(0) -:ELF_end diff --git a/tests/cc-cg/10-string.expected-exit b/tests/cc-cg/10-string.expected-exit @@ -0,0 +1 @@ +2 diff --git a/tests/cc-cg/10-string.scm b/tests/cc-cg/10-string.scm @@ -1,15 +1,37 @@ -;; tests/cc-cg/10-string.scm — string literal interning. -;; Pushes "hello" twice; the second push should reuse the same str label. +;; tests/cc-cg/10-string.scm — string-pool interning. +;; Models: char *p = "hello"; +;; char *q = "hello"; +;; char *r = "world"; +;; return (p == q) + (p != r); /* expected: 2 */ +;; Exercises cg-push-string's idempotent intern: identical literals +;; share an address, distinct literals don't. -(let ((cg (cg-init))) +(let* ((cg (cg-init)) + (cp-ty (%ctype 'ptr 8 8 %t-i8))) (cg-fn-begin cg "main" '() %t-i32) - (cg-push-string cg "hello") - (cg-pop cg) ; discard - (cg-push-string cg "hello") ; should intern to same label - (cg-pop cg) - (cg-push-string cg "world") ; new label - (cg-pop cg) - (cg-push-imm cg %t-i32 0) + (let* ((p-off (cg-alloc-slot cg 8 8)) + (q-off (cg-alloc-slot cg 8 8)) + (r-off (cg-alloc-slot cg 8 8)) + (p (%sym "p" 'var 'auto cp-ty p-off)) + (q (%sym "q" 'var 'auto cp-ty q-off)) + (r (%sym "r" 'var 'auto cp-ty r-off))) + ;; p = "hello" + (cg-push-sym cg p) (cg-push-string cg "hello") + (cg-assign cg) (cg-pop cg) + ;; q = "hello" + (cg-push-sym cg q) (cg-push-string cg "hello") + (cg-assign cg) (cg-pop cg) + ;; r = "world" + (cg-push-sym cg r) (cg-push-string cg "world") + (cg-assign cg) (cg-pop cg) + ;; (p == q) + (p != r) + (cg-push-sym cg p) (cg-load cg) + (cg-push-sym cg q) (cg-load cg) + (cg-binop cg 'eq) + (cg-push-sym cg p) (cg-load cg) + (cg-push-sym cg r) (cg-load cg) + (cg-binop cg 'ne) + (cg-binop cg 'add)) (cg-return cg) (cg-fn-end cg) (write-bv-fd 1 (cg-finish cg))) diff --git a/tests/cc-cg/11-global-var.expected b/tests/cc-cg/11-global-var.expected @@ -1,24 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 16, { -%la(t0, &cc__g) -%ld(t0, t0, 0) -%st(t0, sp, (+ %main__SO 8)) -%ld(a0, sp, (+ %main__SO 8)) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) - -:cc__g -!(0) -!(0) -!(0) -!(0) -:ELF_end diff --git a/tests/cc-cg/11-global-var.expected-exit b/tests/cc-cg/11-global-var.expected-exit @@ -0,0 +1 @@ +0 diff --git a/tests/cc-cg/12-entry-stub.expected b/tests/cc-cg/12-entry-stub.expected @@ -1,5 +0,0 @@ -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-cg/12-entry-stub.scm b/tests/cc-cg/12-entry-stub.scm @@ -1,5 +0,0 @@ -;; tests/cc-cg/12-entry-stub.scm — bare cg-finish (no functions emitted) -;; should still produce just the entry stub block. - -(let ((cg (cg-init))) - (write-bv-fd 1 (cg-finish cg))) diff --git a/tests/cc-cg/13-call-5args.expected b/tests/cc-cg/13-call-5args.expected @@ -1,23 +0,0 @@ -%macro main__SO() -8 -%endm -%fn(cc__main, 32, { -%li(a0, 1) -%li(a1, 2) -%li(a2, 3) -%li(a3, 4) -%li(t0, 5) -%st(t0, sp, 0) -%call(&cc__foo) -%st(a0, sp, (+ %main__SO 8)) -%ld(a0, sp, (+ %main__SO 8)) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-cg/13-call-5args.expected-exit b/tests/cc-cg/13-call-5args.expected-exit @@ -0,0 +1 @@ +15 diff --git a/tests/cc-cg/13-call-5args.scm b/tests/cc-cg/13-call-5args.scm @@ -1,13 +1,35 @@ -;; tests/cc-cg/13-call-5args.scm — 5-arg call exercises outgoing-arg -;; staging at [sp + 0*8] and frame size accounting for max-outgoing. +;; tests/cc-cg/13-call-5args.scm — 5-arg call exercises both sides of +;; the >4-arg ABI: the prologue's LDARG path in cg-fn-begin (callee +;; reads param 4 from the staging area) and the staging emission in +;; cg-call (caller writes arg 4 to [sp + 0*8]). +;; Models: +;; int sum5(int a, int b, int c, int d, int e) { return a+b+c+d+e; } +;; int main(void) { return sum5(1, 2, 3, 4, 5); } /* expected: 15 */ -(let* ((cg (cg-init)) - ;; fn type: 5-arg returning int. - (fn-ty (%ctype 'fn 8 8 - (cons %t-i32 (cons (list %t-i32 %t-i32 %t-i32 %t-i32 %t-i32) #f)))) - (foo (%sym "foo" 'fn 'extern fn-ty #f))) +(let* ((cg (cg-init)) + (sum5-fnty (%ctype 'fn 8 8 + (cons %t-i32 + (cons (list %t-i32 %t-i32 %t-i32 %t-i32 %t-i32) #f)))) + (sum5-sym (%sym "sum5" 'fn 'extern sum5-fnty #f))) + (let* ((params (cg-fn-begin cg "sum5" + (list (cons "a" %t-i32) (cons "b" %t-i32) + (cons "c" %t-i32) (cons "d" %t-i32) + (cons "e" %t-i32)) + %t-i32)) + (a* (cdr (car params))) + (b* (cdr (car (cdr params)))) + (c* (cdr (car (cdr (cdr params))))) + (d* (cdr (car (cdr (cdr (cdr params)))))) + (e* (cdr (car (cdr (cdr (cdr (cdr params)))))))) + (cg-push-sym cg a*) (cg-load cg) + (cg-push-sym cg b*) (cg-load cg) (cg-binop cg 'add) + (cg-push-sym cg c*) (cg-load cg) (cg-binop cg 'add) + (cg-push-sym cg d*) (cg-load cg) (cg-binop cg 'add) + (cg-push-sym cg e*) (cg-load cg) (cg-binop cg 'add) + (cg-return cg) + (cg-fn-end cg)) (cg-fn-begin cg "main" '() %t-i32) - (cg-push-sym cg foo) + (cg-push-sym cg sum5-sym) (cg-push-imm cg %t-i32 1) (cg-push-imm cg %t-i32 2) (cg-push-imm cg %t-i32 3) diff --git a/tests/cc-cg/14-take-addr.expected b/tests/cc-cg/14-take-addr.expected @@ -1,24 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 48, { -%st(a0, sp, (+ %main__SO 8)) -%mov(t0, sp) -%addi(t0, t0, (+ %main__SO 8)) -%st(t0, sp, (+ %main__SO 16)) -%ld(t0, sp, (+ %main__SO 16)) -%st(t0, sp, (+ %main__SO 24)) -%ld(t0, sp, (+ %main__SO 24)) -%ld(t0, t0, 0) -%st(t0, sp, (+ %main__SO 32)) -%ld(a0, sp, (+ %main__SO 32)) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-cg/14-take-addr.expected-exit b/tests/cc-cg/14-take-addr.expected-exit @@ -0,0 +1 @@ +1 diff --git a/tests/cc-parse/00-empty-main.expected-exit b/tests/cc-parse/00-empty-main.expected-exit @@ -0,0 +1 @@ +0 diff --git a/tests/cc-parse/00-empty-main.expected-p1pp b/tests/cc-parse/00-empty-main.expected-p1pp @@ -1,15 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 16, { -%li(a0, 0) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-parse/01-return-argc.expected-exit b/tests/cc-parse/01-return-argc.expected-exit @@ -0,0 +1 @@ +1 diff --git a/tests/cc-parse/01-return-argc.expected-p1pp b/tests/cc-parse/01-return-argc.expected-p1pp @@ -1,19 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 32, { -%st(a0, sp, (+ %main__SO 8)) -%st(a1, sp, (+ %main__SO 16)) -%ld(t0, sp, (+ %main__SO 8)) -%st(t0, sp, (+ %main__SO 24)) -%ld(a0, sp, (+ %main__SO 24)) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-parse/02-add-const.expected-exit b/tests/cc-parse/02-add-const.expected-exit @@ -0,0 +1 @@ +3 diff --git a/tests/cc-parse/02-add-const.expected-p1pp b/tests/cc-parse/02-add-const.expected-p1pp @@ -1,19 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 16, { -%li(a0, 1) -%li(a1, 2) -%add(t0, a0, a1) -%st(t0, sp, (+ %main__SO 8)) -%ld(a0, sp, (+ %main__SO 8)) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-parse/03-local-assign.expected-exit b/tests/cc-parse/03-local-assign.expected-exit @@ -0,0 +1 @@ +5 diff --git a/tests/cc-parse/03-local-assign.expected-p1pp b/tests/cc-parse/03-local-assign.expected-p1pp @@ -1,20 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 32, { -%li(a0, 5) -%st(a0, sp, (+ %main__SO 8)) -%st(a0, sp, (+ %main__SO 16)) -%ld(t0, sp, (+ %main__SO 8)) -%st(t0, sp, (+ %main__SO 24)) -%ld(a0, sp, (+ %main__SO 24)) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-parse/04-if-else.c b/tests/cc-parse/04-if-else.c @@ -1 +1 @@ -int f(int x) { if (x) return 1; else return 0; } +int main(void) { if (1) return 1; else return 0; } diff --git a/tests/cc-parse/04-if-else.expected-exit b/tests/cc-parse/04-if-else.expected-exit @@ -0,0 +1 @@ +1 diff --git a/tests/cc-parse/04-if-else.expected-p1pp b/tests/cc-parse/04-if-else.expected-p1pp @@ -1,25 +0,0 @@ -%macro f__SO() -0 -%endm -%fn(cc__f, 32, { -%st(a0, sp, (+ %f__SO 8)) -%ld(t0, sp, (+ %f__SO 8)) -%st(t0, sp, (+ %f__SO 16)) -%ld(t0, sp, (+ %f__SO 16)) -%ifelse_nez(t0, { -%li(a0, 1) -%st(a0, sp, (+ %f__SO 0)) -%b(&::ret) -}, { -%li(a0, 0) -%st(a0, sp, (+ %f__SO 0)) -%b(&::ret) -}) -::ret -%ld(a0, sp, (+ %f__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-parse/05-while-break.c b/tests/cc-parse/05-while-break.c @@ -1 +1 @@ -int f(int x) { while (x) { break; } return x; } +int main(void) { while (1) break; return 5; } diff --git a/tests/cc-parse/05-while-break.expected-exit b/tests/cc-parse/05-while-break.expected-exit @@ -0,0 +1 @@ +5 diff --git a/tests/cc-parse/05-while-break.expected-p1pp b/tests/cc-parse/05-while-break.expected-p1pp @@ -1,25 +0,0 @@ -%macro f__SO() -0 -%endm -%fn(cc__f, 32, { -%st(a0, sp, (+ %f__SO 8)) -%loop_tag(L0, { -%ld(t0, sp, (+ %f__SO 8)) -%st(t0, sp, (+ %f__SO 16)) -%ld(t0, sp, (+ %f__SO 16)) -%if_eqz(t0, { %break(L0) }) -%break(L0) -}) -%ld(t0, sp, (+ %f__SO 8)) -%st(t0, sp, (+ %f__SO 24)) -%ld(a0, sp, (+ %f__SO 24)) -%st(a0, sp, (+ %f__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %f__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-parse/06-call-no-args.c b/tests/cc-parse/06-call-no-args.c @@ -1,2 +1,2 @@ -int g(void); +int g(void) { return 7; } int main(void) { return g(); } diff --git a/tests/cc-parse/06-call-no-args.expected-exit b/tests/cc-parse/06-call-no-args.expected-exit @@ -0,0 +1 @@ +7 diff --git a/tests/cc-parse/06-call-no-args.expected-p1pp b/tests/cc-parse/06-call-no-args.expected-p1pp @@ -1,17 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 16, { -%call(&cc__g) -%st(a0, sp, (+ %main__SO 8)) -%ld(a0, sp, (+ %main__SO 8)) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-parse/07-call-with-args.c b/tests/cc-parse/07-call-with-args.c @@ -1,2 +1,2 @@ -int g(int, int); -int main(void) { return g(1, 2); } +int g(int a, int b) { return a + b; } +int main(void) { return g(2, 3); } diff --git a/tests/cc-parse/07-call-with-args.expected-exit b/tests/cc-parse/07-call-with-args.expected-exit @@ -0,0 +1 @@ +5 diff --git a/tests/cc-parse/07-call-with-args.expected-p1pp b/tests/cc-parse/07-call-with-args.expected-p1pp @@ -1,19 +0,0 @@ -%macro main__SO() -0 -%endm -%fn(cc__main, 16, { -%li(a0, 1) -%li(a1, 2) -%call(&cc__g) -%st(a0, sp, (+ %main__SO 8)) -%ld(a0, sp, (+ %main__SO 8)) -%st(a0, sp, (+ %main__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %main__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-parse/08-pointer-deref.c b/tests/cc-parse/08-pointer-deref.c @@ -1 +1,2 @@ int f(int *p) { return *p; } +int main(void) { int x = 7; return f(&x); } diff --git a/tests/cc-parse/08-pointer-deref.expected-exit b/tests/cc-parse/08-pointer-deref.expected-exit @@ -0,0 +1 @@ +7 diff --git a/tests/cc-parse/08-pointer-deref.expected-p1pp b/tests/cc-parse/08-pointer-deref.expected-p1pp @@ -1,23 +0,0 @@ -%macro f__SO() -0 -%endm -%fn(cc__f, 48, { -%st(a0, sp, (+ %f__SO 8)) -%ld(t0, sp, (+ %f__SO 8)) -%st(t0, sp, (+ %f__SO 16)) -%ld(t0, sp, (+ %f__SO 16)) -%st(t0, sp, (+ %f__SO 24)) -%ld(t0, sp, (+ %f__SO 24)) -%ld(t0, t0, 0) -%st(t0, sp, (+ %f__SO 32)) -%ld(a0, sp, (+ %f__SO 32)) -%st(a0, sp, (+ %f__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %f__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-parse/09-address-of.c b/tests/cc-parse/09-address-of.c @@ -1 +1 @@ -int *f(int x) { return &x; } +int main(void) { int x = 11; int *p = &x; return *p; } diff --git a/tests/cc-parse/09-address-of.expected-exit b/tests/cc-parse/09-address-of.expected-exit @@ -0,0 +1 @@ +11 diff --git a/tests/cc-parse/09-address-of.expected-p1pp b/tests/cc-parse/09-address-of.expected-p1pp @@ -1,19 +0,0 @@ -%macro f__SO() -0 -%endm -%fn(cc__f, 32, { -%st(a0, sp, (+ %f__SO 8)) -%mov(t0, sp) -%addi(t0, t0, (+ %f__SO 8)) -%st(t0, sp, (+ %f__SO 16)) -%ld(a0, sp, (+ %f__SO 16)) -%st(a0, sp, (+ %f__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %f__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-parse/10-typedef.c b/tests/cc-parse/10-typedef.c @@ -1,2 +1,3 @@ typedef int myint; myint f(myint x) { return x; } +int main(void) { return f(13); } diff --git a/tests/cc-parse/10-typedef.expected-exit b/tests/cc-parse/10-typedef.expected-exit @@ -0,0 +1 @@ +13 diff --git a/tests/cc-parse/10-typedef.expected-p1pp b/tests/cc-parse/10-typedef.expected-p1pp @@ -1,18 +0,0 @@ -%macro f__SO() -0 -%endm -%fn(cc__f, 32, { -%st(a0, sp, (+ %f__SO 8)) -%ld(t0, sp, (+ %f__SO 8)) -%st(t0, sp, (+ %f__SO 16)) -%ld(a0, sp, (+ %f__SO 16)) -%st(a0, sp, (+ %f__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %f__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-parse/11-two-params.c b/tests/cc-parse/11-two-params.c @@ -1 +1,2 @@ int add(int a, int b) { return a + b; } +int main(void) { return add(2, 5); } diff --git a/tests/cc-parse/11-two-params.expected-exit b/tests/cc-parse/11-two-params.expected-exit @@ -0,0 +1 @@ +7 diff --git a/tests/cc-parse/11-two-params.expected-p1pp b/tests/cc-parse/11-two-params.expected-p1pp @@ -1,25 +0,0 @@ -%macro add__SO() -0 -%endm -%fn(cc__add, 48, { -%st(a0, sp, (+ %add__SO 8)) -%st(a1, sp, (+ %add__SO 16)) -%ld(t0, sp, (+ %add__SO 8)) -%st(t0, sp, (+ %add__SO 24)) -%ld(t0, sp, (+ %add__SO 16)) -%st(t0, sp, (+ %add__SO 32)) -%ld(a0, sp, (+ %add__SO 24)) -%ld(a1, sp, (+ %add__SO 32)) -%add(t0, a0, a1) -%st(t0, sp, (+ %add__SO 40)) -%ld(a0, sp, (+ %add__SO 40)) -%st(a0, sp, (+ %add__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %add__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-parse/12-comparison.c b/tests/cc-parse/12-comparison.c @@ -1 +1 @@ -int f(int a, int b) { return a < b; } +int main(void) { return 3 < 5; } diff --git a/tests/cc-parse/12-comparison.expected-exit b/tests/cc-parse/12-comparison.expected-exit @@ -0,0 +1 @@ +1 diff --git a/tests/cc-parse/12-comparison.expected-p1pp b/tests/cc-parse/12-comparison.expected-p1pp @@ -1,25 +0,0 @@ -%macro f__SO() -0 -%endm -%fn(cc__f, 48, { -%st(a0, sp, (+ %f__SO 8)) -%st(a1, sp, (+ %f__SO 16)) -%ld(t0, sp, (+ %f__SO 8)) -%st(t0, sp, (+ %f__SO 24)) -%ld(t0, sp, (+ %f__SO 16)) -%st(t0, sp, (+ %f__SO 32)) -%ld(a0, sp, (+ %f__SO 24)) -%ld(a1, sp, (+ %f__SO 32)) -%ifelse_lt(a0, a1, { %li(t0, 1) }, { %li(t0, 0) }) -%st(t0, sp, (+ %f__SO 40)) -%ld(a0, sp, (+ %f__SO 40)) -%st(a0, sp, (+ %f__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %f__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-parse/13-while-continue.c b/tests/cc-parse/13-while-continue.c @@ -1 +1,5 @@ -int f(int x) { while (x) { continue; } return 0; } +int count(int n, int target) { + while (n < target) { n = n + 1; continue; } + return n; +} +int main(void) { return count(0, 3); } diff --git a/tests/cc-parse/13-while-continue.expected-exit b/tests/cc-parse/13-while-continue.expected-exit @@ -0,0 +1 @@ +3 diff --git a/tests/cc-parse/13-while-continue.expected-p1pp b/tests/cc-parse/13-while-continue.expected-p1pp @@ -1,23 +0,0 @@ -%macro f__SO() -0 -%endm -%fn(cc__f, 32, { -%st(a0, sp, (+ %f__SO 8)) -%loop_tag(L0, { -%ld(t0, sp, (+ %f__SO 8)) -%st(t0, sp, (+ %f__SO 16)) -%ld(t0, sp, (+ %f__SO 16)) -%if_eqz(t0, { %break(L0) }) -%continue(L0) -}) -%li(a0, 0) -%st(a0, sp, (+ %f__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %f__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end diff --git a/tests/cc-parse/14-mul-paren.c b/tests/cc-parse/14-mul-paren.c @@ -1 +1 @@ -int f(int a, int b, int c) { return a * (b + c); } +int main(void) { return 2 * (3 + 4); } diff --git a/tests/cc-parse/14-mul-paren.expected-exit b/tests/cc-parse/14-mul-paren.expected-exit @@ -0,0 +1 @@ +14 diff --git a/tests/cc-parse/14-mul-paren.expected-p1pp b/tests/cc-parse/14-mul-paren.expected-p1pp @@ -1,32 +0,0 @@ -%macro f__SO() -0 -%endm -%fn(cc__f, 80, { -%st(a0, sp, (+ %f__SO 8)) -%st(a1, sp, (+ %f__SO 16)) -%st(a2, sp, (+ %f__SO 24)) -%ld(t0, sp, (+ %f__SO 8)) -%st(t0, sp, (+ %f__SO 32)) -%ld(t0, sp, (+ %f__SO 16)) -%st(t0, sp, (+ %f__SO 40)) -%ld(t0, sp, (+ %f__SO 24)) -%st(t0, sp, (+ %f__SO 48)) -%ld(a0, sp, (+ %f__SO 40)) -%ld(a1, sp, (+ %f__SO 48)) -%add(t0, a0, a1) -%st(t0, sp, (+ %f__SO 56)) -%ld(a0, sp, (+ %f__SO 32)) -%ld(a1, sp, (+ %f__SO 56)) -%mul(t0, a0, a1) -%st(t0, sp, (+ %f__SO 64)) -%ld(a0, sp, (+ %f__SO 64)) -%st(a0, sp, (+ %f__SO 0)) -%b(&::ret) -::ret -%ld(a0, sp, (+ %f__SO 0)) -}) -# entry stub -%fn(p1_main, 16, { -%call(&cc__main) -}) -:ELF_end