boot2

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

commit c0af21b7d2e5511016c8989f404945b0d697b8ba
parent edbea61f7462e1584d862564470647372b578594
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sun, 26 Apr 2026 21:55:29 -0700

cc tests: lock in integer promotion preserves sign (§A.6)

cg-promote's relabel-only path is correct because §A.1's load-side
sign-extension already canonicalises the spill slot before promote
ever sees the opnd. cc-cg/20-promote-sign + cc-parse/20-promote-sign
verify that signed char -1 plus 2 equals 1 in 64-bit form (the
%ifelse_eq compare distinguishes 0x1 from 0x101).

Diffstat:
Mdocs/CC-PUNCHLIST.md | 13+++++++------
Atests/cc-cg/20-promote-sign.expected-exit | 1+
Atests/cc-cg/20-promote-sign.scm | 32++++++++++++++++++++++++++++++++
Atests/cc-parse/20-promote-sign.c | 7+++++++
Atests/cc-parse/20-promote-sign.expected-exit | 1+
5 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/docs/CC-PUNCHLIST.md b/docs/CC-PUNCHLIST.md @@ -81,12 +81,13 @@ upstream of nearly everything else. Land this first. correct; fixture locks the contrast with §A.4 in (same source, same chain shape, divergent result via target signedness). -- [ ] **Integer promotion preserves sign across operations** - - cg: `cc-cg/NN-promote-sign.scm` — operate on a `signed char` slot - holding `-1`; promote, add 1, return 0. - - parse: `cc-parse/NN-promote-sign.c` - - Needs: `cg-promote` is currently relabel-only; emit sext for - `i8`/`i16` sources. +- [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` + - 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. ### B. Lvalue mechanics diff --git a/tests/cc-cg/20-promote-sign.expected-exit b/tests/cc-cg/20-promote-sign.expected-exit @@ -0,0 +1 @@ +1 diff --git a/tests/cc-cg/20-promote-sign.scm b/tests/cc-cg/20-promote-sign.scm @@ -0,0 +1,32 @@ +;; tests/cc-cg/20-promote-sign.scm — integer promotion preserves sign +;; (§A.6 of docs/CC-PUNCHLIST.md). +;; +;; Models: signed char x = -1; return ((int)x + 2) == 1; +;; If promotion doesn't sign-extend, the i8 -1 reads as 255 and +;; 255+2 == 257 (mod 256 == 1) — exit code can't distinguish 1 vs +;; 1 directly. So we instead compare the post-promote post-add +;; value against 1 as a 64-bit comparison: properly-promoted +;; -1 + 2 == 0x0000000000000001; without sext, 0xFF + 2 == 0x101. +;; The %ifelse_eq compares full registers, so the 0x101 case fails +;; → exit 0; the canonical 1 case → exit 1. + +(let ((cg (cg-init))) + (cg-fn-begin cg "main" '() %t-i32) + (let* ((off-x (cg-alloc-slot cg 1 1)) + (sym-x (%sym "x" 'var 'auto %t-i8 off-x))) + ;; x = -1 + (cg-push-sym cg sym-x) + (cg-push-imm cg %t-i8 -1) + (cg-assign cg) (cg-pop cg) + ;; (int)x + 2 == 1 + (cg-push-sym cg sym-x) + (cg-load cg) + (cg-promote cg) + (cg-push-imm cg %t-i32 2) + (cg-arith-conv cg) + (cg-binop cg 'add) + (cg-push-imm cg %t-i32 1) + (cg-binop cg 'eq) + (cg-return cg)) + (cg-fn-end cg) + (write-bv-fd 1 (cg-finish cg))) diff --git a/tests/cc-parse/20-promote-sign.c b/tests/cc-parse/20-promote-sign.c @@ -0,0 +1,7 @@ +// tests/cc-parse/20-promote-sign.c — integer promotion preserves sign. +// §A.6 of docs/CC-PUNCHLIST.md. + +int main() { + signed char x = -1; + return (((int)x) + 2) == 1; +} diff --git a/tests/cc-parse/20-promote-sign.expected-exit b/tests/cc-parse/20-promote-sign.expected-exit @@ -0,0 +1 @@ +1