boot2

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

commit d4a149dfb83a90d38811ac0c1cc8345b82bd9b5a
parent 3b8120ff0d624c22423a5cff80dadaf60abb599c
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Fri,  1 May 2026 17:45:38 -0700

cc/cg: promote narrow integers to (signed) int

C 6.3.1.1 says _Bool, char, short, and any narrower integer type
whose values all fit in int promote to int (signed). cg-promote
previously kept the source signedness — narrow unsigned types went
to u32 — which dragged the subsequent cg-arith-conv into picking
the unsigned common type and flipped the signedness of `>>`,
ordered comparisons, division, etc. away from the C rule. Always
promote to i32; any narrow value's canonical 64-bit slot form
already matches i32 so the cast stays relabel-only.

New cc-cg fixture 84-bool-promotes-int locks in (_Bool=1) * -1
== -1 — previously 0 because the bool→u32 promotion forced an
unsigned multiply.

Diffstat:
Mcc/cc.scm | 14+++++++++-----
Atests/cc-cg/84-bool-promotes-int.expected-exit | 1+
Atests/cc-cg/84-bool-promotes-int.scm | 28++++++++++++++++++++++++++++
3 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/cc/cc.scm b/cc/cc.scm @@ -3478,12 +3478,16 @@ (ty (opnd-type p)) (sz (%ctype-size ty))) (cond + ;; C 6.3.1.1: _Bool, char, short, and any narrower int type + ;; promote to (signed) int — every representable value fits + ;; in i32. Treating narrow unsigned types as u32 here would + ;; drag the subsequent arith-conv into picking the unsigned + ;; common type, flipping signedness of `>>`, comparisons, + ;; division, etc. against the C rule. Canonical form for any + ;; in-range narrow value already matches i32, so the cast is + ;; relabel-only. ((< sz 4) - (cond - ((%ctype-unsigned? ty) - (cg-push cg (%opnd (opnd-kind p) %t-u32 (opnd-ext p) (opnd-lval? p)))) - (else - (cg-push cg (%opnd (opnd-kind p) %t-i32 (opnd-ext p) (opnd-lval? p)))))) + (cg-push cg (%opnd (opnd-kind p) %t-i32 (opnd-ext p) (opnd-lval? p)))) (else (cg-push cg p))))) (define (cg-arith-conv cg) diff --git a/tests/cc-cg/84-bool-promotes-int.expected-exit b/tests/cc-cg/84-bool-promotes-int.expected-exit @@ -0,0 +1 @@ +1 diff --git a/tests/cc-cg/84-bool-promotes-int.scm b/tests/cc-cg/84-bool-promotes-int.scm @@ -0,0 +1,28 @@ +;; tests/cc-cg/84-bool-promotes-int.scm — _Bool promotes to int per +;; C 6.3.1.1 (integer promotions: _Bool, char, short → int, not unsigned). +;; +;; Models: +;; _Bool b = 1; +;; return (b * -1) == -1; +;; +;; cg-promote currently treats bool as unsigned and promotes to u32, +;; so the subsequent arith-conv with i32 picks u32 as the common +;; type. The product 1u * (uint32_t)-1 = 0xFFFFFFFF (canonical +;; 0x00000000FFFFFFFF after spill), and the equality test against +;; (int)-1 (canonical 0xFFFFFFFFFFFFFFFF) fails. Correct cg promotes +;; bool to i32 → arith-conv keeps i32 → product is canonical i32 -1 +;; → equality holds. + +(let ((cg (cg-init))) + (cg-fn-begin cg "main" '() %t-i32) + (cg-push-imm cg %t-bool 1) + (cg-promote cg) + (cg-push-imm cg %t-i32 -1) + (cg-promote cg) + (cg-arith-conv cg) + (cg-binop cg 'mul) + (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)))