boot2

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

commit f725b1162f0038013abb884ca73dd3b10a7ddb89
parent bab1f12ae5c8898d5b816a20dae3f067479e884c
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sun, 26 Apr 2026 21:50:39 -0700

cc/cg: signed narrowing sign-extends on re-widen (§A.4)

cg-cast's narrowing branch used to mask only, so (int)(char)-3 came
back as 253 instead of -3. Now it shli/sari's for signed narrow
targets (i8/i16/i32) — truncate-and-sign-extend in one step — so the
slot holds the canonical 64-bit form and the subsequent widening
relabel preserves the value. Unsigned targets still mask.

cc-cg/18-sext-narrow and cc-parse/18-sext-narrow lock the behavior in.

Diffstat:
Mcc/cg.scm | 8++++++++
Mdocs/CC-PUNCHLIST.md | 12+++++++-----
Atests/cc-cg/18-sext-narrow.expected-exit | 1+
Atests/cc-cg/18-sext-narrow.scm | 20++++++++++++++++++++
Atests/cc-parse/18-sext-narrow.c | 6++++++
Atests/cc-parse/18-sext-narrow.expected-exit | 1+
6 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/cc/cg.scm b/cc/cg.scm @@ -462,8 +462,16 @@ ((>= to-sz from-sz) (cg-push cg (%opnd (opnd-kind p) to-type (opnd-ext p) (opnd-lval? p)))) (else + ;; Narrowing cast. Signed targets (i8/i16/i32) shli/sari to + ;; truncate-and-sign-extend in one step, so the slot holds the + ;; canonical 64-bit form and a subsequent widening cast (which + ;; is relabel-only) restores the value. Unsigned targets mask + ;; off high bits to zero-extend. (%cg-load-opnd-into cg p 't0) (cond + ((eq? to-kind 'i8) (%cg-emit-sext cg 't0 56)) + ((eq? to-kind 'i16) (%cg-emit-sext cg 't0 48)) + ((eq? to-kind 'i32) (%cg-emit-sext cg 't0 32)) ((= to-sz 1) (%cg-emit-many cg (list "%andi(t0, t0, 255)\n"))) ((= to-sz 2) (%cg-emit-many cg (list "%li(t1, 65535)\n%and(t0, t0, t1)\n"))) diff --git a/docs/CC-PUNCHLIST.md b/docs/CC-PUNCHLIST.md @@ -66,11 +66,13 @@ upstream of nearly everything else. Land this first. indirect path now uses `t2` so multi-byte gathers don't alias dest with base. -- [ ] **Signed narrowing keeps sign on re-widen** - - cg: `cc-cg/NN-sext-narrow.scm` — `(unsigned)(int)(char)-3` → exit 253. - - parse: `cc-parse/NN-sext-narrow.c` - - Needs: `cg-cast` emits sign-extend on the narrow path (or signed - `%lds*` loads); `cg-promote` emits sext when source rank < int. +- [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` + - 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 + (relabel-only) preserves it. - [ ] **Unsigned narrowing zero-extends** - cg: `cc-cg/NN-zext-narrow.scm` — `(unsigned)(unsigned char)-3` → 253. diff --git a/tests/cc-cg/18-sext-narrow.expected-exit b/tests/cc-cg/18-sext-narrow.expected-exit @@ -0,0 +1 @@ +1 diff --git a/tests/cc-cg/18-sext-narrow.scm b/tests/cc-cg/18-sext-narrow.scm @@ -0,0 +1,20 @@ +;; tests/cc-cg/18-sext-narrow.scm — signed narrowing keeps sign on +;; re-widen (§A.4 of docs/CC-PUNCHLIST.md). +;; +;; Models: ((int)(char)-3) == -3. +;; Forces the cg-cast narrowing path to sign-encode the result so +;; the subsequent widening cast restores -3, not 0xFD (253). The +;; comparison is against -3 as i32, so a buggy cg that masks-only +;; yields a0=253 vs a1=-3 → not equal → exit 0. Correct cg sign- +;; extends → equal → exit 1. + +(let ((cg (cg-init))) + (cg-fn-begin cg "main" '() %t-i32) + (cg-push-imm cg %t-i32 -3) + (cg-cast cg %t-i8) + (cg-cast cg %t-i32) + (cg-push-imm cg %t-i32 -3) + (cg-binop cg 'eq) + (cg-return cg) + (cg-fn-end cg) + (write-bv-fd 1 (cg-finish cg))) diff --git a/tests/cc-parse/18-sext-narrow.c b/tests/cc-parse/18-sext-narrow.c @@ -0,0 +1,6 @@ +// tests/cc-parse/18-sext-narrow.c — signed narrowing keeps sign on +// re-widen. §A.4 of docs/CC-PUNCHLIST.md. + +int main() { + return ((int)(char)-3) == -3; +} diff --git a/tests/cc-parse/18-sext-narrow.expected-exit b/tests/cc-parse/18-sext-narrow.expected-exit @@ -0,0 +1 @@ +1