cc.scm (363129B)
1 ;; cc/util.scm — leaf helpers. Depends only on the scheme1 prelude. 2 3 ;; -------------------------------------------------------------------- 4 ;; byte-oriented helpers. R7RS strings and bytevectors are disjoint; 5 ;; micro+boot2's bytes? / bytes=? and bytevector operations provide the 6 ;; explicit bridge used by this byte-oriented compiler. 7 ;; -------------------------------------------------------------------- 8 (define (bv= a b) (bytes=? a b)) 9 10 (define (bv-prefix? p s) 11 ;; Is s a bv that starts with the bytes of p? 12 (let ((plen (bytevector-length p)) 13 (slen (bytevector-length s))) 14 (if (< slen plen) 15 #f 16 (let loop ((i 0)) 17 (cond ((= i plen) #t) 18 ((= (bytevector-u8-ref p i) (bytevector-u8-ref s i)) 19 (loop (+ i 1))) 20 (else #f)))))) 21 22 (define (bv-find bv byte from) 23 ;; Linear scan for the first byte == `byte` at index >= from. 24 ;; Returns the index, or #f if not found. 25 (let ((n (bytevector-length bv))) 26 (let loop ((i from)) 27 (cond ((>= i n) #f) 28 ((= (bytevector-u8-ref bv i) byte) i) 29 (else (loop (+ i 1))))))) 30 31 (define (bv-slice bv start end) 32 ;; Fresh copy of bytes in [start, end). bytevector-copy already does 33 ;; this in scheme1 (3-arg form returns a fresh bv). 34 (bytevector-copy bv start end)) 35 36 (define (bv-of-byte b) (make-bytevector 1 b)) 37 38 (define (bv-cat lst-of-bv) 39 ;; Concat a list of bytevectors with one allocation. bytevector-append 40 ;; is variadic, so apply does this in a single linear pass. 41 (apply bytevector-append lst-of-bv)) 42 43 (define (bv->fixnum bv radix) 44 ;; (values ok? val) — #t/n on parse, #f/#f on fail. 45 ;; string->number is pure and returns #f on parse failure. 46 (let ((n (string->number bv radix))) 47 (if n (values #t n) (values #f #f)))) 48 49 (define (fixnum->bv n radix) 50 (bytevector-append (number->string n radix))) 51 52 ;; -------------------------------------------------------------------- 53 ;; lists / alists 54 ;; -------------------------------------------------------------------- 55 (define (key=? a b) 56 (if (and (bytes? a) (bytes? b)) (bytes=? a b) (equal? a b))) 57 58 (define (assoc/key key al) 59 (cond ((null? al) #f) 60 ((key=? key (car (car al))) (car al)) 61 (else (assoc/key key (cdr al))))) 62 63 (define (alist-ref key al) (let ((p (assoc/key key al))) (and p (cdr p)))) 64 (define (alist-ref/eq key al) (let ((p (assq key al))) (and p (cdr p)))) 65 66 (define (alist-set key val al) (cons (cons key val) al)) 67 68 ;; Compiler-wide maps use scheme1's private GC-aware open-addressed table. 69 ;; Bytevector/string keys compare by bytes, matching key=? above; symbol and 70 ;; integer keys compare by identity. Small per-call environments remain alists 71 ;; because constructing a table for a handful of macro parameters would cost 72 ;; more than scanning them. 73 (define (alist->hash al) 74 (let ((h (%make-hash-table 16))) 75 (let loop ((xs al)) 76 (cond ((null? xs) h) 77 (else 78 (%hash-set! h (car (car xs)) (cdr (car xs))) 79 (loop (cdr xs))))))) 80 81 (define (alist-update key f al) 82 ;; Functional update by byte-aware key equality. If found, replace its value with 83 ;; (f old-val). If not found, prepend (cons key (f #f)) so callers 84 ;; can use this as upsert-with-default. 85 (let loop ((xs al) (acc '())) 86 (cond ((null? xs) 87 (cons (cons key (f #f)) (reverse acc))) 88 ((key=? (car (car xs)) key) 89 (append (reverse acc) 90 (cons (cons key (f (cdr (car xs)))) 91 (cdr xs)))) 92 (else (loop (cdr xs) (cons (car xs) acc)))))) 93 94 (define (any p xs) 95 (cond ((null? xs) #f) 96 ((p (car xs)) #t) 97 (else (any p (cdr xs))))) 98 99 (define (every p xs) 100 (cond ((null? xs) #t) 101 ((p (car xs)) (every p (cdr xs))) 102 (else #f))) 103 104 (define (count p xs) 105 (let loop ((xs xs) (n 0)) 106 (cond ((null? xs) n) 107 ((p (car xs)) (loop (cdr xs) (+ n 1))) 108 (else (loop (cdr xs) n))))) 109 110 ;; -------------------------------------------------------------------- 111 ;; ints 112 ;; -------------------------------------------------------------------- 113 (define (min3 a b c) (min a (min b c))) 114 (define (align-up n k) 115 ;; round n up to the nearest multiple of k (k must be a power of 2) 116 (let ((mask (- k 1))) 117 (bit-and (+ n mask) (bit-not mask)))) 118 119 ;; -------------------------------------------------------------------- 120 ;; Fixed-width C integer carrier 121 ;; -------------------------------------------------------------------- 122 ;; 123 ;; scheme1 fixnums have three tag bits, so a P1-32 host cannot carry every 124 ;; 32-bit C value directly (and a P1-64 host cannot carry every u64 either). 125 ;; Keep out-of-range C values as an exact, little-endian 64-bit bytevector. 126 ;; Small results normalize back to fixnums so sizes, offsets, and loop counts 127 ;; keep using the cheap representation. All helpers accept either shape. 128 129 (define-record-type c-value 130 (%c-value bytes) 131 c-value? 132 (bytes c-value-bytes)) 133 134 (define-record-type c-int-lit 135 (%c-int-lit value unsigned? long-count decimal?) 136 c-int-lit? 137 (value c-int-lit-value) 138 (unsigned? c-int-lit-unsigned?) 139 (long-count c-int-lit-long-count) 140 (decimal? c-int-lit-decimal?)) 141 142 (define %C-VALUE-BYTES 8) 143 144 (define (%c-value-coerce v) 145 (cond 146 ((c-int-lit? v) (%c-value-coerce (c-int-lit-value v))) 147 ((c-value? v) v) 148 (else 149 (let ((bv (make-bytevector %C-VALUE-BYTES 0))) 150 (let loop ((i 0) (n v)) 151 (cond 152 ((= i %C-VALUE-BYTES) (%c-value bv)) 153 (else 154 (bytevector-u8-set! bv i (bit-and n 255)) 155 (loop (+ i 1) (arithmetic-shift n -8))))))))) 156 157 (define (%c-value-copy v) 158 (let ((bv (c-value-bytes (%c-value-coerce v)))) 159 (%c-value (bytevector-copy bv 0 (bytevector-length bv))))) 160 161 (define (%c-value-zero) (%c-value (make-bytevector %C-VALUE-BYTES 0))) 162 163 (define (%c-value-zero? v) 164 (cond 165 ((not (c-value? v)) (= v 0)) 166 (else 167 (let ((bv (c-value-bytes v))) 168 (let loop ((i 0)) 169 (cond ((= i %C-VALUE-BYTES) #t) 170 ((not (= (bytevector-u8-ref bv i) 0)) #f) 171 (else (loop (+ i 1))))))))) 172 173 (define (%c-value-positive-small cv) 174 ;; Conservative common subset of both Scheme fixnum ranges: [0,2^27). 175 (let ((bv (c-value-bytes cv))) 176 (cond 177 ((or (not (= (bytevector-u8-ref bv 7) 0)) 178 (not (= (bytevector-u8-ref bv 6) 0)) 179 (not (= (bytevector-u8-ref bv 5) 0)) 180 (not (= (bytevector-u8-ref bv 4) 0)) 181 (>= (bytevector-u8-ref bv 3) 8)) #f) 182 (else 183 (let loop ((i 3) (n 0)) 184 (cond ((< i 0) n) 185 (else 186 (loop (- i 1) 187 (+ (* n 256) (bytevector-u8-ref bv i)))))))))) 188 189 (define (%c-value-negate/raw v) 190 (let* ((src (c-value-bytes (%c-value-coerce v))) 191 (out (make-bytevector %C-VALUE-BYTES 0))) 192 (let loop ((i 0) (carry 1)) 193 (cond 194 ((= i %C-VALUE-BYTES) (%c-value out)) 195 (else 196 (let ((z (+ (- 255 (bytevector-u8-ref src i)) carry))) 197 (bytevector-u8-set! out i (bit-and z 255)) 198 (loop (+ i 1) (if (> z 255) 1 0)))))))) 199 200 (define (%c-value-normalize v) 201 (cond 202 ((not (c-value? v)) v) 203 (else 204 (let ((p (%c-value-positive-small v))) 205 (cond 206 (p p) 207 ((>= (bytevector-u8-ref (c-value-bytes v) 7) 128) 208 (let* ((mag (%c-value-negate/raw v)) 209 (m (%c-value-positive-small mag))) 210 (if m (- 0 m) v))) 211 (else v)))))) 212 213 (define (%c-value-normalize-unsigned v) 214 ;; Lexer accumulation starts from an unsigned source spelling. Preserve 215 ;; values with bit 63 set as c-values rather than collapsing (for example) 216 ;; 18446744073709551615 to the signed fixnum -1. 217 (cond 218 ((not (c-value? v)) v) 219 (else 220 (let ((p (%c-value-positive-small v))) 221 (if p p v))))) 222 223 (define (%c-value->fixnum v context) 224 (let ((n (%c-value-normalize v))) 225 (cond ((c-value? n) (die #f context "value does not fit host fixnum")) 226 (else n)))) 227 228 (define (%c-value-mul-small-add v scale addend) 229 ;; Used by the lexer. scale <= 16 and addend <= 15, so every step is 230 ;; far below even RV32's fixnum ceiling. 231 (let* ((src (c-value-bytes (%c-value-coerce v))) 232 (out (make-bytevector %C-VALUE-BYTES 0))) 233 (let loop ((i 0) (carry addend)) 234 (cond 235 ((= i %C-VALUE-BYTES) (%c-value-normalize-unsigned (%c-value out))) 236 (else 237 (let ((z (+ (* (bytevector-u8-ref src i) scale) carry))) 238 (bytevector-u8-set! out i (remainder z 256)) 239 (loop (+ i 1) (quotient z 256)))))))) 240 241 (define (%c-value-add a b) 242 (let* ((av (c-value-bytes (%c-value-coerce a))) 243 (bv (c-value-bytes (%c-value-coerce b))) 244 (out (make-bytevector %C-VALUE-BYTES 0))) 245 (let loop ((i 0) (carry 0)) 246 (cond 247 ((= i %C-VALUE-BYTES) (%c-value-normalize (%c-value out))) 248 (else 249 (let ((z (+ (bytevector-u8-ref av i) 250 (bytevector-u8-ref bv i) carry))) 251 (bytevector-u8-set! out i (remainder z 256)) 252 (loop (+ i 1) (quotient z 256)))))))) 253 254 (define (%c-value-negate v) 255 (%c-value-normalize (%c-value-negate/raw v))) 256 257 (define (%c-value-sub a b) 258 (%c-value-add a (%c-value-negate/raw b))) 259 260 (define (%c-value-mul a b) 261 (let* ((av (c-value-bytes (%c-value-coerce a))) 262 (bv (c-value-bytes (%c-value-coerce b))) 263 (out (make-bytevector %C-VALUE-BYTES 0))) 264 (let outer ((i 0)) 265 (cond 266 ((= i %C-VALUE-BYTES) (%c-value-normalize (%c-value out))) 267 (else 268 (let inner ((j 0) (carry 0)) 269 (cond 270 ((= (+ i j) %C-VALUE-BYTES) (outer (+ i 1))) 271 (else 272 (let* ((k (+ i j)) 273 (z (+ (bytevector-u8-ref out k) 274 (* (bytevector-u8-ref av i) 275 (bytevector-u8-ref bv j)) 276 carry))) 277 (bytevector-u8-set! out k (remainder z 256)) 278 (inner (+ j 1) (quotient z 256))))))))))) 279 280 (define (%c-value-bitop op a b) 281 (let* ((av (c-value-bytes (%c-value-coerce a))) 282 (bv (c-value-bytes (%c-value-coerce b))) 283 (out (make-bytevector %C-VALUE-BYTES 0))) 284 (let loop ((i 0)) 285 (cond 286 ((= i %C-VALUE-BYTES) (%c-value-normalize (%c-value out))) 287 (else 288 (bytevector-u8-set! out i 289 (op (bytevector-u8-ref av i) (bytevector-u8-ref bv i))) 290 (loop (+ i 1))))))) 291 292 (define (%c-value-and a b) (%c-value-bitop bit-and a b)) 293 (define (%c-value-or a b) (%c-value-bitop bit-or a b)) 294 (define (%c-value-xor a b) (%c-value-bitop bit-xor a b)) 295 (define (%c-value-not a) (%c-value-xor a -1)) 296 297 (define (%c-value-ucmp a b) 298 ;; -1 / 0 / 1 under unsigned 64-bit ordering. 299 (let ((av (c-value-bytes (%c-value-coerce a))) 300 (bv (c-value-bytes (%c-value-coerce b)))) 301 (let loop ((i (- %C-VALUE-BYTES 1))) 302 (cond ((< i 0) 0) 303 ((< (bytevector-u8-ref av i) (bytevector-u8-ref bv i)) -1) 304 ((> (bytevector-u8-ref av i) (bytevector-u8-ref bv i)) 1) 305 (else (loop (- i 1))))))) 306 307 (define (%c-value-eq? a b) (= (%c-value-ucmp a b) 0)) 308 309 (define (%c-value-trunc v nbytes signed?) 310 (let* ((src (c-value-bytes (%c-value-coerce v))) 311 (out (bytevector-copy src 0 (bytevector-length src))) 312 (fill (if (and signed? (> nbytes 0) 313 (>= (bytevector-u8-ref src (- nbytes 1)) 128)) 314 255 0))) 315 (let loop ((i nbytes)) 316 (cond ((= i %C-VALUE-BYTES) 317 (%c-value-normalize (%c-value out))) 318 (else 319 (bytevector-u8-set! out i fill) 320 (loop (+ i 1))))))) 321 322 (define (%c-value-signed-negative? v nbytes) 323 (and (> nbytes 0) 324 (>= (bytevector-u8-ref 325 (c-value-bytes (%c-value-coerce v)) (- nbytes 1)) 326 128))) 327 328 (define (%c-value-cmp a b nbytes unsigned?) 329 (let ((aa (%c-value-trunc a nbytes (not unsigned?))) 330 (bb (%c-value-trunc b nbytes (not unsigned?)))) 331 (cond 332 (unsigned? (%c-value-ucmp aa bb)) 333 ((and (%c-value-signed-negative? aa %C-VALUE-BYTES) 334 (not (%c-value-signed-negative? bb %C-VALUE-BYTES))) -1) 335 ((and (not (%c-value-signed-negative? aa %C-VALUE-BYTES)) 336 (%c-value-signed-negative? bb %C-VALUE-BYTES)) 1) 337 (else (%c-value-ucmp aa bb))))) 338 339 (define (%c-value-shift-one-left/raw cv) 340 (let* ((src (c-value-bytes cv)) 341 (out (make-bytevector %C-VALUE-BYTES 0))) 342 (let loop ((i 0) (carry 0)) 343 (cond ((= i %C-VALUE-BYTES) (%c-value out)) 344 (else 345 (let ((z (+ (* (bytevector-u8-ref src i) 2) carry))) 346 (bytevector-u8-set! out i (remainder z 256)) 347 (loop (+ i 1) (quotient z 256)))))))) 348 349 (define (%c-value-shift-one-right/raw cv arithmetic?) 350 (let* ((src (c-value-bytes cv)) 351 (out (make-bytevector %C-VALUE-BYTES 0)) 352 (initial (if (and arithmetic? 353 (>= (bytevector-u8-ref src 7) 128)) 1 0))) 354 (let loop ((i 7) (carry initial)) 355 (cond ((< i 0) (%c-value out)) 356 (else 357 (let ((byte (bytevector-u8-ref src i))) 358 (bytevector-u8-set! out i 359 (+ (quotient byte 2) (* carry 128))) 360 (loop (- i 1) (remainder byte 2)))))))) 361 362 (define (%c-value-shift v count arithmetic-right?) 363 (let ((start (%c-value-coerce v))) 364 (cond 365 ((>= count 64) 366 (if (and arithmetic-right? 367 (%c-value-signed-negative? start %C-VALUE-BYTES)) 368 -1 0)) 369 ((<= count -64) 0) 370 ((>= count 0) 371 (let loop ((n count) (x start)) 372 (cond ((= n 0) (%c-value-normalize x)) 373 (else 374 (loop (- n 1) 375 (%c-value-shift-one-right/raw x arithmetic-right?)))))) 376 (else 377 (let loop ((n (- 0 count)) (x start)) 378 (cond ((= n 0) (%c-value-normalize x)) 379 (else 380 (loop (- n 1) (%c-value-shift-one-left/raw x))))))))) 381 382 (define (%c-value-bv-sub! dst rhs) 383 (let loop ((i 0) (borrow 0)) 384 (cond 385 ((= i %C-VALUE-BYTES) dst) 386 (else 387 (let ((z (- (bytevector-u8-ref dst i) 388 (bytevector-u8-ref rhs i) borrow))) 389 (cond ((< z 0) 390 (bytevector-u8-set! dst i (+ z 256)) 391 (loop (+ i 1) 1)) 392 (else 393 (bytevector-u8-set! dst i z) 394 (loop (+ i 1) 0)))))))) 395 396 (define (%c-value-bv-shl1! bv incoming) 397 (let loop ((i 0) (carry incoming)) 398 (cond ((= i %C-VALUE-BYTES) bv) 399 (else 400 (let ((z (+ (* (bytevector-u8-ref bv i) 2) carry))) 401 (bytevector-u8-set! bv i (remainder z 256)) 402 (loop (+ i 1) (quotient z 256))))))) 403 404 (define (%c-value-bv-ucmp a b) 405 (let loop ((i 7)) 406 (cond ((< i 0) 0) 407 ((< (bytevector-u8-ref a i) (bytevector-u8-ref b i)) -1) 408 ((> (bytevector-u8-ref a i) (bytevector-u8-ref b i)) 1) 409 (else (loop (- i 1)))))) 410 411 (define (%c-value-udivmod a b) 412 ;; Returns (quotient . remainder), both c-values. Binary long division 413 ;; keeps every Scheme temporary byte-sized. 414 (let* ((num (c-value-bytes (%c-value-coerce a))) 415 (den (c-value-bytes (%c-value-coerce b))) 416 (q (make-bytevector %C-VALUE-BYTES 0)) 417 (r (make-bytevector %C-VALUE-BYTES 0))) 418 (cond ((%c-value-zero? (%c-value den)) 419 (die #f "C constant division by zero"))) 420 (let loop ((bit 63)) 421 (cond 422 ((< bit 0) 423 (cons (%c-value-normalize (%c-value q)) 424 (%c-value-normalize (%c-value r)))) 425 (else 426 (let* ((byte-index (quotient bit 8)) 427 (bit-index (remainder bit 8)) 428 (incoming 429 (bit-and (arithmetic-shift 430 (bytevector-u8-ref num byte-index) 431 (- 0 bit-index)) 432 1))) 433 (%c-value-bv-shl1! r incoming) 434 (cond 435 ((>= (%c-value-bv-ucmp r den) 0) 436 (%c-value-bv-sub! r den) 437 (bytevector-u8-set! q byte-index 438 (bit-or (bytevector-u8-ref q byte-index) 439 (arithmetic-shift 1 bit-index))))) 440 (loop (- bit 1)))))))) 441 442 (define (%c-value-divmod a b nbytes unsigned?) 443 (let* ((aa (%c-value-trunc a nbytes (not unsigned?))) 444 (bb (%c-value-trunc b nbytes (not unsigned?))) 445 (aneg (and (not unsigned?) 446 (%c-value-signed-negative? aa %C-VALUE-BYTES))) 447 (bneg (and (not unsigned?) 448 (%c-value-signed-negative? bb %C-VALUE-BYTES))) 449 (amag (if aneg (%c-value-negate/raw aa) aa)) 450 (bmag (if bneg (%c-value-negate/raw bb) bb)) 451 (qr (%c-value-udivmod amag bmag)) 452 (q (if (if aneg (not bneg) bneg) 453 (%c-value-negate (car qr)) (car qr))) 454 (r (if aneg (%c-value-negate (cdr qr)) (cdr qr)))) 455 (cons (%c-value-trunc q nbytes (not unsigned?)) 456 (%c-value-trunc r nbytes (not unsigned?))))) 457 458 (define (%c-value->decimal-bv v) 459 (cond 460 ((not (c-value? v)) (fixnum->bv v 10)) 461 ((%c-value-zero? v) "0") 462 (else 463 (let* ((src (c-value-bytes v)) 464 (work (bytevector-copy src 0 (bytevector-length src)))) 465 (let digits ((acc '())) 466 (let divide ((i 7) (rem 0)) 467 (cond 468 ((< i 0) 469 (let ((acc2 (cons (bv-of-byte (+ 48 rem)) acc))) 470 (if (= (%c-value-bv-ucmp work 471 (make-bytevector %C-VALUE-BYTES 0)) 0) 472 (bv-cat acc2) 473 (digits acc2)))) 474 (else 475 (let ((z (+ (* rem 256) (bytevector-u8-ref work i)))) 476 (bytevector-u8-set! work i (quotient z 10)) 477 (divide (- i 1) (remainder z 10))))))))))) 478 479 (define (%c-value->hex-bv v) 480 (let* ((src (c-value-bytes (%c-value-coerce v))) 481 (hex "0123456789abcdef") 482 (out (make-bytevector 18 48))) 483 (bytevector-u8-set! out 0 48) 484 (bytevector-u8-set! out 1 120) 485 (let loop ((i 7) (j 2)) 486 (cond ((< i 0) out) 487 (else 488 (let ((b (bytevector-u8-ref src i))) 489 (bytevector-u8-set! out j 490 (bytevector-u8-ref hex (quotient b 16))) 491 (bytevector-u8-set! out (+ j 1) 492 (bytevector-u8-ref hex (remainder b 16))) 493 (loop (- i 1) (+ j 2)))))))) 494 495 (define (%c-value-source-bv v) 496 (%c-value->decimal-bv (if (c-int-lit? v) (c-int-lit-value v) v))) 497 (define (%c-value-literal-bv v) 498 (let ((n (if (c-int-lit? v) (c-int-lit-value v) v))) 499 (if (c-value? n) (%c-value->hex-bv n) (fixnum->bv n 10)))) 500 501 (define (%c-value-u32-literal-bv v word-index) 502 ;; Render one little-endian 32-bit limb without ever converting it to a 503 ;; Scheme integer. This is used when an RV32 %li materializes one half of 504 ;; an i64/u64 C constant. 505 (let ((full (%c-value->hex-bv v))) 506 (cond 507 ((= word-index 0) (bv-cat (list "0x" (bv-slice full 10 18)))) 508 ((= word-index 1) (bv-cat (list "0x" (bv-slice full 2 10)))) 509 (else (die #f "C constant: bad u32 limb index" word-index))))) 510 511 (define (%c-value-parse-decimal-bv bv) 512 (let ((n (bytevector-length bv))) 513 (let loop ((i 0) (v 0)) 514 (cond 515 ((= i n) (cons #t v)) 516 (else 517 (let ((b (bytevector-u8-ref bv i))) 518 (cond ((or (< b 48) (> b 57)) (cons #f #f)) 519 (else 520 (loop (+ i 1) 521 (%c-value-mul-small-add v 10 (- b 48))))))))))) 522 523 (define (%c-value-fits-unsigned-bits? v bits) 524 (let* ((cv (%c-value-coerce v)) 525 (bv (c-value-bytes cv)) 526 (whole (quotient bits 8)) 527 (part (remainder bits 8))) 528 (let loop ((i (+ whole (if (= part 0) 0 1)))) 529 (cond 530 ((= i %C-VALUE-BYTES) 531 (if (= part 0) #t 532 (< (bytevector-u8-ref bv whole) 533 (arithmetic-shift 1 part)))) 534 ((not (= (bytevector-u8-ref bv i) 0)) #f) 535 (else (loop (+ i 1))))))) 536 537 (define (%c-int-raw v) 538 (if (c-int-lit? v) (c-int-lit-value v) v)) 539 540 ;; -------------------------------------------------------------------- 541 ;; output buffer (fixed-size pre-allocated byte storage) 542 ;; 543 ;; Every buf owns one bytevector of `cap` bytes, plus a write `offset`. 544 ;; buf-push! is bytevector-copy! into storage — zero allocation per 545 ;; push, no chunks list to chase. The destination storage has stable 546 ;; identity, so byte-level mutations survive collection. 547 ;; 548 ;; Sizing knobs live in one place so they're easy to tune as inputs 549 ;; grow. cg-init picks per-buf caps; the per-fn bufs are reused 550 ;; across functions (reset, not re-allocated). 551 ;; -------------------------------------------------------------------- 552 553 ;; Tuning constants — total fixed pre-allocation is about 12.58 MiB on 554 ;; 64-bit targets and 12.33 MiB on RV32. Bump these when a workload overflows; the buf-overflow 555 ;; die() reports off/len/cap so misses are easy to diagnose. 556 ;; 557 ;; Each cap is a power of two. scheme1's bv_capacity_for rounds the 558 ;; requested length up to the smallest power of two ≥ n, so asking for 559 ;; 2^k bytes consumes exactly 2^k of heap. 560 (define %BUF-CAP-TEXT 8388608) ; 8 MiB: .text + entry stub 561 (define %BUF-CAP-DATA 2097152) ; 2 MiB: .data (strings, globals) 562 (define %BUF-CAP-BSS 2097152) ; 2 MiB: .bss 563 (define %BUF-CAP-FN (if (= (target-word-bytes) 4) 262144 524288)) 564 ; 256/512 KiB: per-fn body asm 565 (define %BUF-CAP-PROLOGUE 16384) ; 16 KiB: per-fn prologue 566 (define %BUF-CAP-DEFAULT 65536) ; 64 KiB: make-buf fallback 567 568 (define-record-type buf 569 (%buf storage offset cap) 570 buf? 571 (storage buf-storage) ; bv: pre-allocated, never resized 572 (offset buf-offset buf-offset-set!) ; fixnum: bytes written so far 573 (cap buf-cap)) ; fixnum: storage capacity 574 575 (define (make-buf/cap cap) 576 (%buf (make-bytevector cap 0) 0 cap)) 577 578 (define (make-buf) (make-buf/cap %BUF-CAP-DEFAULT)) 579 580 (define (buf-push! b bv) 581 (let* ((n (bytevector-length bv)) 582 (off (buf-offset b)) 583 (newoff (+ off n))) 584 (cond 585 ((> newoff (buf-cap b)) 586 (die #f "buf overflow" off n (buf-cap b)))) 587 (bytevector-copy! (buf-storage b) off bv 0 n) 588 (buf-offset-set! b newoff))) 589 590 (define (buf-flush b) 591 ;; Snapshot the used prefix as a fresh bv. One allocation; the 592 ;; underlying storage is unchanged. 593 (bytevector-copy (buf-storage b) 0 (buf-offset b))) 594 595 (define (buf-reset! b) (buf-offset-set! b 0)) 596 597 (define (buf-drain! dst src) 598 ;; Copy src's used bytes into dst at dst's current write head; reset 599 ;; src to empty. dst and src must be distinct bufs. 600 (let* ((slen (buf-offset src)) 601 (doff (buf-offset dst)) 602 (newoff (+ doff slen))) 603 (cond 604 ((> newoff (buf-cap dst)) 605 (die #f "buf-drain overflow" doff slen (buf-cap dst)))) 606 (bytevector-copy! (buf-storage dst) doff (buf-storage src) 0 slen) 607 (buf-offset-set! dst newoff) 608 (buf-offset-set! src 0))) 609 610 ;; -------------------------------------------------------------------- 611 ;; diagnostics + I/O 612 ;; -------------------------------------------------------------------- 613 (define (die loc msg . irritants) 614 ;; Format: 615 ;; <file>:<line>:<col>: error: <msg>: <irritant> <irritant> ... 616 ;; When loc is #f, the "<file>:<line>:<col>: " prefix is omitted. 617 ;; irritants are written via display semantics (no quoting); format's 618 ;; ~a handles bv/fixnum/pair/symbol the same way display does. 619 ;; 620 ;; All output is built into a single bv and sent to fd 2 with one 621 ;; sys-write loop, so a partial write doesn't interleave fragments 622 ;; from a concurrent process. 623 (let* ((prefix (if loc 624 (format "~a:~d:~d: error: " 625 (loc-file loc) (loc-line loc) (loc-col loc)) 626 "error: ")) 627 (head (bytevector-append prefix (format "~a" msg))) 628 ;; Irritants get ": " before the first and " " between the rest. 629 (tail (if (null? irritants) 630 (list NL-BV) 631 (let walk ((xs irritants) (sep ": ") (acc '())) 632 (if (null? xs) 633 (reverse (cons NL-BV acc)) 634 (walk (cdr xs) 635 " " 636 (cons (format "~a" (car xs)) 637 (cons sep acc))))))) 638 (out (bv-cat (cons head tail)))) 639 (write-bv-fd 2 out) 640 (sys-exit 1))) 641 642 (define (slurp-fd fd) 643 ;; Read fd to EOF. Uses BUFSIZE chunks (same constant the prelude's 644 ;; port layer uses); bv-concat-reverse builds the result in one 645 ;; allocation so a multi-MB tcc.c stays linear. 646 (let ((buf (make-bytevector BUFSIZE))) 647 (let loop ((acc '())) 648 (let ((r (sys-read fd buf 0 BUFSIZE))) 649 (cond ((not (car r)) 650 (die #f "slurp-fd: sys-read failed" (cdr r))) 651 ((zero? (cdr r)) 652 (bv-concat-reverse acc)) 653 (else 654 (loop (cons (bytevector-copy buf 0 (cdr r)) acc)))))))) 655 656 (define (write-bv-fd fd bv) 657 ;; Full write or die. sys-write may write fewer bytes than requested; 658 ;; advance the offset and retry the unwritten tail. 659 ;; 660 ;; On failure we sys-exit directly instead of routing through `die` 661 ;; — `die` itself uses write-bv-fd, so a write failure to fd 2 must 662 ;; not recurse infinitely. Status 1 matches the contract for `die`. 663 (let ((len (bytevector-length bv))) 664 (let loop ((off 0)) 665 (if (= off len) 666 #t 667 (let ((r (sys-write fd bv off (- len off)))) 668 (cond ((not (car r)) (sys-exit 1)) 669 ((zero? (cdr r)) (sys-exit 1)) 670 (else (loop (+ off (cdr r)))))))))) 671 672 (define (write-bv-range-fd fd bv start len) 673 ;; Write exactly LEN bytes starting at START without first copying the 674 ;; range into a right-sized bytevector. This is important for the large, 675 ;; fixed-capacity codegen buffers: their used prefixes can be streamed 676 ;; without requiring another contiguous managed allocation. 677 (let loop ((off start) (left len)) 678 (if (= left 0) 679 #t 680 (let ((r (sys-write fd bv off left))) 681 (cond ((not (car r)) (sys-exit 1)) 682 ((zero? (cdr r)) (sys-exit 1)) 683 (else (loop (+ off (cdr r)) (- left (cdr r))))))))) 684 685 ;; -------------------------------------------------------------------- 686 ;; debug logging 687 ;; 688 ;; Cheap sticky on/off: the cc compiler is single-threaded and short- 689 ;; lived, so a top-level mutable flag is fine. Toggle via 690 ;; (debug-log-on!) / (debug-log-off!). When on, (debug-log msg . irr) 691 ;; writes one line to fd 2 in the same display-style format as `die`, 692 ;; but doesn't abort. The intent is to trace heap usage between cc 693 ;; phases (lex/pp/parse/cg-finish) without compile-time conditionals. 694 ;; -------------------------------------------------------------------- 695 (define %debug-log-enabled #f) 696 (define (debug-log-on!) (set! %debug-log-enabled #t)) 697 (define (debug-log-off!) (set! %debug-log-enabled #f)) 698 (define (debug-log? ) %debug-log-enabled) 699 700 ;; --cc-trace-emit: if on, cg-fn-end injects a `%trace(MANGLED)` line 701 ;; at the top of each emitted function body (right after the prologue's 702 ;; argument-spill, so the macro is free to clobber a0..a3). Pairs with 703 ;; libp1pp's %trace macro + libp1pp__trace runtime helper to produce a 704 ;; stderr line per function entry, with the runtime address of the 705 ;; first body instruction. See P1/P1pp.P1pp's "Tracepoint" section. 706 (define %trace-emit-enabled #f) 707 (define (trace-emit-on!) (set! %trace-emit-enabled #t)) 708 (define (trace-emit-off!) (set! %trace-emit-enabled #f)) 709 (define (trace-emit?) %trace-emit-enabled) 710 711 (define (debug-log msg . irritants) 712 (cond 713 (%debug-log-enabled 714 (let* ((head (bytevector-append "[cc] " (format "~a" msg))) 715 (tail (if (null? irritants) 716 (list NL-BV) 717 (let walk ((xs irritants) (sep ": ") (acc '())) 718 (if (null? xs) 719 (reverse (cons NL-BV acc)) 720 (walk (cdr xs) 721 " " 722 (cons (format "~a" (car xs)) 723 (cons sep acc))))))) 724 (out (bv-cat (cons head tail)))) 725 (write-bv-fd 2 out))) 726 (else #t))) 727 728 ;; -------------------------------------------------------------------- 729 ;; fresh-name generator (used for cg label counters, etc.) 730 ;; -------------------------------------------------------------------- 731 (define (make-namer prefix) 732 ;; Returns a thunk; each call yields prefix0, prefix1, ... as a fresh 733 ;; bv. The counter lives in the closure's lexical environment; scheme1 734 ;; closures heap-capture by reference, so set! on ctr is sticky. 735 (let ((ctr 0)) 736 (lambda () 737 (let ((s (bytevector-append prefix (number->string ctr 10)))) 738 (set! ctr (+ ctr 1)) 739 s)))) 740 ;; cc/data.scm — record types and symbol alphabets shared across modules. 741 742 ;; -------------------------------------------------------------------- 743 ;; loc — source location for diagnostics 744 ;; -------------------------------------------------------------------- 745 (define-record-type loc 746 (%loc file line col) 747 loc? 748 (file loc-file) ; bv 749 (line loc-line) ; fixnum 750 (col loc-col)) ; fixnum 751 752 ;; -------------------------------------------------------------------- 753 ;; tok — lexer token. 754 ;; -------------------------------------------------------------------- 755 (define-record-type tok 756 (%tok kind value loc hide) 757 tok? 758 (kind tok-kind) ; IDENT | INT | STR | CHAR | KW | PUNCT 759 ; | NL | HASH | EOF 760 (value tok-value) ; bv | fixnum/c-value/c-int-lit | symbol | #f 761 (loc tok-loc) ; loc 762 (hide tok-hide)) ; list of bv (macro names already expanded) 763 764 (define (make-tok kind value loc) 765 (%tok kind value loc '())) 766 767 ;; -------------------------------------------------------------------- 768 ;; macro — preprocessor macro definition 769 ;; -------------------------------------------------------------------- 770 (define-record-type macro 771 (%macro kind params body) 772 macro? 773 (kind macro-kind) ; 'obj | 'fn | 'fn-vararg 774 (params macro-params) ; list of bv 775 (body macro-body)) ; list of tok 776 777 ;; -------------------------------------------------------------------- 778 ;; ctype — C type. 779 ;; 780 ;; size/align/ext mutate only on forward struct/union completion (see 781 ;; complete-agg!). Every other ctype is constructed in its final shape 782 ;; and treated as immutable thereafter. 783 ;; -------------------------------------------------------------------- 784 (define-record-type ctype 785 (%ctype kind size align ext) 786 ctype? 787 (kind ctype-kind) 788 (size ctype-size ctype-size-set!) 789 (align ctype-align ctype-align-set!) 790 (ext ctype-ext ctype-ext-set!)) 791 792 ;; Active P1 data model. scheme1 supplies these from the selected backend, 793 ;; so cc.scm emits ILP32 for RV32 and LP64 for the existing targets. 794 (define %CC-WORD-BYTES (target-word-bytes)) 795 (define %CC-WORD-BITS (target-word-bits)) 796 (define %CC-PAIR-BYTES (* 2 %CC-WORD-BYTES)) 797 798 ;; Interned primitive ctypes. Equality is eq?. 799 (define %t-void (%ctype 'void -1 -1 #f)) 800 (define %t-i8 (%ctype 'i8 1 1 #f)) 801 (define %t-u8 (%ctype 'u8 1 1 #f)) 802 (define %t-i16 (%ctype 'i16 2 2 #f)) 803 (define %t-u16 (%ctype 'u16 2 2 #f)) 804 (define %t-i32 (%ctype 'i32 4 4 #f)) 805 (define %t-u32 (%ctype 'u32 4 4 #f)) 806 (define %t-i64 (%ctype 'i64 8 8 #f)) 807 (define %t-u64 (%ctype 'u64 8 8 #f)) 808 (define %t-word-i (if (= %CC-WORD-BYTES 4) %t-i32 %t-i64)) 809 (define %t-word-u (if (= %CC-WORD-BYTES 4) %t-u32 %t-u64)) 810 (define %t-bool (%ctype 'bool 1 1 #f)) 811 ;; Floating-point ctypes are parsed but never codegen'd; see CC.md §Cut. 812 ;; Sizes/aligns match the SysV ABI so struct layout containing fp fields 813 ;; works even when the cg refuses to emit fp ops. 814 (define %t-flt (%ctype 'flt 4 4 #f)) 815 (define %t-dbl (%ctype 'dbl 8 8 #f)) 816 (define %t-ldbl (%ctype 'ldbl 8 8 #f)) 817 818 ;; Select the first type in C11 6.4.4.1's candidate list that can represent 819 ;; an integer constant. CType intentionally interns same-width C types into 820 ;; one representation (for example, int and long are both i32 on ILP32), so 821 ;; this returns the correct width and signedness even where their ranks differ. 822 (define (%c-int-type lit) 823 (let* ((v (%c-int-raw lit)) 824 (u? (and (c-int-lit? lit) (c-int-lit-unsigned? lit))) 825 (lc (if (c-int-lit? lit) (c-int-lit-long-count lit) 0)) 826 (decimal? (if (c-int-lit? lit) (c-int-lit-decimal? lit) #t)) 827 (fits-i32? (%c-value-fits-unsigned-bits? v 31)) 828 (fits-u32? (%c-value-fits-unsigned-bits? v 32)) 829 (fits-i64? (%c-value-fits-unsigned-bits? v 63))) 830 (cond 831 ;; LL / ULL have only a 64-bit candidate in the supported data models. 832 ((>= lc 2) 833 (cond (u? %t-u64) (fits-i64? %t-i64) (else %t-u64))) 834 ;; L / UL begin at the target's C long width. 835 ((= lc 1) 836 (cond 837 (u? 838 (if (= %CC-WORD-BITS 32) 839 (if fits-u32? %t-u32 %t-u64) 840 %t-u64)) 841 (decimal? 842 (if (= %CC-WORD-BITS 32) 843 (cond (fits-i32? %t-i32) (fits-i64? %t-i64) (else %t-u64)) 844 (if fits-i64? %t-i64 %t-u64))) 845 ((= %CC-WORD-BITS 32) 846 (cond (fits-i32? %t-i32) (fits-u32? %t-u32) 847 (fits-i64? %t-i64) (else %t-u64))) 848 (else (if fits-i64? %t-i64 %t-u64)))) 849 ;; A bare U suffix starts at unsigned int on both targets. 850 (u? (if fits-u32? %t-u32 %t-u64)) 851 ;; Unsuffixed decimal has no unsigned candidates before the extension 852 ;; fallback; octal/hex may select unsigned int or unsigned long. 853 (decimal? 854 (cond (fits-i32? %t-i32) (fits-i64? %t-i64) (else %t-u64))) 855 (else 856 (cond (fits-i32? %t-i32) (fits-u32? %t-u32) 857 (fits-i64? %t-i64) (else %t-u64)))))) 858 859 ;; -------------------------------------------------------------------- 860 ;; sym — declared identifier (function, variable, typedef, …) 861 ;; defined? distinguishes a forward declaration (extern fn proto, extern 862 ;; var) from a definition (fn body, var with initializer, tentative def 863 ;; without `extern`). scope-bind! merges compatible decls; only two 864 ;; defined? syms with the same name fire a redefinition error. 865 ;; 866 ;; sym is immutable — no `sym-*-set!` accessor exists. scope-bind!'s 867 ;; merge logic constructs a fresh sym rather than mutating in place. 868 ;; Keeping it immutable also makes shared world bindings straightforward. 869 ;; -------------------------------------------------------------------- 870 (define-record-type sym 871 (%sym name kind storage type slot defined?) 872 sym? 873 (name sym-name) ; bv 874 (kind sym-kind) ; symbol from §1.7 875 (storage sym-storage) ; symbol from §1.8 or #f 876 (type sym-type) ; ctype 877 (slot sym-slot) ; fixnum (auto local / param / enum-const value) 878 ; | #f (fn / global var / typedef) 879 (defined? sym-defined?)) ; #t = definition, #f = decl-only 880 881 ;; -------------------------------------------------------------------- 882 ;; opnd — operand on cg's vstack. 883 ;; -------------------------------------------------------------------- 884 (define-record-type opnd 885 (%opnd kind type ext lval?) 886 opnd? 887 (kind opnd-kind) 888 (type opnd-type) 889 (ext opnd-ext) 890 (lval? opnd-lval?)) 891 892 ;; -------------------------------------------------------------------- 893 ;; loop-ctx — entry on parser's loop/switch context stack. 894 ;; -------------------------------------------------------------------- 895 (define-record-type loop-ctx 896 (%loop-ctx kind tag has-continue?) 897 loop-ctx? 898 (kind loop-ctx-kind) 899 (tag loop-ctx-tag) 900 (has-continue? loop-ctx-has-continue?)) 901 902 ;; -------------------------------------------------------------------- 903 ;; fn-ctx — current-function context inside the parser. 904 ;; -------------------------------------------------------------------- 905 (define-record-type fn-ctx 906 (%fn-ctx name return-type params variadic? labels static-counter) 907 fn-ctx? 908 (name fn-ctx-name) 909 (return-type fn-ctx-return-type) 910 (params fn-ctx-params) 911 (variadic? fn-ctx-variadic?) 912 (labels fn-ctx-labels fn-ctx-labels-set!) 913 ;; Monotonic declaration identity for block-scope static objects. The C 914 ;; identifier alone is not unique: separate lexical scopes in one function 915 ;; may each declare `static ... order[]` and must emit distinct labels. 916 (static-counter fn-ctx-static-counter fn-ctx-static-counter-set!)) 917 918 ;; -------------------------------------------------------------------- 919 ;; world — cross-decl persistent parser/cg state. The same world record 920 ;; is shared by pstate and cg so its slots — scope/tag hash-frame stacks, 921 ;; the interned-string hash, and tentative-definition list + membership 922 ;; hash — can be reasoned about as one persistent root graph. 923 ;; -------------------------------------------------------------------- 924 (define-record-type world 925 (%world scope tags str-pool tentatives) 926 world? 927 (scope world-scope world-scope-set!) 928 (tags world-tags world-tags-set!) 929 (str-pool world-str-pool world-str-pool-set!) 930 (tentatives world-tentatives world-tentatives-set!)) 931 932 (define (make-world) 933 (%world (list (%make-hash-table 64)) 934 (list (%make-hash-table 16)) 935 (%make-hash-table 16) 936 (cons '() (%make-hash-table 16)))) 937 938 ;; -------------------------------------------------------------------- 939 ;; pstate — parser state. Owned by parse.scm; read-only to cg. 940 ;; -------------------------------------------------------------------- 941 ;; iter holds a tok-iter (typically a pp-iter chained over a lex-iter). 942 ;; peek / peek2 / advance go through iter-peek / iter-peek2 / iter-next 943 ;; so the parser pulls one token at a time, with no full materialized 944 ;; token list. 945 (define-record-type pstate 946 (%pstate iter world loops fn-ctx cg) 947 pstate? 948 (iter ps-iter ps-iter-set!) 949 (world ps-world) 950 (loops ps-loops ps-loops-set!) 951 (fn-ctx ps-fn-ctx ps-fn-ctx-set!) 952 (cg ps-cg)) 953 954 (define (ps-scope ps) (world-scope (ps-world ps))) 955 (define (ps-scope-set! ps v) (world-scope-set! (ps-world ps) v)) 956 (define (ps-tags ps) (world-tags (ps-world ps))) 957 (define (ps-tags-set! ps v) (world-tags-set! (ps-world ps) v)) 958 959 ;; -------------------------------------------------------------------- 960 ;; cg — codegen state. Owned by cg.scm. 961 ;; -------------------------------------------------------------------- 962 ;; fn-buf and prologue-buf are pre-allocated (cg-init) and reused across 963 ;; functions — cg-fn-begin/v calls buf-reset! on them, cg-fn-end drains 964 ;; them into cg-text via buf-drain!. Fixed-storage byte writes remain 965 ;; stable while ordinary transient parser data is reclaimed by the GC. 966 ;; 967 ;; in-fn? discriminates "currently inside a function body" so 968 ;; %cg-emit-buf can route emits to fn-buf during the body and cg-text 969 ;; outside it (entry stub, etc.). 970 ;; 971 ;; cg-fn-meta: transient per-function state (fn-name, ret-slot, ret-type, 972 ;; vararg-first-slot, indirect-slots, switch-case lists, ...). Reset on 973 ;; cg-fn-begin/v; reads via %cg-fn-get / writes via %cg-fn-set!. 974 ;; lib? / str-prefix encode the --lib=PFX flag from cc-main: 975 ;; #f / "" — exec mode (default): cg-finish emits the 976 ;; p1_main entry stub and trailing :ELF_end, and 977 ;; cg-intern-string labels strings cc__str_N. 978 ;; #t / "<pfx>" — library mode: skip the stub and :ELF_end so the 979 ;; output catm's into a larger TU, and label strings 980 ;; <pfx>cc__str_N so two cc.scm outputs in the same 981 ;; link don't collide on cc__str_0..N. 982 (define-record-type cg 983 (%cg text data bss vstack frame-hi label-ctr world fn-meta fn-buf prologue-buf max-outgoing in-fn? lib? str-prefix) 984 cg? 985 (text cg-text) 986 (data cg-data) 987 (bss cg-bss) 988 (vstack cg-vstack cg-vstack-set!) 989 (frame-hi cg-frame-hi cg-frame-hi-set!) 990 (label-ctr cg-label-ctr cg-label-ctr-set!) 991 (world cg-world) 992 (fn-meta cg-fn-meta cg-fn-meta-set!) 993 (fn-buf cg-fn-buf) 994 (prologue-buf cg-prologue-buf) 995 (max-outgoing cg-max-outgoing cg-max-outgoing-set!) 996 (in-fn? cg-in-fn? cg-in-fn?-set!) 997 (lib? cg-lib?) 998 (str-prefix cg-str-prefix)) 999 1000 (define (cg-str-pool cg) (world-str-pool (cg-world cg))) 1001 (define (cg-str-pool-set! cg v) (world-str-pool-set! (cg-world cg) v)) 1002 1003 ;; ctype predicates used by both cg and parser. 1004 (define (%ctype-ptr? t) 1005 (let ((k (ctype-kind t))) 1006 (if (eq? k 'ptr) #t (eq? k 'arr)))) 1007 1008 (define (%ctype-pointee t) 1009 (cond ((eq? (ctype-kind t) 'ptr) (ctype-ext t)) 1010 ((eq? (ctype-kind t) 'arr) (car (ctype-ext t))) 1011 (else #f))) 1012 1013 (define (%ctype-unsigned? t) 1014 (let ((k (ctype-kind t))) 1015 (cond ((eq? k 'u8) #t) ((eq? k 'u16) #t) ((eq? k 'u32) #t) 1016 ((eq? k 'u64) #t) ((eq? k 'bool) #t) 1017 ((eq? k 'ptr) #t) ((eq? k 'arr) #t) ((eq? k 'fn) #t) 1018 (else #f)))) 1019 1020 (define (%ctype-arith? t) 1021 (let ((k (ctype-kind t))) 1022 (cond ((eq? k 'i8) #t) ((eq? k 'i16) #t) ((eq? k 'i32) #t) 1023 ((eq? k 'i64) #t) ((eq? k 'u8) #t) ((eq? k 'u16) #t) 1024 ((eq? k 'u32) #t) ((eq? k 'u64) #t) ((eq? k 'bool) #t) 1025 (else #f)))) 1026 1027 (define (%ctype-fp? t) 1028 (let ((k (ctype-kind t))) 1029 (cond ((eq? k 'flt) #t) ((eq? k 'dbl) #t) ((eq? k 'ldbl) #t) 1030 (else #f)))) 1031 1032 (define (%ctype-wide-int? t) 1033 ;; P1-32 lowers C's 64-bit integer types through a two-word pair. P1-64 1034 ;; keeps the historical one-register representation. 1035 (and (= %CC-WORD-BYTES 4) 1036 (let ((k (ctype-kind t))) 1037 (or (eq? k 'i64) (eq? k 'u64))))) 1038 1039 ;; -------------------------------------------------------------------- 1040 ;; Symbol alphabets — canonical alists. 1041 ;; -------------------------------------------------------------------- 1042 1043 ;; Keyword bytevector → keyword symbol. 1044 (define %keyword-alist 1045 '(;; storage 1046 ("auto" . auto) ("register" . register) ("static" . static) 1047 ("extern" . extern) ("typedef" . typedef) 1048 ;; qualifiers (parsed and discarded by parse) 1049 ("const" . const) ("volatile" . volatile) ("restrict" . restrict) 1050 ("inline" . inline) ("_Noreturn" . _Noreturn) 1051 ;; GNU attribute spec — parsed and discarded; see skip-gnu-attribute! 1052 ("__attribute__" . __attribute__) 1053 ;; type specifiers 1054 ("void" . void) ("char" . char) ("short" . short) 1055 ("int" . int) ("long" . long) 1056 ("signed" . signed) ("unsigned" . unsigned) ("_Bool" . _Bool) 1057 ;; rejected type specifiers (KW so diagnostics are crisp) 1058 ("float" . float) ("double" . double) 1059 ;; aggregates 1060 ("struct" . struct) ("union" . union) ("enum" . enum) 1061 ;; statements 1062 ("if" . if) ("else" . else) 1063 ("while" . while) ("do" . do) ("for" . for) 1064 ("switch" . switch) ("case" . case) ("default" . default) 1065 ("break" . break) ("continue" . continue) 1066 ("return" . return) ("goto" . goto) 1067 ;; operators 1068 ("sizeof" . sizeof) 1069 ;; reserved-and-rejected (KW so diagnostics are crisp) 1070 ("_Generic" . _Generic) ("_Atomic" . _Atomic) 1071 ("_Thread_local" . _Thread_local) 1072 ("_Alignof" . _Alignof) ("__alignof" . _Alignof) 1073 ("__alignof__" . _Alignof) ("_Alignas" . _Alignas) 1074 ("_Static_assert" . _Static_assert) 1075 ("_Complex" . _Complex) ("_Imaginary" . _Imaginary))) 1076 1077 (define %keyword-map (alist->hash %keyword-alist)) 1078 1079 ;; Punctuator bytevector → punct symbol. 1080 ;; Listed longest-match-first; the lexer scans this list in order. 1081 ;; Digraphs (<: :> <% %> %: %:%:) lex to their standard equivalents. 1082 (define %punct-alist 1083 '(;; 4-byte 1084 ("%:%:" . paste) 1085 ;; 3-byte 1086 ("..." . ellipsis) ("<<=" . shl-eq) (">>=" . shr-eq) 1087 ;; 2-byte 1088 ("##" . paste) ("->" . arrow) 1089 ("++" . inc) ("--" . dec) 1090 ("<<" . shl) (">>" . shr) 1091 ("<=" . le) (">=" . ge) ("==" . eq2) ("!=" . ne) 1092 ("&&" . land) ("||" . lor) 1093 ("+=" . plus-eq) ("-=" . minus-eq) ("*=" . star-eq) 1094 ("/=" . slash-eq) ("%=" . pct-eq) 1095 ("&=" . amp-eq) ("^=" . caret-eq) ("|=" . bar-eq) 1096 ;; digraphs (mapped to the standard equivalent symbol) 1097 ("<:" . lbrack) (":>" . rbrack) 1098 ("<%" . lbrace) ("%>" . rbrace) ("%:" . hash) 1099 ;; 1-byte 1100 ("[" . lbrack) ("]" . rbrack) 1101 ("(" . lparen) (")" . rparen) 1102 ("{" . lbrace) ("}" . rbrace) 1103 ("." . dot) ("," . comma) (";" . semi) (":" . colon) ("?" . qmark) 1104 ("+" . plus) ("-" . minus) ("*" . star) ("/" . slash) ("%" . pct) 1105 ("&" . amp) ("|" . bar) ("^" . caret) ("~" . tilde) ("!" . bang) 1106 ("<" . lt) (">" . gt) ("=" . assign) 1107 ("#" . hash))) 1108 ;; cc/lex.scm — bytestream → token list. Pure function; no I/O, 1109 ;; no macro awareness. 1110 ;; 1111 ;; The lexer walks `src` byte-by-byte, threading (pos, line, col) 1112 ;; explicitly through every helper (no mutable state). Each token 1113 ;; captures its starting loc; helpers return (tok npos nline ncol). 1114 ;; Trigraphs and `\<newline>` line splicing are handled via a single 1115 ;; logical-byte primitive `%lex-peek`: it advances over splices and 1116 ;; translates trigraphs in-place, so downstream code only ever sees 1117 ;; the "translation phase 2" stream. Comments are stripped at the 1118 ;; same level as whitespace. NL tokens are emitted at every physical 1119 ;; newline so pp can use them to terminate directives. 1120 ;; 1121 (define (%lex-init!) #t) 1122 1123 ;; -------------------------------------------------------------------- 1124 ;; Byte-class predicates (raw u8 values, not chars). 1125 ;; -------------------------------------------------------------------- 1126 (define (%digit? b) (if (< b 48) #f (if (< 57 b) #f #t))) ; '0'..'9' 1127 (define (%hex? b) 1128 (cond ((%digit? b) #t) 1129 ((if (< b 65) #f (if (< 70 b) #f #t)) #t) ; 'A'..'F' 1130 ((if (< b 97) #f (if (< 102 b) #f #t)) #t) ; 'a'..'f' 1131 (else #f))) 1132 (define (%octal? b) (if (< b 48) #f (if (< 55 b) #f #t))) ; '0'..'7' 1133 (define (%alpha? b) 1134 (cond ((if (< b 65) #f (if (< 90 b) #f #t)) #t) ; 'A'..'Z' 1135 ((if (< b 97) #f (if (< 122 b) #f #t)) #t) ; 'a'..'z' 1136 (else #f))) 1137 (define (%ident-start? b) (or (%alpha? b) (= b 95))) ; '_' 1138 (define (%ident-cont? b) (or (%ident-start? b) (%digit? b))) 1139 (define (%hspace? b) (or (= b 32) (= b 9) (= b 11) (= b 12))) ; SP TAB VT FF 1140 (define (%newline? b) (= b 10)) ; '\n' 1141 1142 ;; -------------------------------------------------------------------- 1143 ;; Logical byte access. %lex-peek returns 1144 ;; (byte npos nline ncol) 1145 ;; where (npos, nline, ncol) points *just past* the consumed physical 1146 ;; bytes. On EOF it returns (#f pos line col). 1147 ;; 1148 ;; Two transformations folded in here: 1149 ;; 1150 ;; - Trigraphs: ??= ??( ??/ ??) ??' ??< ??! ??> ??- 1151 ;; # [ \ ] ^ { | } ~ 1152 ;; The pair `??` followed by one of the nine trigraph completers 1153 ;; produces the translated byte and advances 3 source bytes. 1154 ;; - Line splice: a backslash immediately followed by `\n` is removed 1155 ;; as a unit (incrementing line, resetting col to 1) and we recurse 1156 ;; to fetch the next logical byte. 1157 ;; 1158 ;; Other escapes (e.g. `\<not-newline>`) are returned as-is — string and 1159 ;; char literals do their own escape-handling. 1160 ;; -------------------------------------------------------------------- 1161 (define (%trigraph-byte b) 1162 ;; Map the third trigraph byte to its replacement, or #f. 1163 (cond ((= b 61) 35) ; '=' -> '#' 1164 ((= b 40) 91) ; '(' -> '[' 1165 ((= b 47) 92) ; '/' -> '\\' 1166 ((= b 41) 93) ; ')' -> ']' 1167 ((= b 39) 94) ; '\'' -> '^' 1168 ((= b 60) 123) ; '<' -> '{' 1169 ((= b 33) 124) ; '!' -> '|' 1170 ((= b 62) 125) ; '>' -> '}' 1171 ((= b 45) 126) ; '-' -> '~' 1172 (else #f))) 1173 1174 (define (%lex-peek src pos line col) 1175 (let ((n (bytevector-length src))) 1176 (cond 1177 ((>= pos n) (list #f pos line col)) 1178 (else 1179 (let ((b (bytevector-u8-ref src pos))) 1180 (cond 1181 ;; Trigraph: ?? + completer 1182 ((and (= b 63) 1183 (< (+ pos 2) n) 1184 (= (bytevector-u8-ref src (+ pos 1)) 63)) 1185 (let ((tr (%trigraph-byte (bytevector-u8-ref src (+ pos 2))))) 1186 (if tr 1187 (list tr (+ pos 3) line (+ col 3)) 1188 (list b (+ pos 1) line (+ col 1))))) 1189 ;; Line splice: backslash + newline (consume both, no token) 1190 ((and (= b 92) 1191 (< (+ pos 1) n) 1192 (= (bytevector-u8-ref src (+ pos 1)) 10)) 1193 (%lex-peek src (+ pos 2) (+ line 1) 1)) 1194 ;; Newline: pass through but caller decides line/col bump 1195 ((%newline? b) 1196 (list b (+ pos 1) (+ line 1) 1)) 1197 (else 1198 (list b (+ pos 1) line (+ col 1))))))))) 1199 1200 ;; Convenience accessors over the 4-list. 1201 (define (%pk-byte p) (car p)) 1202 (define (%pk-pos p) (car (cdr p))) 1203 (define (%pk-line p) (car (cdr (cdr p)))) 1204 (define (%pk-col p) (car (cdr (cdr (cdr p))))) 1205 1206 ;; Fast-byte test. When (%fast-byte? b) is #t, reading b directly with 1207 ;; bytevector-u8-ref is exactly equivalent to %lex-peek's result: the 1208 ;; logical byte is b, npos = pos+1, nline unchanged, ncol = col+1, and 1209 ;; no list allocation is needed. Excludes the three bytes that %lex-peek 1210 ;; can transform: '?' (trigraph), '\\' (line splice), '\n' (line bump). 1211 (define (%fast-byte? b) 1212 (cond ((= b 63) #f) 1213 ((= b 92) #f) 1214 ((= b 10) #f) 1215 (else #t))) 1216 1217 ;; -------------------------------------------------------------------- 1218 ;; Whitespace + comment skipper. Returns (pos line col). 1219 ;; Handles spaces/tabs, // line comments, /* block */ comments. Does 1220 ;; *not* consume `\n` — newlines are tokens. 1221 ;; -------------------------------------------------------------------- 1222 (define (%skip-ws-and-comments src pos line col file) 1223 (let ((n (bytevector-length src))) 1224 (cond 1225 ((>= pos n) (list pos line col)) 1226 (else 1227 (let ((b (bytevector-u8-ref src pos))) 1228 (cond 1229 ((and (%fast-byte? b) (%hspace? b)) 1230 (%skip-ws-and-comments src (+ pos 1) line (+ col 1) file)) 1231 ((%fast-byte? b) 1232 ;; Fast-byte that isn't hspace. Only '/' is interesting; 1233 ;; everything else terminates the skip. 1234 (cond 1235 ((= b 47) (%maybe-comment src pos line col file)) 1236 (else (list pos line col)))) 1237 (else 1238 ;; Slow path: trigraph / splice / newline. 1239 (let* ((p (%lex-peek src pos line col)) 1240 (b2 (%pk-byte p))) 1241 (cond 1242 ((not b2) (list pos line col)) 1243 ((%hspace? b2) 1244 (%skip-ws-and-comments src (%pk-pos p) (%pk-line p) (%pk-col p) 1245 file)) 1246 ((= b2 47) (%maybe-comment src pos line col file)) 1247 (else (list pos line col))))))))))) 1248 1249 (define (%maybe-comment src pos line col file) 1250 ;; Source byte at pos resolves to '/'. Decide between // line comment, 1251 ;; /* block comment, or "leave the slash alone" (it's a punctuator). 1252 (let* ((p (%lex-peek src pos line col)) 1253 (q (%lex-peek src (%pk-pos p) (%pk-line p) (%pk-col p))) 1254 (b2 (%pk-byte q))) 1255 (cond 1256 ((and b2 (= b2 47)) 1257 (%skip-line-comment src (%pk-pos q) (%pk-line q) (%pk-col q) file)) 1258 ((and b2 (= b2 42)) 1259 (%skip-block-comment src (%pk-pos q) (%pk-line q) (%pk-col q) 1260 file line col)) 1261 (else (list pos line col))))) 1262 1263 (define (%skip-line-comment src pos line col file) 1264 ;; Consume bytes until end-of-stream or until we *see* '\n' (do not 1265 ;; consume the newline itself; outer loop emits the NL). 1266 (let ((n (bytevector-length src))) 1267 (cond 1268 ((>= pos n) (%skip-ws-and-comments src pos line col file)) 1269 (else 1270 (let ((b (bytevector-u8-ref src pos))) 1271 (cond 1272 ;; '\n' terminates without consuming. 1273 ((= b 10) (%skip-ws-and-comments src pos line col file)) 1274 ((%fast-byte? b) 1275 (%skip-line-comment src (+ pos 1) line (+ col 1) file)) 1276 (else 1277 ;; Slow path: ?/\ — let %lex-peek handle trigraph/splice. 1278 (let* ((p (%lex-peek src pos line col)) 1279 (b2 (%pk-byte p))) 1280 (cond 1281 ((not b2) (%skip-ws-and-comments src pos line col file)) 1282 ((%newline? b2) (%skip-ws-and-comments src pos line col file)) 1283 (else 1284 (%skip-line-comment src (%pk-pos p) (%pk-line p) (%pk-col p) 1285 file))))))))))) 1286 1287 (define (%skip-block-comment src pos line col file start-line start-col) 1288 (let ((n (bytevector-length src))) 1289 (cond 1290 ((>= pos n) 1291 (die (%loc file start-line start-col) 1292 "unterminated /* block comment")) 1293 (else 1294 (let ((b (bytevector-u8-ref src pos))) 1295 (cond 1296 ;; Fast path for plain content bytes that aren't '*'. 1297 ((and (%fast-byte? b) (not (= b 42))) 1298 (%skip-block-comment src (+ pos 1) line (+ col 1) 1299 file start-line start-col)) 1300 (else 1301 ;; Slow path: '*', '\n', '?' (trigraph), '\\' (splice). 1302 (let* ((p (%lex-peek src pos line col)) 1303 (b1 (%pk-byte p))) 1304 (cond 1305 ((not b1) 1306 (die (%loc file start-line start-col) 1307 "unterminated /* block comment")) 1308 ((= b1 42) 1309 (let* ((q (%lex-peek src (%pk-pos p) (%pk-line p) (%pk-col p))) 1310 (b2 (%pk-byte q))) 1311 (cond 1312 ((not b2) 1313 (die (%loc file start-line start-col) 1314 "unterminated /* block comment")) 1315 ((= b2 47) 1316 (%skip-ws-and-comments src (%pk-pos q) (%pk-line q) (%pk-col q) 1317 file)) 1318 (else 1319 ;; Re-scan starting at the byte after '*'; the '*' was 1320 ;; not the closer, but the next byte might itself be '*'. 1321 (%skip-block-comment src (%pk-pos p) (%pk-line p) (%pk-col p) 1322 file start-line start-col))))) 1323 (else 1324 (%skip-block-comment src (%pk-pos p) (%pk-line p) (%pk-col p) 1325 file start-line start-col))))))))))) 1326 1327 ;; -------------------------------------------------------------------- 1328 ;; Byte-run scanners. 1329 ;; 1330 ;; Tail-recursive walkers used by ident/number/string readers. None 1331 ;; allocate per scanned byte on the fast path (only %lex-peek 4-lists 1332 ;; on trigraph/splice/newline); tail recursion keeps the native call path 1333 ;; bounded and unreachable Scheme environments are collected normally. 1334 ;; 1335 ;; - %scan-while: count bytes that satisfy pred. (count npos nline ncol) 1336 ;; - %fill-while-bv: write matching bytes into a pre-sized bv. 1337 ;; - %accum-int-while: accumulate a base-N integer over digit bytes. 1338 ;; (val count npos nline ncol) 1339 ;; - %accum-octal-bounded: same, but stops after k digits. 1340 ;; -------------------------------------------------------------------- 1341 (define (%scan-while pred src pos line col) 1342 (let ((n (bytevector-length src))) 1343 (let loop ((pos pos) (line line) (col col) (cnt 0)) 1344 (cond 1345 ((>= pos n) (list cnt pos line col)) 1346 (else 1347 (let ((b (bytevector-u8-ref src pos))) 1348 (cond 1349 ((%fast-byte? b) 1350 (if (pred b) 1351 (loop (+ pos 1) line (+ col 1) (+ cnt 1)) 1352 (list cnt pos line col))) 1353 (else 1354 (let* ((p (%lex-peek src pos line col)) 1355 (b2 (%pk-byte p))) 1356 (if (and b2 (pred b2)) 1357 (loop (%pk-pos p) (%pk-line p) (%pk-col p) (+ cnt 1)) 1358 (list cnt pos line col))))))))))) 1359 1360 (define (%fill-while-bv pred src pos line col bv idx) 1361 (let ((n (bytevector-length src))) 1362 (let loop ((pos pos) (line line) (col col) (idx idx)) 1363 (cond 1364 ((>= pos n) idx) 1365 (else 1366 (let ((b (bytevector-u8-ref src pos))) 1367 (cond 1368 ((%fast-byte? b) 1369 (cond 1370 ((pred b) 1371 (bytevector-u8-set! bv idx b) 1372 (loop (+ pos 1) line (+ col 1) (+ idx 1))) 1373 (else idx))) 1374 (else 1375 (let* ((p (%lex-peek src pos line col)) 1376 (b2 (%pk-byte p))) 1377 (cond 1378 ((and b2 (pred b2)) 1379 (bytevector-u8-set! bv idx b2) 1380 (loop (%pk-pos p) (%pk-line p) (%pk-col p) (+ idx 1))) 1381 (else idx))))))))))) 1382 1383 (define (%digit-val-byte b) 1384 ;; ASCII digit byte → integer value. Caller guarantees b is a valid 1385 ;; digit in the relevant base (0-9 / 0-7 / 0-9a-fA-F). 1386 (cond ((%digit? b) (- b 48)) 1387 ((if (< b 65) #f (if (< 70 b) #f #t)) (+ (- b 65) 10)) 1388 ((if (< b 97) #f (if (< 102 b) #f #t)) (+ (- b 97) 10)) 1389 (else 0))) 1390 1391 (define (%accum-int-while pred src pos line col base) 1392 (let ((n (bytevector-length src))) 1393 (let loop ((pos pos) (line line) (col col) (val 0) (cnt 0)) 1394 (cond 1395 ((>= pos n) (list val cnt pos line col)) 1396 (else 1397 (let ((b (bytevector-u8-ref src pos))) 1398 (cond 1399 ((%fast-byte? b) 1400 (if (pred b) 1401 (loop (+ pos 1) line (+ col 1) 1402 (%c-value-mul-small-add 1403 val base (%digit-val-byte b)) 1404 (+ cnt 1)) 1405 (list val cnt pos line col))) 1406 (else 1407 (let* ((p (%lex-peek src pos line col)) 1408 (b2 (%pk-byte p))) 1409 (if (and b2 (pred b2)) 1410 (loop (%pk-pos p) (%pk-line p) (%pk-col p) 1411 (%c-value-mul-small-add 1412 val base (%digit-val-byte b2)) 1413 (+ cnt 1)) 1414 (list val cnt pos line col))))))))))) 1415 1416 (define (%accum-octal-bounded src pos line col k) 1417 ;; Up to k octal digits. Returns (val count npos nline ncol). 1418 (let ((n (bytevector-length src))) 1419 (let loop ((pos pos) (line line) (col col) (k k) (val 0) (cnt 0)) 1420 (cond 1421 ((zero? k) (list val cnt pos line col)) 1422 ((>= pos n) (list val cnt pos line col)) 1423 (else 1424 (let ((b (bytevector-u8-ref src pos))) 1425 (cond 1426 ((%fast-byte? b) 1427 (if (%octal? b) 1428 (loop (+ pos 1) line (+ col 1) (- k 1) 1429 (+ (* val 8) (- b 48)) (+ cnt 1)) 1430 (list val cnt pos line col))) 1431 (else 1432 (let* ((p (%lex-peek src pos line col)) 1433 (b2 (%pk-byte p))) 1434 (if (and b2 (%octal? b2)) 1435 (loop (%pk-pos p) (%pk-line p) (%pk-col p) (- k 1) 1436 (+ (* val 8) (- b2 48)) (+ cnt 1)) 1437 (list val cnt pos line col))))))))))) 1438 1439 ;; -------------------------------------------------------------------- 1440 ;; Identifier / keyword reader. 1441 ;; 1442 ;; Returns (tok npos nline ncol). Caller has already verified that the 1443 ;; first byte at `pos` satisfies %ident-start?. 1444 ;; 1445 ;; Two-pass: pass 1 (%scan-while) sizes the run, then pass 2 1446 ;; (%fill-while-bv) writes directly into the exact-size bytevector. 1447 ;; -------------------------------------------------------------------- 1448 (define (lex-read-ident src pos file) 1449 ;; Public for tests. Threads line/col from a fresh start. 1450 (%lex-read-ident src pos 1 (+ pos 1) file)) 1451 1452 (define (%lex-read-ident src pos line col file) 1453 (let ((start-loc (%loc file line col)) 1454 (count 0) (npos 0) (nline 0) (ncol 0)) 1455 (let ((sres (%scan-while %ident-cont? src pos line col))) 1456 (set! count (car sres)) 1457 (set! npos (car (cdr sres))) 1458 (set! nline (car (cdr (cdr sres)))) 1459 (set! ncol (car (cdr (cdr (cdr sres)))))) 1460 (let ((name (make-bytevector count 0))) 1461 (%fill-while-bv %ident-cont? src pos line col name 0) 1462 (let ((kw (%hash-ref %keyword-map name))) 1463 (cons (if kw 1464 (make-tok 'KW kw start-loc) 1465 (make-tok 'IDENT name start-loc)) 1466 (list npos nline ncol)))))) 1467 1468 ;; -------------------------------------------------------------------- 1469 ;; Number reader. 1470 ;; 1471 ;; Decimal: [1-9][0-9]* (suffix: u U l L ll LL combinations) 1472 ;; Hex: 0x[0-9a-fA-F]+ | 0X... 1473 ;; Octal: 0[0-7]* 1474 ;; Float: anything looking like 1.0, 1e3, .5 → die crisply. 1475 ;; 1476 ;; Returns (tok npos nline ncol) on success. Aborts via `die` on float. 1477 ;; 1478 ;; %accum-int-while folds digit collection and value computation into 1479 ;; one walk — no per-byte cons cells, no separate digits-list pass. 1480 ;; -------------------------------------------------------------------- 1481 (define (lex-read-number src pos file) 1482 (%lex-read-number src pos 1 (+ pos 1) file)) 1483 1484 (define (%lex-finish-int val start-loc decimal? after) 1485 ;; AFTER carries the scanner position followed by the parsed suffix flags. 1486 ;; Keep only the position in the public lexer result; retain the flags on 1487 ;; the INT token so the parser can apply the target C data model. 1488 (let ((npos (car after)) 1489 (nline (car (cdr after))) 1490 (ncol (car (cdr (cdr after)))) 1491 (unsigned? (car (cdr (cdr (cdr after))))) 1492 (long-count (car (cdr (cdr (cdr (cdr after))))))) 1493 (cons (make-tok 'INT 1494 (%c-int-lit val unsigned? long-count decimal?) 1495 start-loc) 1496 (list npos nline ncol)))) 1497 1498 (define (%lex-read-number src pos line col file) 1499 (let* ((start-loc (%loc file line col)) 1500 (p (%lex-peek src pos line col)) 1501 (b (%pk-byte p))) 1502 (cond 1503 ;; '0x' / '0X' hex prefix 1504 ((and (= b 48) 1505 (let* ((q (%lex-peek src (%pk-pos p) (%pk-line p) (%pk-col p))) 1506 (b2 (%pk-byte q))) 1507 (and b2 (or (= b2 120) (= b2 88))))) ; 'x' or 'X' 1508 (let* ((q (%lex-peek src (%pk-pos p) (%pk-line p) (%pk-col p))) 1509 (r (%accum-int-while %hex? src 1510 (%pk-pos q) (%pk-line q) (%pk-col q) 16)) 1511 (val (car r)) 1512 (cnt (car (cdr r))) 1513 (pos2 (car (cdr (cdr r)))) 1514 (line2 (car (cdr (cdr (cdr r))))) 1515 (col2 (car (cdr (cdr (cdr (cdr r))))))) 1516 (if (zero? cnt) 1517 (die start-loc "expected hex digits after 0x") 1518 (let ((after (%lex-strip-int-suffix src pos2 line2 col2 file))) 1519 (%lex-finish-int val start-loc #f after))))) 1520 ;; '0' alone → octal sequence (could be just zero) 1521 ((= b 48) 1522 (let* ((r (%accum-int-while %octal? src 1523 (%pk-pos p) (%pk-line p) (%pk-col p) 8)) 1524 (val (car r)) 1525 (pos2 (car (cdr (cdr r)))) 1526 (line2 (car (cdr (cdr (cdr r))))) 1527 (col2 (car (cdr (cdr (cdr (cdr r))))))) 1528 ;; Reject '.' / 'e' / 'E' immediately after the octal run — float. 1529 (%check-no-float src pos2 line2 col2 file start-loc) 1530 ;; Reject stray digits 8/9 in an octal context (e.g. 089). 1531 (let* ((p3 (%lex-peek src pos2 line2 col2)) 1532 (b3 (%pk-byte p3))) 1533 (if (and b3 (%digit? b3)) 1534 (die start-loc "invalid octal digit" (bv-of-byte b3)) 1535 (let ((after (%lex-strip-int-suffix src pos2 line2 col2 file))) 1536 (%lex-finish-int val start-loc #f after)))))) 1537 ;; '1'-'9' → decimal 1538 ((%digit? b) 1539 (let* ((r (%accum-int-while %digit? src pos line col 10)) 1540 (val (car r)) 1541 (pos2 (car (cdr (cdr r)))) 1542 (line2 (car (cdr (cdr (cdr r))))) 1543 (col2 (car (cdr (cdr (cdr (cdr r))))))) 1544 (%check-no-float src pos2 line2 col2 file start-loc) 1545 (let ((after (%lex-strip-int-suffix src pos2 line2 col2 file))) 1546 (%lex-finish-int val start-loc #t after)))) 1547 ;; '.' followed by a digit = float-style literal — reject. 1548 ((= b 46) 1549 (let* ((q (%lex-peek src (%pk-pos p) (%pk-line p) (%pk-col p))) 1550 (b2 (%pk-byte q))) 1551 (if (and b2 (%digit? b2)) 1552 (die start-loc "floating-point literal not supported") 1553 ;; Otherwise '.' was a punctuator — caller wouldn't have 1554 ;; routed here unless it was a digit-led prefix. 1555 (die start-loc "internal: number reader on non-number")))) 1556 (else 1557 (die start-loc "internal: number reader on non-number"))))) 1558 1559 (define (%check-no-float src pos line col file start-loc) 1560 ;; If the byte at pos starts a fractional/exponent part, abort. 1561 (let* ((p (%lex-peek src pos line col)) 1562 (b (%pk-byte p))) 1563 (cond 1564 ((not b) #t) 1565 ((= b 46) ; '.' 1566 (die start-loc "floating-point literal not supported")) 1567 ((or (= b 101) (= b 69)) ; 'e' / 'E' 1568 ;; Only a float exponent if followed by [+-]?digit. 1569 (let* ((q (%lex-peek src (%pk-pos p) (%pk-line p) (%pk-col p))) 1570 (b2 (%pk-byte q))) 1571 (cond 1572 ((and b2 (%digit? b2)) 1573 (die start-loc "floating-point literal not supported")) 1574 ((and b2 (or (= b2 43) (= b2 45))) 1575 (let* ((r (%lex-peek src (%pk-pos q) (%pk-line q) (%pk-col q))) 1576 (b3 (%pk-byte r))) 1577 (if (and b3 (%digit? b3)) 1578 (die start-loc "floating-point literal not supported") 1579 #t))) 1580 (else #t)))) 1581 (else #t)))) 1582 1583 (define (%lex-strip-int-suffix src pos line col file) 1584 ;; Consume any combination of u U l L (the long can be doubled). We 1585 ;; don't validate orderings strictly; tcc.c uses the canonical forms. 1586 ;; Returns (npos nline ncol unsigned? long-count). 1587 (let loop ((pos pos) (line line) (col col) (unsigned? #f) (long-count 0)) 1588 (let* ((p (%lex-peek src pos line col)) 1589 (b (%pk-byte p))) 1590 (cond 1591 ((not b) (list pos line col unsigned? long-count)) 1592 ((or (= b 117) (= b 85)) ; u U 1593 (loop (%pk-pos p) (%pk-line p) (%pk-col p) #t long-count)) 1594 ((or (= b 108) (= b 76)) ; l L 1595 (loop (%pk-pos p) (%pk-line p) (%pk-col p) 1596 unsigned? (+ long-count 1))) 1597 (else (list pos line col unsigned? long-count)))))) 1598 1599 ;; -------------------------------------------------------------------- 1600 ;; Escape sequence reader. 1601 ;; 1602 ;; %scan-or-fill-escape decodes one escape sequence starting at `pos` 1603 ;; (which points one past the leading `\\`). When `bv` is a bytevector, 1604 ;; the resulting byte is written to (bv idx); when it is #f, no write 1605 ;; occurs (used during the string-pass scan phase). Returns the 4-list 1606 ;; (val npos nline ncol). 1607 ;; -------------------------------------------------------------------- 1608 (define (%scan-or-fill-escape src pos line col file start-loc bv idx) 1609 (let* ((p (%lex-peek src pos line col)) 1610 (b (%pk-byte p))) 1611 (cond 1612 ((not b) (die start-loc "unterminated escape sequence")) 1613 ;; \xNN — 1+ hex digits (tcc.c uses 1- and 2-digit forms). 1614 ((or (= b 120) (= b 88)) ; 'x' / 'X' 1615 (let* ((r (%accum-int-while %hex? src 1616 (%pk-pos p) (%pk-line p) (%pk-col p) 16)) 1617 (val0 (car r)) 1618 (cnt (car (cdr r))) 1619 (pos2 (car (cdr (cdr r)))) 1620 (line2 (car (cdr (cdr (cdr r))))) 1621 (col2 (car (cdr (cdr (cdr (cdr r))))))) 1622 (cond 1623 ((zero? cnt) (die start-loc "expected hex digits after \\x")) 1624 (else 1625 (let ((val (bit-and val0 255))) 1626 (cond (bv (bytevector-u8-set! bv idx val)) 1627 (else #f)) 1628 (list val pos2 line2 col2)))))) 1629 ;; \NNN — 1..3 octal digits. 1630 ((%octal? b) 1631 (let* ((r (%accum-octal-bounded src pos line col 3)) 1632 (val0 (car r)) 1633 (pos2 (car (cdr (cdr r)))) 1634 (line2 (car (cdr (cdr (cdr r))))) 1635 (col2 (car (cdr (cdr (cdr (cdr r)))))) 1636 (val (bit-and val0 255))) 1637 (cond (bv (bytevector-u8-set! bv idx val)) 1638 (else #f)) 1639 (list val pos2 line2 col2))) 1640 (else 1641 (let ((val (cond ((= b 110) 10) ; n 1642 ((= b 116) 9) ; t 1643 ((= b 114) 13) ; r 1644 ((= b 92) 92) ; \\ 1645 ((= b 39) 39) ; ' 1646 ((= b 34) 34) ; " 1647 ((= b 48) 0) ; 0 (already handled by octal but be safe) 1648 ((= b 97) 7) ; \a -> BEL 1649 ((= b 98) 8) ; \b 1650 ((= b 102) 12) ; \f 1651 ((= b 118) 11) ; \v 1652 ((= b 63) 63) ; \? 1653 (else 1654 (die start-loc "unknown escape" (bv-of-byte b)))))) 1655 (cond (bv (bytevector-u8-set! bv idx val)) 1656 (else #f)) 1657 (list val (%pk-pos p) (%pk-line p) (%pk-col p))))))) 1658 1659 ;; -------------------------------------------------------------------- 1660 ;; String reader. 1661 ;; 1662 ;; Caller has verified src[pos] == '"' (raw byte 34). Returns 1663 ;; (tok npos nline ncol) with the raw decoded bytes (no NUL appended). 1664 ;; 1665 ;; Two-pass: %string-pass with bv=#f counts effective bytes (escapes 1666 ;; collapse to 1 byte each); then allocate the final bv and rerun with 1667 ;; bv set so the bytes are written directly into it. 1668 ;; -------------------------------------------------------------------- 1669 (define (lex-read-string src pos file) 1670 (%lex-read-string src pos 1 (+ pos 1) file)) 1671 1672 (define (%lex-read-string src pos line col file) 1673 (let ((start-loc (%loc file line col)) 1674 (cnt 0) (npos 0) (nline 0) (ncol 0)) 1675 ;; '"' (34) is a fast-byte and never a trigraph result, so the 1676 ;; physical byte at `pos` is exactly the opening quote. 1677 (cond 1678 ((or (>= pos (bytevector-length src)) 1679 (not (= (bytevector-u8-ref src pos) 34))) 1680 (die start-loc "internal: string reader on non-quote")) 1681 (else 1682 (let ((sres (%string-pass src (+ pos 1) line (+ col 1) 1683 file start-loc #f))) 1684 (set! cnt (car sres)) 1685 (set! npos (car (cdr sres))) 1686 (set! nline (car (cdr (cdr sres)))) 1687 (set! ncol (car (cdr (cdr (cdr sres)))))) 1688 (let ((bv (make-bytevector cnt 0))) 1689 (%string-pass src (+ pos 1) line (+ col 1) file start-loc bv) 1690 (cons (make-tok 'STR bv start-loc) 1691 (list npos nline ncol))))))) 1692 1693 (define (%string-pass src pos line col file start-loc bv) 1694 ;; Walk the string body (after opening "). When `bv` is #f, count 1695 ;; effective bytes; when it is a bytevector, write bytes into it at 1696 ;; index 0..count-1. Returns (count npos nline ncol). 1697 (let ((n (bytevector-length src))) 1698 (let loop ((pos pos) (line line) (col col) (idx 0)) 1699 (cond 1700 ((>= pos n) (die start-loc "unterminated string literal")) 1701 (else 1702 (let ((b (bytevector-u8-ref src pos))) 1703 (cond 1704 ;; Closing quote — fast byte but special. 1705 ((= b 34) 1706 (list idx (+ pos 1) line (+ col 1))) 1707 ((%fast-byte? b) 1708 (cond (bv (bytevector-u8-set! bv idx b)) 1709 (else #f)) 1710 (loop (+ pos 1) line (+ col 1) (+ idx 1))) 1711 (else 1712 ;; Slow path: ?/\ (trigraph/splice/escape) or '\n'. 1713 (let* ((p (%lex-peek src pos line col)) 1714 (b2 (%pk-byte p))) 1715 (cond 1716 ((not b2) 1717 (die start-loc "unterminated string literal")) 1718 ((= b2 34) 1719 (list idx (%pk-pos p) (%pk-line p) (%pk-col p))) 1720 ((%newline? b2) 1721 (die start-loc "newline in string literal")) 1722 ((= b2 92) 1723 (let* ((er (%scan-or-fill-escape 1724 src (%pk-pos p) (%pk-line p) (%pk-col p) 1725 file start-loc bv idx)) 1726 (epos (car (cdr er))) 1727 (eline (car (cdr (cdr er)))) 1728 (ecol (car (cdr (cdr (cdr er)))))) 1729 (loop epos eline ecol (+ idx 1)))) 1730 (else 1731 (cond (bv (bytevector-u8-set! bv idx b2)) 1732 (else #f)) 1733 (loop (%pk-pos p) (%pk-line p) (%pk-col p) (+ idx 1))))))))))))) 1734 1735 ;; -------------------------------------------------------------------- 1736 ;; Char reader. 1737 ;; 1738 ;; Caller has verified src[pos] == '\''. Multi-character constants 1739 ;; ('AB') are rejected via die. 1740 ;; -------------------------------------------------------------------- 1741 (define (lex-read-char src pos file) 1742 (%lex-read-char src pos 1 (+ pos 1) file)) 1743 1744 (define (%lex-read-char src pos line col file) 1745 (let* ((start-loc (%loc file line col)) 1746 (p0 (%lex-peek src pos line col)) 1747 (b0 (%pk-byte p0))) 1748 (if (not (and b0 (= b0 39))) 1749 (die start-loc "internal: char reader on non-quote") 1750 (%collect-char src (%pk-pos p0) (%pk-line p0) (%pk-col p0) 1751 file start-loc)))) 1752 1753 (define (%collect-char src pos line col file start-loc) 1754 ;; Read exactly one byte (handling escapes), then expect closing '\''. 1755 (let* ((p (%lex-peek src pos line col)) 1756 (b (%pk-byte p))) 1757 (cond 1758 ((not b) (die start-loc "unterminated char literal")) 1759 ((= b 39) (die start-loc "empty char literal")) 1760 ((%newline? b) (die start-loc "newline in char literal")) 1761 ((= b 92) ; escape 1762 (let* ((r (%scan-or-fill-escape src 1763 (%pk-pos p) (%pk-line p) (%pk-col p) 1764 file start-loc #f 0)) 1765 (val (car r)) 1766 (pos2 (car (cdr r))) 1767 (line2 (car (cdr (cdr r)))) 1768 (col2 (car (cdr (cdr (cdr r)))))) 1769 (%expect-char-close src pos2 line2 col2 file start-loc val))) 1770 (else 1771 (%expect-char-close src (%pk-pos p) (%pk-line p) (%pk-col p) 1772 file start-loc b))))) 1773 1774 (define (%expect-char-close src pos line col file start-loc val) 1775 (let* ((p (%lex-peek src pos line col)) 1776 (b (%pk-byte p))) 1777 (cond 1778 ((not b) (die start-loc "unterminated char literal")) 1779 ((= b 39) 1780 (cons (make-tok 'CHAR val start-loc) 1781 (list (%pk-pos p) (%pk-line p) (%pk-col p)))) 1782 (else 1783 (die start-loc "multi-character char constant not supported"))))) 1784 1785 ;; -------------------------------------------------------------------- 1786 ;; Punctuator reader. 1787 ;; 1788 ;; Greedy longest-match against %punct-alist. The alist 1789 ;; is already ordered longest-first. We additionally bucket entries by 1790 ;; their first byte so %lex-read-punct only loops over the small set of 1791 ;; patterns that can start at the current source byte. 1792 ;; -------------------------------------------------------------------- 1793 1794 (define (%alist-ref-int k al) 1795 ;; Lookup in an int-keyed alist (linear scan, '= compare). 1796 (cond ((null? al) #f) 1797 ((= (car (car al)) k) (cdr (car al))) 1798 (else (%alist-ref-int k (cdr al))))) 1799 1800 (define (%mem-int? k xs) 1801 (cond ((null? xs) #f) 1802 ((= (car xs) k) #t) 1803 (else (%mem-int? k (cdr xs))))) 1804 1805 (define (%filter-by-first-byte b al) 1806 ;; Subset of `al` whose pattern starts with byte b, preserving order. 1807 (cond 1808 ((null? al) '()) 1809 ((= (bytevector-u8-ref (car (car al)) 0) b) 1810 (cons (car al) (%filter-by-first-byte b (cdr al)))) 1811 (else (%filter-by-first-byte b (cdr al))))) 1812 1813 (define (%group-by-first-byte al) 1814 ;; Build ((first-byte . sub-alist) ...) over `al`, one bucket per 1815 ;; distinct first byte; sub-alist preserves longest-match-first 1816 ;; order from the source list. 1817 (let loop ((xs al) (seen '()) (out '())) 1818 (cond 1819 ((null? xs) (reverse out)) 1820 (else 1821 (let* ((entry (car xs)) 1822 (pat (car entry)) 1823 (b (bytevector-u8-ref pat 0))) 1824 (cond 1825 ((%mem-int? b seen) (loop (cdr xs) seen out)) 1826 (else 1827 (loop (cdr xs) 1828 (cons b seen) 1829 (cons (cons b (%filter-by-first-byte b al)) out))))))))) 1830 1831 (define %punct-buckets (%group-by-first-byte %punct-alist)) 1832 1833 (define (lex-read-punct src pos file) 1834 (%lex-read-punct src pos 1 (+ pos 1) file)) 1835 1836 (define (%lex-read-punct src pos line col file) 1837 (let* ((start-loc (%loc file line col)) 1838 (p (%lex-peek src pos line col)) 1839 (b (%pk-byte p))) 1840 (cond 1841 ((not b) (die start-loc "unrecognized byte" "EOF")) 1842 (else 1843 (let ((bucket (%alist-ref-int b %punct-buckets))) 1844 (cond 1845 ((not bucket) (die start-loc "unrecognized byte" (bv-of-byte b))) 1846 (else (%punct-loop src pos line col file start-loc bucket)))))))) 1847 1848 (define (%punct-loop src pos line col file start-loc al) 1849 (cond 1850 ((null? al) 1851 (let* ((p (%lex-peek src pos line col))) 1852 (die start-loc "unrecognized byte" 1853 (if (%pk-byte p) (bv-of-byte (%pk-byte p)) "EOF")))) 1854 (else 1855 (let* ((entry (car al)) 1856 (pat (car entry)) 1857 (sym (cdr entry)) 1858 (m (%match-bytes src pos line col pat 0))) 1859 (if m 1860 (cons (make-tok 'PUNCT sym start-loc) m) 1861 (%punct-loop src pos line col file start-loc (cdr al))))))) 1862 1863 (define (%match-bytes src pos line col pat i) 1864 ;; If the next bytes from (pos line col), in logical-byte stream 1865 ;; order, equal `pat[i..]`, return (npos nline ncol) after the 1866 ;; match. Otherwise #f. 1867 (cond 1868 ((= i (bytevector-length pat)) (list pos line col)) 1869 (else 1870 (let ((n (bytevector-length src))) 1871 (cond 1872 ((>= pos n) #f) 1873 (else 1874 (let ((b (bytevector-u8-ref src pos)) 1875 (pb (bytevector-u8-ref pat i))) 1876 (cond 1877 ((%fast-byte? b) 1878 (if (= b pb) 1879 (%match-bytes src (+ pos 1) line (+ col 1) pat (+ i 1)) 1880 #f)) 1881 (else 1882 (let* ((p (%lex-peek src pos line col)) 1883 (b2 (%pk-byte p))) 1884 (cond 1885 ((not b2) #f) 1886 ((= b2 pb) 1887 (%match-bytes src (%pk-pos p) (%pk-line p) (%pk-col p) 1888 pat (+ i 1))) 1889 (else #f)))))))))))) 1890 1891 ;; -------------------------------------------------------------------- 1892 ;; tok-iter — streaming token source. 1893 ;; -------------------------------------------------------------------- 1894 ;; Each pipeline layer (lex, pp, parser) wraps the layer below as a 1895 ;; tok-iter. iter-next pulls one token at a time. iter-peek/iter-peek2 1896 ;; cache lookahead in `buf`. iter-unget! pushes back. Live-data bound is 1897 ;; lookahead (≤2) + per-layer state, not source length. 1898 ;; 1899 ;; Pull-fns must keep yielding EOF after the first EOF (idempotent). 1900 (define-record-type tok-iter 1901 (%tok-iter pull-fn state buf) 1902 tok-iter? 1903 (pull-fn tok-iter-pull-fn) 1904 (state tok-iter-state) 1905 (buf tok-iter-buf tok-iter-buf-set!)) 1906 1907 (define (iter-next it) 1908 (let ((b (tok-iter-buf it))) 1909 (cond 1910 ((null? b) ((tok-iter-pull-fn it) (tok-iter-state it))) 1911 (else 1912 (tok-iter-buf-set! it (cdr b)) 1913 (car b))))) 1914 1915 (define (iter-peek it) 1916 (let ((b (tok-iter-buf it))) 1917 (cond 1918 ((null? b) 1919 (let ((t ((tok-iter-pull-fn it) (tok-iter-state it)))) 1920 (tok-iter-buf-set! it (list t)) 1921 t)) 1922 (else (car b))))) 1923 1924 (define (iter-peek2 it) 1925 (let ((b (tok-iter-buf it))) 1926 (cond 1927 ((null? b) 1928 (let* ((t1 ((tok-iter-pull-fn it) (tok-iter-state it))) 1929 (t2 ((tok-iter-pull-fn it) (tok-iter-state it)))) 1930 (tok-iter-buf-set! it (list t1 t2)) 1931 t2)) 1932 ((null? (cdr b)) 1933 (let ((t2 ((tok-iter-pull-fn it) (tok-iter-state it)))) 1934 (tok-iter-buf-set! it (cons (car b) (list t2))) 1935 t2)) 1936 (else (car (cdr b)))))) 1937 1938 (define (iter-unget! it t) 1939 (tok-iter-buf-set! it (cons t (tok-iter-buf it)))) 1940 1941 ;; Drain an iter to a list ending in EOF. Used by lex-tokenize / 1942 ;; pp-expand so the cc-lex / cc-pp test runners can inspect the 1943 ;; materialized stream. 1944 (define (iter->list it) 1945 (let loop ((acc '())) 1946 (let ((t (iter-next it))) 1947 (cond 1948 ((eq? (tok-kind t) 'EOF) (reverse (cons t acc))) 1949 (else (loop (cons t acc))))))) 1950 1951 ;; -------------------------------------------------------------------- 1952 ;; list-iter — wrap an existing token list as a tok-iter. Yields each 1953 ;; tok in turn; once exhausted, keeps yielding EOF (idempotent). The 1954 ;; wrapped list typically already ends in EOF. 1955 ;; -------------------------------------------------------------------- 1956 (define-record-type list-iter-state 1957 (%list-iter-state toks) 1958 list-iter-state? 1959 (toks lis-toks lis-toks-set!)) 1960 1961 (define (make-list-iter toks) 1962 (%tok-iter %list-iter-pull (%list-iter-state toks) '())) 1963 1964 (define (%list-iter-pull st) 1965 (let ((toks (lis-toks st))) 1966 (cond 1967 ((null? toks) (make-tok 'EOF #f #f)) 1968 (else 1969 (lis-toks-set! st (cdr toks)) 1970 (car toks))))) 1971 1972 ;; -------------------------------------------------------------------- 1973 ;; lex-iter — streaming lexer. Steady state: pos/line/col + bol? in 1974 ;; lex-state; discarded per-token allocation is reclaimed by the GC. 1975 ;; -------------------------------------------------------------------- 1976 ;; bol? — `#t` when no token has been emitted on the current physical 1977 ;; line yet (start of file, or only NL + whitespace seen since the last 1978 ;; line break). pp recognizes a directive only when its leading `#` is 1979 ;; at line-start; we forward that decision into the token stream by 1980 ;; emitting `HASH` instead of `(PUNCT hash …)` for a line-leading `#`. 1981 (define-record-type lex-state 1982 (%lex-state src file pos line col bol? done?) 1983 lex-state? 1984 (src lex-state-src) 1985 (file lex-state-file) 1986 (pos lex-state-pos lex-state-pos-set!) 1987 (line lex-state-line lex-state-line-set!) 1988 (col lex-state-col lex-state-col-set!) 1989 (bol? lex-state-bol? lex-state-bol?-set!) 1990 (done? lex-state-done? lex-state-done?-set!)) 1991 1992 (define (make-lex-iter src file) 1993 (%lex-init!) 1994 (%tok-iter %lex-iter-pull 1995 (%lex-state src file 0 1 1 #t #f) 1996 '())) 1997 1998 (define (%lex-iter-pull st) 1999 (cond 2000 ((lex-state-done? st) 2001 ;; Idempotent EOF: keep yielding EOF after the first one. 2002 (make-tok 'EOF #f (%loc (lex-state-file st) 2003 (lex-state-line st) 2004 (lex-state-col st)))) 2005 (else (%lex-iter-step st)))) 2006 2007 (define (%lex-iter-step st) 2008 (let ((src (lex-state-src st)) 2009 (file (lex-state-file st)) 2010 (pos (lex-state-pos st)) 2011 (line (lex-state-line st)) 2012 (col (lex-state-col st)) 2013 (bol? (lex-state-bol? st)) 2014 (kind #f) (val #f) 2015 (loc-line 1) (loc-col 1) 2016 (npos 0) (nline 1) (ncol 1) (nbol? #f)) 2017 (let* ((sw (%skip-ws-and-comments src pos line col file)) 2018 (pos1 (car sw)) 2019 (line1 (car (cdr sw))) 2020 (col1 (car (cdr (cdr sw)))) 2021 (p (%lex-peek src pos1 line1 col1)) 2022 (b (%pk-byte p))) 2023 (set! loc-line line1) 2024 (set! loc-col col1) 2025 (set! val #f) (set! nbol? #f) 2026 (cond 2027 ;; EOF 2028 ((not b) 2029 (set! kind 'EOF) 2030 (set! npos pos1) (set! nline line1) (set! ncol col1)) 2031 ;; Newline → NL token; next call starts at bol. 2032 ((%newline? b) 2033 (set! kind 'NL) 2034 (set! npos (%pk-pos p)) 2035 (set! nline (%pk-line p)) 2036 (set! ncol (%pk-col p)) 2037 (set! nbol? #t)) 2038 ;; Line-leading `#`: bare `#` becomes HASH; `##` falls 2039 ;; through to punctuator (lexes as `paste`). 2040 ((and bol? (= b 35)) 2041 (let* ((q (%lex-peek src (%pk-pos p) (%pk-line p) (%pk-col p))) 2042 (b2 (%pk-byte q))) 2043 (cond 2044 ((and b2 (= b2 35)) 2045 (let* ((r (%lex-read-punct src pos1 line1 col1 file)) 2046 (tok (car r)) (rest (cdr r))) 2047 (set! kind 'PUNCT) (set! val (tok-value tok)) 2048 (set! npos (car rest)) 2049 (set! nline (car (cdr rest))) 2050 (set! ncol (car (cdr (cdr rest)))))) 2051 (else 2052 (set! kind 'HASH) 2053 (set! npos (%pk-pos p)) 2054 (set! nline (%pk-line p)) 2055 (set! ncol (%pk-col p)))))) 2056 ;; Identifier / keyword 2057 ((%ident-start? b) 2058 (let* ((r (%lex-read-ident src pos1 line1 col1 file)) 2059 (tok (car r)) (rest (cdr r))) 2060 (set! kind (tok-kind tok)) 2061 (cond ((eq? (tok-kind tok) 'KW) 2062 (set! val (tok-value tok))) 2063 (else (set! val (tok-value tok)))) 2064 (set! npos (car rest)) 2065 (set! nline (car (cdr rest))) 2066 (set! ncol (car (cdr (cdr rest)))))) 2067 ;; Number (digit start) 2068 ((%digit? b) 2069 (let* ((r (%lex-read-number src pos1 line1 col1 file)) 2070 (tok (car r)) (rest (cdr r))) 2071 (set! kind 'INT) (set! val (tok-value tok)) 2072 (set! npos (car rest)) 2073 (set! nline (car (cdr rest))) 2074 (set! ncol (car (cdr (cdr rest)))))) 2075 ;; '.' is a punctuator unless followed by a digit (float). 2076 ((= b 46) 2077 (let* ((q (%lex-peek src (%pk-pos p) (%pk-line p) (%pk-col p))) 2078 (b2 (%pk-byte q))) 2079 (cond 2080 ((and b2 (%digit? b2)) 2081 (die (%loc file line1 col1) 2082 "floating-point literal not supported")) 2083 (else 2084 (let* ((r (%lex-read-punct src pos1 line1 col1 file)) 2085 (tok (car r)) (rest (cdr r))) 2086 (set! kind 'PUNCT) (set! val (tok-value tok)) 2087 (set! npos (car rest)) 2088 (set! nline (car (cdr rest))) 2089 (set! ncol (car (cdr (cdr rest))))))))) 2090 ;; String 2091 ((= b 34) 2092 (let* ((r (%lex-read-string src pos1 line1 col1 file)) 2093 (tok (car r)) (rest (cdr r)) 2094 (bv (tok-value tok))) 2095 (set! kind 'STR) 2096 (set! val bv) 2097 (set! npos (car rest)) 2098 (set! nline (car (cdr rest))) 2099 (set! ncol (car (cdr (cdr rest)))))) 2100 ;; Char 2101 ((= b 39) 2102 (let* ((r (%lex-read-char src pos1 line1 col1 file)) 2103 (tok (car r)) (rest (cdr r))) 2104 (set! kind 'CHAR) (set! val (tok-value tok)) 2105 (set! npos (car rest)) 2106 (set! nline (car (cdr rest))) 2107 (set! ncol (car (cdr (cdr rest)))))) 2108 ;; Punctuator (default) 2109 (else 2110 (let* ((r (%lex-read-punct src pos1 line1 col1 file)) 2111 (tok (car r)) (rest (cdr r))) 2112 ;; Line-leading `%:` digraph also acts as HASH for directives. 2113 (cond 2114 ((and bol? (eq? (tok-value tok) 'hash)) 2115 (set! kind 'HASH)) 2116 (else 2117 (set! kind 'PUNCT) (set! val (tok-value tok)))) 2118 (set! npos (car rest)) 2119 (set! nline (car (cdr rest))) 2120 (set! ncol (car (cdr (cdr rest)))))))) 2121 ;; Advance the iterator state and return the token. 2122 (cond 2123 ((eq? kind 'EOF) 2124 (lex-state-done?-set! st #t) 2125 (make-tok 'EOF #f (%loc file loc-line loc-col))) 2126 (else 2127 (lex-state-pos-set! st npos) 2128 (lex-state-line-set! st nline) 2129 (lex-state-col-set! st ncol) 2130 (lex-state-bol?-set! st nbol?) 2131 (make-tok kind val (%loc file loc-line loc-col)))))) 2132 2133 ;; Drain a lex-iter into a list ending in EOF, for the cc-lex test 2134 ;; runner. Production callers chain make-lex-iter directly. 2135 (define (lex-tokenize src file) 2136 (iter->list (make-lex-iter src file))) 2137 ;; cc/pp.scm — preprocessor. Hide-set per C11 6.10.3.4. 2138 ;; #include rejected (CC.md §Toolchain envelope). 2139 2140 ;; --- helpers --- 2141 (define (%pp-bv-mem? x xs) 2142 (cond ((null? xs) #f) 2143 ((bv= x (car xs)) #t) 2144 (else (%pp-bv-mem? x (cdr xs))))) 2145 2146 (define (%pp-bv-union a b) 2147 (cond ((null? a) b) 2148 ((%pp-bv-mem? (car a) b) (%pp-bv-union (cdr a) b)) 2149 (else (cons (car a) (%pp-bv-union (cdr a) b))))) 2150 2151 (define (%pp-with-hide t hide) 2152 (%tok (tok-kind t) (tok-value t) (tok-loc t) hide)) 2153 (define (%pp-with-loc t loc) 2154 (%tok (tok-kind t) (tok-value t) loc (tok-hide t))) 2155 2156 ;; --- pp-state (private record) --- 2157 ;; cond-stack: list of (active? . has-taken?). Outer-active gating is 2158 ;; computed by walking the stack rather than encoding it in frames. 2159 ;; 2160 ;; Streaming fields drive make-pp-iter; the bounded-buffer path used 2161 ;; by pp-eval-cexpr leaves them at #f / '(). 2162 ;; lex-iter — upstream tok-iter, or #f 2163 ;; up-pending — toks unshifted upstream (macro-expansion bodies that 2164 ;; must be re-scanned for further expansion) 2165 ;; out-buf — toks already dispatched but stashed for the next pull 2166 ;; (peek-and-fuse for adjacent STRs lookahead overshoots 2167 ;; by one tok, which lands here) 2168 (define-record-type pp-state 2169 (%pp-state macros cond-stack cur-file line-delta lex-iter up-pending out-buf) 2170 pp-state? 2171 (macros pps-macros pps-macros-set!) 2172 (cond-stack pps-cond-stack pps-cond-stack-set!) 2173 (cur-file pps-cur-file pps-cur-file-set!) 2174 (line-delta pps-line-delta pps-line-delta-set!) 2175 (lex-iter pps-lex-iter) 2176 (up-pending pps-up-pending pps-up-pending-set!) 2177 (out-buf pps-out-buf pps-out-buf-set!)) 2178 2179 (define (%pp-make-state defs) (%pp-state defs '() #f 0 #f '() '())) 2180 2181 (define (%pp-active? state) 2182 (let loop ((xs (pps-cond-stack state))) 2183 (cond ((null? xs) #t) 2184 ((not (car (car xs))) #f) 2185 (else (loop (cdr xs)))))) 2186 2187 ;; Active for the *parent* of the top frame (used by elif/else). 2188 (define (%pp-parent-active? state) 2189 (let ((cs (pps-cond-stack state))) 2190 (cond ((null? cs) #t) 2191 (else 2192 (let loop ((xs (cdr cs))) 2193 (cond ((null? xs) #t) 2194 ((not (car (car xs))) #f) 2195 (else (loop (cdr xs))))))))) 2196 2197 ;; --- token classification --- 2198 (define (%pp-eof? t) (eq? (tok-kind t) 'EOF)) 2199 (define (%pp-nl? t) (eq? (tok-kind t) 'NL)) 2200 (define (%pp-hash? t) (eq? (tok-kind t) 'HASH)) 2201 (define (%pp-ident? t) (eq? (tok-kind t) 'IDENT)) 2202 (define (%pp-int? t) (eq? (tok-kind t) 'INT)) 2203 (define (%pp-punct? t pname) 2204 (and (eq? (tok-kind t) 'PUNCT) (eq? (tok-value t) pname))) 2205 (define (%pp-ident-name? t name-bv) 2206 (and (%pp-ident? t) (bv= (tok-value t) name-bv))) 2207 (define (%pp-skip-ws toks) toks) 2208 2209 ;; --- built-in macro names --- 2210 (define %pp-bv-FILE "__FILE__") 2211 (define %pp-bv-LINE "__LINE__") 2212 (define %pp-bv-STDC "__STDC__") 2213 (define %pp-bv-LISPCC "__LISPCC__") 2214 (define %pp-bv-DATE "__DATE__") 2215 (define %pp-bv-TIME "__TIME__") 2216 (define %pp-bv-STDC-VERSION "__STDC_VERSION__") 2217 (define %pp-bv-STDC-HOSTED "__STDC_HOSTED__") 2218 (define %pp-bv-VA-ARGS "__VA_ARGS__") 2219 (define %pp-bv-defined "defined") 2220 2221 ;; Fixed values for reproducibility — we don't read the wall clock. 2222 (define %pp-bv-DATE-VALUE "Jan 1 1970") 2223 (define %pp-bv-TIME-VALUE "00:00:00") 2224 2225 (define (%pp-builtin? name) 2226 (or (bv= name %pp-bv-FILE) (bv= name %pp-bv-LINE) 2227 (bv= name %pp-bv-STDC) (bv= name %pp-bv-LISPCC) 2228 (bv= name %pp-bv-DATE) (bv= name %pp-bv-TIME) 2229 (bv= name %pp-bv-STDC-VERSION) (bv= name %pp-bv-STDC-HOSTED))) 2230 2231 (define (%pp-expand-builtin name loc state) 2232 ;; Emit the token at the ORIGINAL loc; %pp-relocate downstream will 2233 ;; apply pps-cur-file / pps-line-delta. Doing the rewrite here too 2234 ;; (then letting relocate re-apply it) double-shifts __LINE__'s loc. 2235 ;; The VALUE of __LINE__ / __FILE__ already reflects the post-#line 2236 ;; mapping because we compute `file`/`line` from cur-file/line-delta. 2237 (let* ((file (or (pps-cur-file state) (loc-file loc))) 2238 (line (+ (loc-line loc) (pps-line-delta state)))) 2239 (cond 2240 ((bv= name %pp-bv-FILE) (list (%tok 'STR file loc '()))) 2241 ((bv= name %pp-bv-LINE) (list (%tok 'INT line loc '()))) 2242 ((bv= name %pp-bv-STDC) (list (%tok 'INT 1 loc '()))) 2243 ((bv= name %pp-bv-LISPCC) (list (%tok 'INT 1 loc '()))) 2244 ((bv= name %pp-bv-DATE) (list (%tok 'STR %pp-bv-DATE-VALUE loc '()))) 2245 ((bv= name %pp-bv-TIME) (list (%tok 'STR %pp-bv-TIME-VALUE loc '()))) 2246 ((bv= name %pp-bv-STDC-VERSION) (list (%tok 'INT 199901 loc '()))) 2247 ((bv= name %pp-bv-STDC-HOSTED) (list (%tok 'INT 1 loc '()))) 2248 (else (die loc "internal: not a builtin" name))))) 2249 2250 ;; --- buf-list: simple reversed-list buffer of toks --- 2251 (define-record-type buf-list 2252 (%buf-list xs) 2253 buf-list? 2254 (xs buf-list-xs buf-list-xs-set!)) 2255 (define (make-buf-list) (%buf-list '())) 2256 (define (buf-list-push! b t) (buf-list-xs-set! b (cons t (buf-list-xs b)))) 2257 (define (buf-list-push-many! b ts) 2258 (let loop ((ts ts)) 2259 (cond ((null? ts) #t) 2260 (else (buf-list-push! b (car ts)) (loop (cdr ts)))))) 2261 (define (buf-list-flush b) (reverse (buf-list-xs b))) 2262 2263 ;; --- make-pp-iter: streaming preprocessor --- 2264 ;; Wraps a lex-iter (or any tok-iter). Returns a tok-iter. Live data 2265 ;; bounded by parser state + lookahead, not source length. Adjacent-STR 2266 ;; fusion happens inline via peek-and-stash. 2267 (define (make-pp-iter src-iter initial-defines) 2268 (let ((st (%pp-state (alist->hash initial-defines) 2269 '() #f 0 src-iter '() '()))) 2270 (%tok-iter %pp-iter-pull st '()))) 2271 2272 (define (%pp-iter-pull st) 2273 (let ((ob (pps-out-buf st))) 2274 (cond 2275 ((not (null? ob)) 2276 (pps-out-buf-set! st (cdr ob)) 2277 (car ob)) 2278 (else (%pp-maybe-fuse-str st (%pp-dispatch-step st)))))) 2279 2280 ;; --- upstream helpers --- 2281 ;; Upstream tokens come either from up-pending (macro-expansion bodies 2282 ;; that need re-scanning) or from the wrapped lex-iter. 2283 (define (%pp-pull-upstream st) 2284 (let ((up (pps-up-pending st))) 2285 (cond 2286 ((not (null? up)) 2287 (pps-up-pending-set! st (cdr up)) 2288 (car up)) 2289 (else (iter-next (pps-lex-iter st)))))) 2290 2291 (define (%pp-peek-upstream st) 2292 (let ((up (pps-up-pending st))) 2293 (cond 2294 ((not (null? up)) (car up)) 2295 (else (iter-peek (pps-lex-iter st)))))) 2296 2297 ;; Push toks to the front of upstream so (car toks) is yielded next. 2298 (define (%pp-unshift-upstream! st toks) 2299 (pps-up-pending-set! st (append toks (pps-up-pending st)))) 2300 2301 ;; Collect tokens up to (not including) NL or EOF. NL is consumed; EOF 2302 ;; is unshifted back so the main loop sees it. 2303 (define (%pp-collect-line-stream st) 2304 (let loop ((acc '())) 2305 (let ((t (%pp-pull-upstream st))) 2306 (cond 2307 ((%pp-eof? t) 2308 (%pp-unshift-upstream! st (list t)) 2309 (reverse acc)) 2310 ((%pp-nl? t) (reverse acc)) 2311 (else (loop (cons t acc))))))) 2312 2313 ;; Streaming arg collection for fn-like macro calls. Position is just 2314 ;; AFTER the opening `(`. Returns the list of arg-tokenlists. 2315 (define (%pp-collect-args-stream st call-loc) 2316 (let loop ((depth 0) (cur '()) (args '())) 2317 (let ((t (%pp-pull-upstream st))) 2318 (cond 2319 ((%pp-eof? t) 2320 (die call-loc "macro call: unterminated argument list")) 2321 ((and (= depth 0) (%pp-punct? t 'rparen)) 2322 (cond 2323 ;; Empty parens count as one empty argument; bind-args 2324 ;; degenerates this back to "no args" for 0-param macros. 2325 ((and (null? args) (null? cur)) (list '())) 2326 (else (reverse (cons (reverse cur) args))))) 2327 ((and (= depth 0) (%pp-punct? t 'comma)) 2328 (loop 0 '() (cons (reverse cur) args))) 2329 ((%pp-punct? t 'lparen) 2330 (loop (+ depth 1) (cons t cur) args)) 2331 ((%pp-punct? t 'rparen) 2332 (loop (- depth 1) (cons t cur) args)) 2333 (else (loop depth (cons t cur) args)))))) 2334 2335 ;; Single dispatch step. Returns one post-pp tok (skipping NLs, 2336 ;; processing directives, expanding macros). Does NOT apply STR-fusion 2337 ;; — that happens one layer up in %pp-iter-pull, otherwise the 2338 ;; recursive lookahead during fusion would itself fuse further STRs 2339 ;; and drag tokens past the run into out-buf. 2340 (define (%pp-dispatch-step st) 2341 (let ((t (%pp-pull-upstream st))) 2342 (cond 2343 ((%pp-eof? t) 2344 (cond ((not (null? (pps-cond-stack st))) 2345 (die (tok-loc t) "unterminated #if/#ifdef/#ifndef")) 2346 (else t))) 2347 ((%pp-nl? t) (%pp-dispatch-step st)) 2348 ((%pp-hash? t) 2349 (let ((line (%pp-collect-line-stream st))) 2350 (%pp-dispatch-directive t line st #f) 2351 (%pp-dispatch-step st))) 2352 ((not (%pp-active? st)) 2353 (%pp-dispatch-step st)) 2354 ((%pp-ident? t) 2355 (let ((name (tok-value t))) 2356 (cond 2357 ((%pp-bv-mem? name (tok-hide t)) 2358 (%pp-relocate t st)) 2359 ((%pp-builtin? name) 2360 (let ((toks (%pp-expand-builtin name (tok-loc t) st))) 2361 (%pp-unshift-upstream! st toks) 2362 (%pp-dispatch-step st))) 2363 (else 2364 (let ((m (%hash-ref (pps-macros st) name))) 2365 (cond 2366 ((not m) (%pp-relocate t st)) 2367 ((eq? (macro-kind m) 'obj) 2368 (let ((body (%pp-prepare-body (macro-body m) 2369 (cons name (tok-hide t)) 2370 (tok-loc t)))) 2371 (%pp-unshift-upstream! st body) 2372 (%pp-dispatch-step st))) 2373 (else 2374 ;; fn-like or fn-vararg: peek upstream for `(`. If 2375 ;; not present, pass IDENT through unchanged (no 2376 ;; consumption); the next iter call will process the 2377 ;; following tok normally. 2378 (let ((next (%pp-peek-upstream st))) 2379 (cond 2380 ((not (%pp-punct? next 'lparen)) 2381 (%pp-relocate t st)) 2382 (else 2383 (%pp-pull-upstream st) ; consume `(` 2384 (let* ((args (%pp-collect-args-stream st (tok-loc t))) 2385 (params (macro-params m)) 2386 (variadic? (eq? (macro-kind m) 'fn-vararg)) 2387 (env (%pp-bind-args params args variadic? (tok-loc t))) 2388 (sub (%pp-substitute (macro-body m) env (tok-loc t) st)) 2389 (body (%pp-prepare-body sub 2390 (cons name (tok-hide t)) 2391 (tok-loc t)))) 2392 (%pp-unshift-upstream! st body) 2393 (%pp-dispatch-step st)))))))))))) 2394 (else (%pp-relocate t st))))) 2395 2396 ;; Translation phase 6 (peek-and-fuse). If `cur` is STR, look at the 2397 ;; next post-pp tok; if it's STR, fuse and repeat. Anything else gets 2398 ;; stashed in out-buf for the next iter-next call. Lookahead goes 2399 ;; through %pp-dispatch-step (no fusion), so a non-STR neighbor 2400 ;; correctly terminates the run. 2401 (define (%pp-maybe-fuse-str st cur) 2402 (cond 2403 ((not (eq? (tok-kind cur) 'STR)) cur) 2404 (else 2405 (let loop ((cur cur)) 2406 (let ((next (%pp-dispatch-step st))) 2407 (cond 2408 ((eq? (tok-kind next) 'STR) 2409 (loop (%tok 'STR 2410 (bytevector-append (tok-value cur) (tok-value next)) 2411 (tok-loc cur) 2412 (tok-hide cur)))) 2413 (else 2414 (pps-out-buf-set! st (cons next (pps-out-buf st))) 2415 cur))))))) 2416 2417 ;; Drain a pp-iter into a list ending in EOF, for the cc-pp test 2418 ;; runner. The input token list becomes the upstream via make-list-iter. 2419 ;; Production callers chain make-pp-iter directly over a make-lex-iter. 2420 (define (pp-expand toks initial-defines) 2421 (iter->list (make-pp-iter (make-list-iter toks) initial-defines))) 2422 2423 ;; --- directive dispatch --- 2424 ;; pmatch-based on the directive name bytes. Byte literals use the explicit 2425 ;; mixed string/bytevector rule from micro+boot2. 2426 ;; Directive name can arrive as IDENT (most cases) or KW (`if` and `else` 2427 ;; are C keywords promoted by lex; their KW symbol values map back to bv 2428 ;; via symbol->string). 2429 (define (%pp-directive-name t) 2430 (cond ((eq? (tok-kind t) 'IDENT) (tok-value t)) 2431 ((eq? (tok-kind t) 'KW) (symbol->string (tok-value t))) 2432 (else #f))) 2433 2434 (define (%pp-dispatch-directive hash-tok line state out) 2435 (let ((line (%pp-skip-ws line))) 2436 (cond 2437 ((null? line) #t) ; bare `#` line — null directive 2438 ((%pp-directive-name (car line)) 2439 (let ((name (%pp-directive-name (car line))) 2440 (rest (cdr line)) 2441 (loc (tok-loc (car line)))) 2442 (pmatch name 2443 ("define" (cond ((%pp-active? state) (%pp-do-define rest state)) (else #t))) 2444 ("undef" (cond ((%pp-active? state) (%pp-do-undef rest state)) (else #t))) 2445 ("if" (%pp-do-if rest state)) 2446 ("ifdef" (%pp-do-ifdef rest state)) 2447 ("ifndef" (%pp-do-ifndef rest state)) 2448 ("elif" (%pp-do-elif rest state)) 2449 ("else" (%pp-do-else rest state)) 2450 ("endif" (%pp-do-endif rest state)) 2451 ("error" (cond ((%pp-active? state) 2452 (%pp-do-error (cons (car line) rest) state)) 2453 (else #t))) 2454 ("line" (cond ((%pp-active? state) 2455 ;; Macro-expand the operands BEFORE 2456 ;; processing (`#line MACRO`). Pre-expansion 2457 ;; we capture the directive's source line so 2458 ;; the line-delta math doesn't anchor on a 2459 ;; macro definition site. 2460 (let ((here (cond 2461 ((null? rest) 2462 (loc-line (tok-loc hash-tok))) 2463 (else 2464 (loc-line (tok-loc (car rest))))))) 2465 (%pp-do-line (%pp-expand-line rest state) 2466 state here))) 2467 (else #t))) 2468 ("pragma" (cond ((%pp-active? state) (%pp-do-pragma rest state)) (else #t))) 2469 ("include" (cond ((%pp-active? state) (%pp-do-include rest state)) (else #t))) 2470 (else (die loc "unknown preprocessor directive" name))))) 2471 (else 2472 (die (tok-loc (car line)) "expected directive name after '#'" 2473 (tok-kind (car line))))))) 2474 2475 ;; --- #define --- 2476 ;; function-like vs object-like is decided by an immediately-adjacent `(`. 2477 ;; "Adjacent" = column of `(` equals column of name + length of name. 2478 (define (%pp-do-define line state) 2479 (cond 2480 ((null? line) (die #f "#define requires a macro name")) 2481 ((not (%pp-ident? (car line))) 2482 (die (tok-loc (car line)) "#define: expected identifier")) 2483 (else 2484 (let* ((nt (car line)) (name (tok-value nt)) (rest (cdr line))) 2485 (cond 2486 ((and (not (null? rest)) 2487 (%pp-punct? (car rest) 'lparen) 2488 (= (loc-col (tok-loc (car rest))) 2489 (+ (loc-col (tok-loc nt)) 2490 (bytevector-length name)))) 2491 (%pp-define-fn name (cdr rest) (tok-loc nt) state)) 2492 (else 2493 (let ((m (%macro 'obj '() rest))) 2494 (%hash-set! (pps-macros state) name m)))))))) 2495 2496 (define (%pp-define-fn name post-lparen nloc state) 2497 (let loop ((toks post-lparen) (params '()) (variadic? #f)) 2498 (cond 2499 ((null? toks) (die nloc "#define: unterminated parameter list")) 2500 ((%pp-punct? (car toks) 'rparen) 2501 (let* ((body (cdr toks)) 2502 (kind (if variadic? 'fn-vararg 'fn)) 2503 (m (%macro kind (reverse params) body))) 2504 (%hash-set! (pps-macros state) name m))) 2505 ((%pp-punct? (car toks) 'ellipsis) 2506 (let ((rest (cdr toks))) 2507 (cond 2508 ((null? rest) (die (tok-loc (car toks)) "#define: '...' must precede ')'")) 2509 ((%pp-punct? (car rest) 'rparen) (loop rest params #t)) 2510 (else (die (tok-loc (car rest)) "#define: garbage after '...'"))))) 2511 ((null? params) 2512 (cond 2513 ((%pp-ident? (car toks)) 2514 (loop (cdr toks) (cons (tok-value (car toks)) params) #f)) 2515 (else (die (tok-loc (car toks)) "#define: expected parameter name")))) 2516 (else 2517 (cond 2518 ((%pp-punct? (car toks) 'comma) 2519 (let ((after (cdr toks))) 2520 (cond 2521 ((null? after) (die (tok-loc (car toks)) "#define: trailing ','")) 2522 ((%pp-punct? (car after) 'ellipsis) 2523 (let ((aa (cdr after))) 2524 (cond 2525 ((and (not (null? aa)) (%pp-punct? (car aa) 'rparen)) 2526 (loop aa params #t)) 2527 (else (die (tok-loc (car after)) 2528 "#define: '...' must precede ')'"))))) 2529 ((%pp-ident? (car after)) 2530 (loop (cdr after) (cons (tok-value (car after)) params) #f)) 2531 (else 2532 (die (tok-loc (car after)) 2533 "#define: expected parameter name after ','"))))) 2534 (else (die (tok-loc (car toks)) 2535 "#define: expected ',' or ')' in parameter list"))))))) 2536 2537 ;; --- #undef --- 2538 (define (%pp-do-undef line state) 2539 (cond 2540 ((null? line) (die #f "#undef requires a macro name")) 2541 ((not (%pp-ident? (car line))) 2542 (die (tok-loc (car line)) "#undef: expected identifier")) 2543 (else 2544 (%hash-delete! (pps-macros state) (tok-value (car line)))))) 2545 2546 ;; --- #if / #ifdef / #ifndef / #elif / #else / #endif --- 2547 ;; cond-stack frame: (active? taken? else?). active? gates the body 2548 ;; until the next #elif/#else/#endif; taken? records whether ANY arm 2549 ;; (the original #if branch or any #elif) has matched, so later arms 2550 ;; stay inactive; else? records that we have already passed an #else 2551 ;; in this frame, so a subsequent #elif/#else is rejected. 2552 (define (%pp-frame a? t? e?) (list a? t? e?)) 2553 (define (%pp-frame-active? f) (car f)) 2554 (define (%pp-frame-taken? f) (car (cdr f))) 2555 (define (%pp-frame-else? f) (car (cdr (cdr f)))) 2556 2557 (define (%pp-do-if line state) 2558 (cond 2559 ((not (%pp-active? state)) 2560 (pps-cond-stack-set! state (cons (%pp-frame #f #f #f) (pps-cond-stack state)))) 2561 (else 2562 (let* ((v (pp-eval-cexpr line state)) 2563 (a? (not (%c-value-zero? v)))) 2564 (pps-cond-stack-set! state (cons (%pp-frame a? a? #f) (pps-cond-stack state))))))) 2565 2566 (define (%pp-do-ifdef line state) 2567 (cond 2568 ((not (%pp-active? state)) 2569 (pps-cond-stack-set! state (cons (%pp-frame #f #f #f) (pps-cond-stack state)))) 2570 (else 2571 (let ((d? (%pp-defined? (%pp-name-of-single line) state))) 2572 (pps-cond-stack-set! state 2573 (cons (%pp-frame d? d? #f) (pps-cond-stack state))))))) 2574 2575 (define (%pp-do-ifndef line state) 2576 (cond 2577 ((not (%pp-active? state)) 2578 (pps-cond-stack-set! state (cons (%pp-frame #f #f #f) (pps-cond-stack state)))) 2579 (else 2580 (let ((a? (not (%pp-defined? (%pp-name-of-single line) state)))) 2581 (pps-cond-stack-set! state 2582 (cons (%pp-frame a? a? #f) (pps-cond-stack state))))))) 2583 2584 (define (%pp-name-of-single line) 2585 (cond 2586 ((null? line) (die #f "#ifdef/#ifndef: missing identifier")) 2587 ((not (%pp-ident? (car line))) 2588 (die (tok-loc (car line)) "#ifdef/#ifndef: expected identifier")) 2589 (else (tok-value (car line))))) 2590 2591 (define (%pp-defined? name state) 2592 (or (%hash-ref (pps-macros state) name) 2593 (%pp-builtin? name) 2594 #f)) 2595 2596 (define (%pp-do-elif line state) 2597 (let ((cs (pps-cond-stack state))) 2598 (cond 2599 ((null? cs) (die #f "#elif outside #if")) 2600 (else 2601 (let* ((top (car cs)) (rest (cdr cs)) 2602 (taken? (%pp-frame-taken? top)) 2603 (else? (%pp-frame-else? top)) 2604 (par? (%pp-parent-active? state))) 2605 (cond 2606 (else? (die #f "#elif after #else")) 2607 ((or (not par?) taken?) 2608 (pps-cond-stack-set! state (cons (%pp-frame #f taken? #f) rest))) 2609 (else 2610 (let* ((v (pp-eval-cexpr line state)) 2611 (a? (not (%c-value-zero? v)))) 2612 (pps-cond-stack-set! state 2613 (cons (%pp-frame a? (or a? taken?) #f) rest)))))))))) 2614 2615 (define (%pp-do-else line state) 2616 (let ((cs (pps-cond-stack state))) 2617 (cond 2618 ((null? cs) (die #f "#else outside #if")) 2619 (else 2620 (let* ((top (car cs)) (rest (cdr cs)) 2621 (taken? (%pp-frame-taken? top)) 2622 (else? (%pp-frame-else? top)) 2623 (par? (%pp-parent-active? state))) 2624 (cond 2625 (else? (die #f "#else after #else")) 2626 ((not par?) 2627 (pps-cond-stack-set! state (cons (%pp-frame #f taken? #t) rest))) 2628 (taken? 2629 (pps-cond-stack-set! state (cons (%pp-frame #f #t #t) rest))) 2630 (else 2631 (pps-cond-stack-set! state (cons (%pp-frame #t #t #t) rest))))))))) 2632 2633 (define (%pp-do-endif line state) 2634 (let ((cs (pps-cond-stack state))) 2635 (cond ((null? cs) (die #f "#endif outside #if")) 2636 (else (pps-cond-stack-set! state (cdr cs)))))) 2637 2638 ;; --- #error --- 2639 ;; line[0] is the directive name "error"; the rest is the user message. 2640 (define (%pp-do-error line state) 2641 (let* ((msg-toks (if (null? line) '() (cdr line))) 2642 (loc (if (null? line) #f (tok-loc (car line)))) 2643 (msg (%pp-toks->display msg-toks))) 2644 (die loc "#error" msg))) 2645 2646 ;; Per C11 §6.10.3.2 ¶2: whitespace between argument tokens becomes a 2647 ;; single space; absence of whitespace must NOT introduce one. We 2648 ;; approximate "had whitespace" by comparing locations: a space goes 2649 ;; in iff the next token does not abut the previous one (different 2650 ;; line, or column gap larger than the prev spelling length). 2651 (define (%pp-toks->display toks) 2652 (let loop ((toks toks) (prev #f) (prev-bv #f) (acc '())) 2653 (cond 2654 ((null? toks) (bv-cat (reverse acc))) 2655 (else 2656 (let* ((t (car toks)) (p (%pp-tok->bv t)) 2657 (sep? (cond 2658 ((not prev) #f) 2659 ((or (not (tok-loc prev)) (not (tok-loc t))) #t) 2660 ((not (= (loc-line (tok-loc prev)) 2661 (loc-line (tok-loc t)))) #t) 2662 (else 2663 (not (= (loc-col (tok-loc t)) 2664 (+ (loc-col (tok-loc prev)) 2665 (bytevector-length prev-bv)))))))) 2666 (loop (cdr toks) t p 2667 (if sep? (cons p (cons " " acc)) (cons p acc)))))))) 2668 2669 ;; Reverse-map punctuator symbol -> source spelling. %punct-alist may 2670 ;; map several spellings to the same symbol (e.g. both "[" and "<:" 2671 ;; resolve to 'lbrack); the 1-byte canonical forms appear last in the 2672 ;; source list, so a last-wins fold yields "[" rather than the digraph. 2673 (define %pp-punct-spell 2674 (let loop ((al %punct-alist) (acc '())) 2675 (cond ((null? al) acc) 2676 (else (loop (cdr al) 2677 (alist-set (cdr (car al)) (car (car al)) acc)))))) 2678 2679 (define (%pp-punct-spelling sym) 2680 (or (alist-ref/eq sym %pp-punct-spell) (symbol->string sym))) 2681 2682 (define (%pp-tok->bv t) 2683 (let ((k (tok-kind t)) (v (tok-value t))) 2684 (cond 2685 ((eq? k 'IDENT) v) 2686 ((eq? k 'INT) (%c-value-source-bv v)) 2687 ((eq? k 'STR) (%pp-quote-bytes v 34)) 2688 ((eq? k 'CHAR) (%pp-quote-bytes (bv-of-byte v) 39)) 2689 ((eq? k 'KW) (symbol->string v)) 2690 ((eq? k 'PUNCT) (%pp-punct-spelling v)) 2691 (else "?")))) 2692 2693 ;; Reconstruct a string/char literal source spelling from cooked content. 2694 ;; Per C11 6.10.3.2: stringize must reproduce the source spelling of 2695 ;; STR/CHAR constants — every `"` and `\` is prefixed with `\`, and 2696 ;; the common control-character escapes are restored from their cooked 2697 ;; bytes. `delim` is 34 for STR, 39 for CHAR. 2698 (define (%pp-quote-bytes bv delim) 2699 (let* ((n (bytevector-length bv)) 2700 (delim-bv (bv-of-byte delim))) 2701 (let loop ((i 0) (acc (list delim-bv))) 2702 (cond 2703 ((= i n) (bv-cat (reverse (cons delim-bv acc)))) 2704 (else 2705 (let ((b (bytevector-u8-ref bv i))) 2706 (cond 2707 ((or (= b delim) (= b 92)) 2708 (loop (+ i 1) (cons (bv-of-byte b) (cons "\\" acc)))) 2709 ((= b 10) (loop (+ i 1) (cons "\\n" acc))) 2710 ((= b 9) (loop (+ i 1) (cons "\\t" acc))) 2711 ((= b 13) (loop (+ i 1) (cons "\\r" acc))) 2712 (else 2713 (loop (+ i 1) (cons (bv-of-byte b) acc)))))))))) 2714 2715 ;; --- #line / #pragma / #include --- 2716 ;; Approximate #line: subsequent toks have line = (orig-line + delta), 2717 ;; where delta = (N - here-line - 1). Good enough for most cases. 2718 (define (%pp-do-line line state here) 2719 (cond 2720 ((null? line) (die #f "#line requires a line number")) 2721 ((not (%pp-int? (car line))) 2722 (die (tok-loc (car line)) "#line: expected integer")) 2723 (else 2724 (let* ((nt (car line)) (n (%c-int-raw (tok-value nt))) 2725 (rest (cdr line))) 2726 (pps-line-delta-set! state (- n here 1)) 2727 (cond 2728 ((null? rest) #t) 2729 ((eq? (tok-kind (car rest)) 'STR) 2730 (pps-cur-file-set! state (tok-value (car rest)))) 2731 (else (die (tok-loc (car rest)) 2732 "#line: expected string after number"))))))) 2733 2734 (define (%pp-do-pragma line state) #t) 2735 2736 (define (%pp-do-include line state) 2737 (die (if (null? line) #f (tok-loc (car line))) 2738 "#include: file inclusion is handled upstream by pre-flatten")) 2739 2740 ;; --- macro expansion engine --- 2741 ;; Walk toks; for each IDENT, look up in macros / builtins. Hide-set: 2742 ;; if the name is in t.hide, don't expand. Otherwise expand and rescan 2743 ;; the produced body, with hide += {name}. 2744 (define (%pp-emit-expanded toks state out) 2745 (let loop ((toks toks)) 2746 (cond 2747 ((null? toks) #t) 2748 (else 2749 (let* ((t (car toks)) (rest (cdr toks))) 2750 (cond 2751 ((not (%pp-ident? t)) 2752 (buf-list-push! out (%pp-relocate t state)) 2753 (loop rest)) 2754 (else 2755 (let ((name (tok-value t))) 2756 (cond 2757 ((%pp-bv-mem? name (tok-hide t)) 2758 (buf-list-push! out (%pp-relocate t state)) 2759 (loop rest)) 2760 ((%pp-builtin? name) 2761 (buf-list-push-many! out 2762 (%pp-expand-builtin name (tok-loc t) state)) 2763 (loop rest)) 2764 (else 2765 (let ((m (%hash-ref (pps-macros state) name))) 2766 (cond 2767 ((not m) 2768 (buf-list-push! out (%pp-relocate t state)) 2769 (loop rest)) 2770 (else 2771 (%pp-apply-macro t m rest state out loop)))))))))))))) 2772 2773 (define (%pp-apply-macro t m rest state out cont) 2774 (let ((kind (macro-kind m)) (name (tok-value t))) 2775 (cond 2776 ((eq? kind 'obj) 2777 (let ((bodies (%pp-prepare-body (macro-body m) 2778 (cons name (tok-hide t)) 2779 (tok-loc t)))) 2780 (%pp-emit-expanded bodies state out) 2781 (cont rest))) 2782 (else 2783 (let ((after (%pp-skip-ws rest))) 2784 (cond 2785 ((or (null? after) (not (%pp-punct? (car after) 'lparen))) 2786 (buf-list-push! out (%pp-relocate t state)) 2787 (cont rest)) 2788 (else 2789 (let* ((ar (%pp-collect-args (cdr after) (tok-loc t))) 2790 (args (car ar)) (rest2 (cdr ar)) 2791 (params (macro-params m)) 2792 (variadic? (eq? kind 'fn-vararg)) 2793 (env (%pp-bind-args params args variadic? (tok-loc t))) 2794 (sub (%pp-substitute (macro-body m) env (tok-loc t) state)) 2795 (bodies (%pp-prepare-body sub 2796 (cons name (tok-hide t)) 2797 (tok-loc t)))) 2798 (%pp-emit-expanded bodies state out) 2799 (cont rest2))))))))) 2800 2801 ;; Stamp built-in marker tokens (__LINE__ / __FILE__) inside the body 2802 ;; with the macro-invocation location, so they report the call site 2803 ;; per C11 §6.10.8. Other body tokens keep their #define-time loc so 2804 ;; diagnostics still point at the macro body. Hide-set is updated 2805 ;; with the macro name on every token. 2806 (define (%pp-prepare-body body extra-hide . call-loc-opt) 2807 (let ((call-loc (cond ((null? call-loc-opt) #f) 2808 (else (car call-loc-opt))))) 2809 (map (lambda (t) 2810 (let ((hidden (%pp-with-hide t (%pp-bv-union extra-hide 2811 (tok-hide t))))) 2812 (cond 2813 ((and call-loc (%pp-ident? hidden) 2814 (or (bv= (tok-value hidden) %pp-bv-LINE) 2815 (bv= (tok-value hidden) %pp-bv-FILE))) 2816 (%pp-with-loc hidden call-loc)) 2817 (else hidden)))) 2818 body))) 2819 2820 ;; Collect comma-separated args. `toks` starts AFTER `(`. Returns 2821 ;; (args . rest), where args is a list of token-lists. 2822 (define (%pp-collect-args toks call-loc) 2823 (let loop ((toks toks) (depth 0) (cur '()) (args '())) 2824 (cond 2825 ((null? toks) (die call-loc "macro call: unterminated argument list")) 2826 ((%pp-eof? (car toks)) 2827 (die call-loc "macro call: unterminated argument list")) 2828 ((and (= depth 0) (%pp-punct? (car toks) 'rparen)) 2829 (let ((args* 2830 (cond 2831 ;; Empty parens count as one empty argument; bind-args 2832 ;; degenerates this back to "no args" for 0-param macros. 2833 ((and (null? args) (null? cur)) (list '())) 2834 (else (reverse (cons (reverse cur) args)))))) 2835 (cons args* (cdr toks)))) 2836 ((and (= depth 0) (%pp-punct? (car toks) 'comma)) 2837 (loop (cdr toks) 0 '() (cons (reverse cur) args))) 2838 ((%pp-punct? (car toks) 'lparen) 2839 (loop (cdr toks) (+ depth 1) (cons (car toks) cur) args)) 2840 ((%pp-punct? (car toks) 'rparen) 2841 (loop (cdr toks) (- depth 1) (cons (car toks) cur) args)) 2842 (else 2843 (loop (cdr toks) depth (cons (car toks) cur) args))))) 2844 2845 ;; Bind formals → token-lists (alist by bv key). Variadic gathers 2846 ;; trailing actuals into __VA_ARGS__, joined with synthetic commas. 2847 (define (%pp-bind-args params args variadic? call-loc) 2848 (let* ((np (length params)) (na (length args))) 2849 (cond 2850 (variadic? 2851 (cond 2852 ((< na np) (die call-loc "macro call: too few arguments")) 2853 (else 2854 (let loop ((ps params) (as args) (acc '())) 2855 (cond 2856 ((null? ps) 2857 (alist-set %pp-bv-VA-ARGS (%pp-join-comma as) acc)) 2858 (else 2859 (loop (cdr ps) (cdr as) 2860 (alist-set (car ps) (car as) acc)))))))) 2861 (else 2862 (cond 2863 ((and (= np 0) (= na 1) (null? (car args))) '()) 2864 ((not (= np na)) (die call-loc "macro call: argument count mismatch")) 2865 (else 2866 (let loop ((ps params) (as args) (acc '())) 2867 (cond 2868 ((null? ps) acc) 2869 (else (loop (cdr ps) (cdr as) 2870 (alist-set (car ps) (car as) acc))))))))))) 2871 2872 (define (%pp-join-comma argss) 2873 (cond 2874 ((null? argss) '()) 2875 ((null? (cdr argss)) (car argss)) 2876 (else 2877 (append (car argss) 2878 (cons (%pp-synth-comma) (%pp-join-comma (cdr argss))))))) 2879 2880 (define (%pp-synth-comma) 2881 (%tok 'PUNCT 'comma (%loc "<expand>" 0 0) '())) 2882 2883 ;; Body substitution: walk body; replace param IDENTs with arg toks, 2884 ;; handle `#param` (stringize) and `a##b` (paste). Per C11 §6.10.3.1, 2885 ;; arguments are macro-expanded BEFORE substitution into the body 2886 ;; EXCEPT when the parameter is the operand of `#` or `##` (in which 2887 ;; case the raw token list is used). Without prescan, recursive uses 2888 ;; like M(M(1)) for `#define M(x) ...x...` fail to expand the inner 2889 ;; M during rescan because the outer M is in every substituted 2890 ;; token's hide-set. 2891 (define (%pp-substitute body env call-loc state) 2892 (let loop ((body body) (out '())) 2893 (cond 2894 ((null? body) (reverse out)) 2895 (else 2896 (let ((t (car body)) (rest (cdr body))) 2897 (cond 2898 ((%pp-punct? t 'hash) 2899 (cond 2900 ((or (null? rest) (not (%pp-ident? (car rest)))) 2901 (die (tok-loc t) "stringize: '#' must precede a parameter name")) 2902 (else 2903 (let* ((id (car rest)) (pn (tok-value id)) 2904 (pt (alist-ref pn env))) 2905 (cond 2906 ((not pt) 2907 (die (tok-loc id) "stringize: '#' operand must be a parameter" pn)) 2908 (else 2909 (let ((s (%tok 'STR (%pp-toks->display pt) (tok-loc t) '()))) 2910 (loop (cdr rest) (cons s out))))))))) 2911 ((%pp-punct? t 'paste) 2912 (cond 2913 ((null? out) (die (tok-loc t) "paste: '##' cannot start a body")) 2914 ((null? rest) (die (tok-loc t) "paste: '##' cannot end a body")) 2915 (else 2916 (let* ((lhs (car out)) 2917 (rt (car rest)) 2918 (rhs-list 2919 (cond 2920 ((and (%pp-ident? rt) (alist-ref (tok-value rt) env)) 2921 (alist-ref (tok-value rt) env)) 2922 (else (list rt))))) 2923 (cond 2924 ((null? rhs-list) (loop (cdr rest) out)) 2925 (else 2926 (let* ((p (%pp-paste-tokens lhs (car rhs-list))) 2927 (after (append (cdr rhs-list) (cdr rest)))) 2928 (loop after (cons p (cdr out)))))))))) 2929 ((%pp-ident? t) 2930 (let* ((pn (tok-value t)) (pt (alist-ref pn env))) 2931 (cond 2932 ((not pt) (loop rest (cons t out))) 2933 ((and (not (null? rest)) (%pp-punct? (car rest) 'paste)) 2934 ;; Operand of ##: use raw arg tokens (no prescan). 2935 (cond 2936 ((null? pt) (loop (cdr rest) out)) 2937 (else (loop rest (append (reverse pt) out))))) 2938 (else 2939 ;; Normal use: prescan (fully macro-expand the arg) 2940 ;; before substitution, per C11 §6.10.3.1. 2941 (let ((exp (%pp-expand-line pt state))) 2942 (loop rest (append (reverse exp) out))))))) 2943 (else (loop rest (cons t out))))))))) 2944 2945 ;; Paste two tokens textually; reparse the result. 2946 (define (%pp-paste-tokens lhs rhs) 2947 (let ((lk (tok-kind lhs)) (rk (tok-kind rhs))) 2948 (cond 2949 ((and (eq? lk 'IDENT) (eq? rk 'IDENT)) 2950 (%tok 'IDENT (bytevector-append (tok-value lhs) (tok-value rhs)) 2951 (tok-loc lhs) (%pp-bv-union (tok-hide lhs) (tok-hide rhs)))) 2952 ((and (eq? lk 'IDENT) (eq? rk 'INT)) 2953 (%tok 'IDENT (bytevector-append (tok-value lhs) 2954 (%c-value-source-bv (tok-value rhs))) 2955 (tok-loc lhs) (%pp-bv-union (tok-hide lhs) (tok-hide rhs)))) 2956 ((and (eq? lk 'INT) (eq? rk 'INT)) 2957 (let* ((s (bytevector-append (%c-value-source-bv (tok-value lhs)) 2958 (%c-value-source-bv (tok-value rhs)))) 2959 (r (%c-value-parse-decimal-bv s))) 2960 (cond 2961 ((not (car r)) (die (tok-loc lhs) "paste: cannot reparse as integer" s)) 2962 (else (%tok 'INT (%c-int-lit (cdr r) #f 0 #t) (tok-loc lhs) 2963 (%pp-bv-union (tok-hide lhs) (tok-hide rhs))))))) 2964 (else (die (tok-loc lhs) "paste: unsupported token kinds" lk rk))))) 2965 2966 (define (%pp-relocate t state) 2967 (cond 2968 ((and (= (pps-line-delta state) 0) (not (pps-cur-file state))) t) 2969 (else 2970 (let* ((l (tok-loc t)) 2971 (f (or (pps-cur-file state) (loc-file l))) 2972 (ln (+ (loc-line l) (pps-line-delta state))) 2973 (c (loc-col l))) 2974 (%pp-with-loc t (%loc f ln c)))))) 2975 2976 ;; --- pp-eval-cexpr: #if expression evaluator --- 2977 ;; Steps: resolve `defined NAME`, macro-expand the rest, treat any 2978 ;; remaining IDENT as 0, then delegate to parse-const-int via a minimal 2979 ;; pstate (empty scope, no cg). sizeof(type) works as an extension; 2980 ;; sizeof(expr) dies with a clear message. 2981 ;; 2982 (define (%pp-make-const-ps toks) 2983 (%pstate (make-list-iter toks) 2984 (%world (list (%make-hash-table 8)) 2985 (list (%make-hash-table 8)) 2986 (%make-hash-table 8) 2987 (cons '() (%make-hash-table 8))) 2988 '() #f #f)) 2989 2990 (define (pp-eval-cexpr toks outer) 2991 ;; `outer` is the live %pp-state. We mint a fresh state for #if 2992 ;; evaluation but inherit cur-file and line-delta so __FILE__ / 2993 ;; __LINE__ inside the expression reflect any preceding #line. 2994 (let* ((state (%pp-state (pps-macros outer) '() 2995 (pps-cur-file outer) 2996 (pps-line-delta outer) 2997 #f '() '())) 2998 (s1 (%pp-resolve-defined toks state)) 2999 (s2 (%pp-expand-line s1 state)) 3000 (s3 (%pp-idents-as-zero s2)) 3001 (ps (%pp-make-const-ps s3)) 3002 (val (parse-const-int ps)) 3003 (t (peek ps))) 3004 (cond 3005 ((eq? (tok-kind t) 'EOF) val) 3006 (else (die (tok-loc t) "#if: garbage at end of expression" 3007 (tok-kind t)))))) 3008 3009 (define (%pp-expand-line toks state) 3010 (let ((out (make-buf-list))) 3011 (%pp-emit-expanded toks state out) 3012 (buf-list-flush out))) 3013 3014 (define (%pp-resolve-defined toks state) 3015 (let loop ((toks toks) (acc '())) 3016 (cond 3017 ((null? toks) (reverse acc)) 3018 ((%pp-ident-name? (car toks) %pp-bv-defined) 3019 (let ((rest (cdr toks))) 3020 (cond 3021 ((null? rest) (die (tok-loc (car toks)) "defined: missing operand")) 3022 ((%pp-ident? (car rest)) 3023 (let ((v (if (%pp-defined? (tok-value (car rest)) state) 1 0))) 3024 (loop (cdr rest) 3025 (cons (%tok 'INT v (tok-loc (car toks)) '()) acc)))) 3026 ((%pp-punct? (car rest) 'lparen) 3027 (let ((after (cdr rest))) 3028 (cond 3029 ((or (null? after) (not (%pp-ident? (car after)))) 3030 (die (tok-loc (car toks)) "defined: expected identifier")) 3031 (else 3032 (let ((aa (cdr after))) 3033 (cond 3034 ((or (null? aa) (not (%pp-punct? (car aa) 'rparen))) 3035 (die (tok-loc (car toks)) "defined: expected ')'")) 3036 (else 3037 (let ((v (if (%pp-defined? (tok-value (car after)) state) 1 0))) 3038 (loop (cdr aa) 3039 (cons (%tok 'INT v (tok-loc (car toks)) '()) acc)))))))) )) 3040 (else (die (tok-loc (car rest)) "defined: expected identifier or '('"))))) 3041 (else (loop (cdr toks) (cons (car toks) acc)))))) 3042 3043 (define (%pp-idents-as-zero toks) 3044 (map (lambda (t) 3045 (cond ((%pp-ident? t) (%tok 'INT 0 (tok-loc t) '())) 3046 (else t))) 3047 toks)) 3048 3049 ;; cc/cg.scm — codegen state and emission API. 3050 ;; Conversion split: parse owns promotion etc; cg owns sign extension, 3051 ;; signed/unsigned dispatch, pointer scaling. 3052 ;; 3053 ;; Output uses libp1pp's structured macros (%fn, %ifelse_nez, 3054 ;; %break, %continue) per docs/LIBP1PP.md. Function-local control-flow 3055 ;; labels are hex2++ dotted labels inside %fn's .scope. 3056 ;; 3057 ;; Frame layout: 3058 ;; [sp + 0 .. staging*8) outgoing-arg staging 3059 ;; [sp + staging*8 ..) locals + spilled vstack values 3060 ;; Slot offsets are emitted symbolically as `(+ %<fn>__SO N)` so the 3061 ;; staging size, only known at fn-end, can be filled in via a 0-arg 3062 ;; M1pp macro `<fn>__SO` defined just before the `%fn(...)` block. 3063 3064 (define (%cg-emit-buf cg) 3065 (cond ((cg-in-fn? cg) (cg-fn-buf cg)) (else (cg-text cg)))) 3066 3067 (define (%cg-emit cg bv) 3068 (buf-push! (%cg-emit-buf cg) bv)) 3069 3070 (define (%cg-emit-many cg bvs) 3071 (for-each (lambda (b) (%cg-emit cg b)) bvs)) 3072 3073 (define (%n n) (%c-value-literal-bv n)) 3074 3075 ;; Per-fn metadata (name, ret-slot, ret-type, switch-case lists, ...) 3076 ;; lives on cg-fn-meta, reset at every cg-fn-begin/v. 3077 ;; 3078 ;; Update is destructive: assq for the key, set-cdr! if found, else 3079 ;; prepend. The functional alist-update path was O(n) per write *with* 3080 ;; an append+reverse rebuild — and cg-fn-begin/v plus every emit in a 3081 ;; function body hammers this. Mutation here is safe: the meta alist 3082 ;; is private to one cg, scratch-only, and discarded at fn-end. 3083 (define (%cg-fn-set! cg key val) 3084 (let* ((meta (cg-fn-meta cg)) 3085 (p (assq key meta))) 3086 (cond (p (set-cdr! p val)) 3087 (else (cg-fn-meta-set! cg (cons (cons key val) meta)))))) 3088 3089 (define (%cg-fn-get cg key) (alist-ref/eq key (cg-fn-meta cg))) 3090 3091 (define (%cg-fresh-label cg prefix) 3092 (let* ((n (cg-label-ctr cg)) 3093 (bv (bytevector-append prefix (%n n)))) 3094 (cg-label-ctr-set! cg (+ n 1)) 3095 bv)) 3096 3097 (define (%cg-fresh-loop-tag cg) (%cg-fresh-label cg "L")) 3098 (define (%cg-fresh-lbl cg) (%cg-fresh-label cg "lbl_")) 3099 3100 (define (%cg-bump-outgoing! cg n) 3101 (if (< (cg-max-outgoing cg) n) (cg-max-outgoing-set! cg n) 0)) 3102 3103 (define (%cg-slot-expr cg logical-off) 3104 (let ((nm (%cg-fn-get cg '%fn-label))) 3105 (bv-cat (list "(+ %" nm "__SO " (%n logical-off) ")")))) 3106 3107 (define (%cg-mangle-global cg name-bv) 3108 (bytevector-append (cg-str-prefix cg) "cc__" name-bv)) 3109 3110 ;; Label for a sym at the M1 layer. 3111 ;; 3112 ;; C linkage rules drive this directly: 3113 ;; - external linkage (the default at file scope, plus any `extern` 3114 ;; decl): bare ident. Same label name shared between every decl 3115 ;; and the eventual definition, in any order. `extern T memcpy()` 3116 ;; links to libp1pp's `:memcpy`; `int g_acc;` and refs to it 3117 ;; share `:g_acc`. 3118 ;; - internal linkage (`static`): cc__-prefixed. Free to mangle 3119 ;; since `static` is invisible across TUs, and the prefix keeps 3120 ;; it out of the external/runtime namespace. 3121 ;; Block-scope statics already mangle their sym-name to 3122 ;; `<fnname>__<n>` at parse time (see line ~5125); the cc__ prefix 3123 ;; here just nests another layer of namespacing on top of that. 3124 (define (%cg-sym-label cg sm) 3125 (cond 3126 ((eq? (sym-storage sm) 'static) (%cg-mangle-global cg (sym-name sm))) 3127 (else (sym-name sm)))) 3128 3129 (define (%cg-reg->bv r) (bytevector-append (symbol->string r))) 3130 3131 (define (%cg-emit-li cg reg n) 3132 (%cg-emit-many cg (list "%li(" (%cg-reg->bv reg) ", " (%n n) ")\n"))) 3133 3134 (define (%cg-emit-la cg reg label-bv) 3135 (%cg-emit-many cg (list "%la(" (%cg-reg->bv reg) ", &" label-bv ")\n"))) 3136 3137 (define (%cg-emit-ld-slot cg reg logical-off) 3138 (%cg-emit-many cg (list "%ld(" (%cg-reg->bv reg) ", sp, " 3139 (%cg-slot-expr cg logical-off) ")\n"))) 3140 3141 (define (%cg-emit-st-slot cg reg logical-off) 3142 (%cg-emit-many cg (list "%st(" (%cg-reg->bv reg) ", sp, " 3143 (%cg-slot-expr cg logical-off) ")\n"))) 3144 3145 (define (%cg-emit-ld cg reg base off) 3146 (%cg-emit-many cg (list "%ld(" (%cg-reg->bv reg) ", " 3147 (%cg-reg->bv base) ", " (%n off) ")\n"))) 3148 3149 (define (%cg-emit-st cg reg base off) 3150 (%cg-emit-many cg (list "%st(" (%cg-reg->bv reg) ", " 3151 (%cg-reg->bv base) ", " (%n off) ")\n"))) 3152 3153 ;; Width-aware load/store. Dispatches on ctype-size: 3154 ;; 1: %lb / %sb (LB zero-extends; for signed i8 we sign-extend by 3155 ;; target-word-relative shli/sari to materialize canonical form). 3156 ;; 2/4: handled by libp1pp helpers (P1 has only 1-byte and target-word ops, 3157 ;; and word ops require natural alignment which we can't promise 3158 ;; for struct fields or non-word-aligned local slots). Loads 3159 ;; gather bytes via %lb + shli/or; stores scatter via shri/%sb. 3160 ;; Signed loads (i16/i32) sign-extend via shli/sari to canonical 3161 ;; target-word form. 3162 ;; target-word-sized fallback: %ld / %st. RV32 i64/u64 accesses use 3163 ;; the dedicated adjacent-pair path before reaching these helpers. 3164 ;; Scratch convention: helpers may clobber t1; callers never pass 3165 ;; reg=t1. 3166 3167 ;; Sub-word loads/stores defer byte-decomposition to libp1pp's 3168 ;; %ld_h / %ld_w / %ld_sh / %ld_sw / %st_h / %st_w macros (see 3169 ;; P1/P1pp.P1pp). cc.scm just emits one macro call per access; the 3170 ;; macro arranges the byte gather/scatter and (for signed loads) folds 3171 ;; in the sign-extend. t1 is the conventional scratch. 3172 (define (%cg-emit-ld-sub cg reg base-bv off-bv signed? n-bytes) 3173 (let ((mname (cond ((= n-bytes 2) (if signed? "%ld_sh(" "%ld_h(")) 3174 ((= n-bytes 4) (if signed? "%ld_sw(" "%ld_w(")) 3175 (else (die #f "cg-emit-ld-sub: bad width" n-bytes))))) 3176 (%cg-emit-many cg (list mname (%cg-reg->bv reg) ", " 3177 base-bv ", " off-bv ", t1)\n")))) 3178 3179 (define (%cg-emit-st-sub cg reg base-bv off-bv n-bytes) 3180 (let ((mname (cond ((= n-bytes 2) "%st_h(") 3181 ((= n-bytes 4) "%st_w(") 3182 (else (die #f "cg-emit-st-sub: bad width" n-bytes))))) 3183 (%cg-emit-many cg (list mname (%cg-reg->bv reg) ", " 3184 base-bv ", " off-bv ", t1)\n")))) 3185 3186 ;; "address of frame slot" — defers to libp1pp's %lea_slot, which hides 3187 ;; the backend frame-header offset that %mov(rd, sp) folds in. 3188 (define (%cg-emit-lea-slot cg reg-bv slot-bv) 3189 (%cg-emit-many cg (list "%lea_slot(" reg-bv ", " slot-bv ")\n"))) 3190 3191 ;; sext8/16/32 emitted via libp1pp's %sext<N>(rd, ra). shift-amount is 3192 ;; kept as the parameter for call-site clarity (callers think in bit 3193 ;; widths via the same 56/48/32 amounts they always have). 3194 (define (%cg-emit-sext cg reg shift-amount) 3195 (let ((width (cond ((= shift-amount 56) "8") 3196 ((= shift-amount 48) "16") 3197 ((= shift-amount 32) "32") 3198 (else (die #f "cg-emit-sext: bad shift" shift-amount)))) 3199 (rb (%cg-reg->bv reg))) 3200 (%cg-emit-many cg (list "%sext" width "(" rb ", " rb ")\n")))) 3201 3202 ;; Canonicalize REG against CTYPE's kind: signed narrow types sign-extend, 3203 ;; unsigned narrow types zero-extend, anything else is left alone (the 3204 ;; full target-word value is already canonical). Used after operations that 3205 ;; may have left a non-canonical bit pattern in reg — frame-rval load, 3206 ;; narrowing cast, narrow-typed binop result. 3207 (define (%cg-canonicalize cg reg ctype) 3208 (let* ((rb (%cg-reg->bv reg)) 3209 (k (ctype-kind ctype))) 3210 (cond 3211 ((eq? k 'i8) (%cg-emit-sext cg reg 56)) 3212 ((eq? k 'i16) (%cg-emit-sext cg reg 48)) 3213 ((eq? k 'i32) (%cg-emit-sext cg reg 32)) 3214 ((or (eq? k 'u8) (eq? k 'bool)) 3215 (%cg-emit-many cg (list "%zext8(" rb ", " rb ")\n"))) 3216 ((eq? k 'u16) 3217 (%cg-emit-many cg (list "%zext16(" rb ", " rb ")\n"))) 3218 ((eq? k 'u32) 3219 (%cg-emit-many cg (list "%zext32(" rb ", " rb ", t1)\n"))) 3220 (else 0)))) 3221 3222 ;; Width-aware load/store core. BASE-BV / OFF-BV are pre-built (so the 3223 ;; same body serves both the slot variants — base = "sp", off rendered 3224 ;; through %cg-slot-expr — and the typed variants, where base is a 3225 ;; register and off is a raw integer rendered via %n). 1-byte uses 3226 ;; %lb/%sb (with i8 sext); 2- and 4-byte use the sub-word helpers; the 3227 ;; target-word fallback emits a plain %ld/%st against the same base/off. 3228 (define (%cg-emit-ld-bv cg reg ctype base-bv off-bv) 3229 (%cg-fp-reject! 'ld ctype) 3230 (let* ((sz (ctype-size ctype)) (kind (ctype-kind ctype)) 3231 (rb (%cg-reg->bv reg))) 3232 (cond 3233 ((= sz 1) 3234 (%cg-emit-many cg (list "%lb(" rb ", " base-bv ", " off-bv ")\n")) 3235 (cond ((eq? kind 'i8) (%cg-emit-sext cg reg 56)))) 3236 ((= sz 2) (%cg-emit-ld-sub cg reg base-bv off-bv (eq? kind 'i16) 2)) 3237 ((= sz 4) (%cg-emit-ld-sub cg reg base-bv off-bv (eq? kind 'i32) 4)) 3238 (else 3239 (%cg-emit-many cg (list "%ld(" rb ", " base-bv ", " off-bv ")\n")))))) 3240 3241 (define (%cg-emit-st-bv cg reg ctype base-bv off-bv) 3242 (%cg-fp-reject! 'st ctype) 3243 (let ((sz (ctype-size ctype)) 3244 (rb (%cg-reg->bv reg))) 3245 (cond 3246 ((= sz 1) 3247 (%cg-emit-many cg (list "%sb(" rb ", " base-bv ", " off-bv ")\n"))) 3248 ((= sz 2) (%cg-emit-st-sub cg reg base-bv off-bv 2)) 3249 ((= sz 4) (%cg-emit-st-sub cg reg base-bv off-bv 4)) 3250 (else 3251 (%cg-emit-many cg (list "%st(" rb ", " base-bv ", " off-bv ")\n")))))) 3252 3253 (define (%cg-emit-ld-slot-typed cg reg ctype logical-off) 3254 (%cg-emit-ld-bv cg reg ctype "sp" (%cg-slot-expr cg logical-off))) 3255 (define (%cg-emit-st-slot-typed cg reg ctype logical-off) 3256 (%cg-emit-st-bv cg reg ctype "sp" (%cg-slot-expr cg logical-off))) 3257 3258 (define (%cg-emit-ld-typed cg reg ctype base off) 3259 (%cg-emit-ld-bv cg reg ctype (%cg-reg->bv base) (%n off))) 3260 (define (%cg-emit-st-typed cg reg ctype base off) 3261 (%cg-emit-st-bv cg reg ctype (%cg-reg->bv base) (%n off))) 3262 3263 (define (%cg-emit-li-wide cg lo hi value) 3264 (%cg-emit-many cg 3265 (list "%li(" (%cg-reg->bv lo) ", " 3266 (%c-value-u32-literal-bv value 0) ")\n" 3267 "%li(" (%cg-reg->bv hi) ", " 3268 (%c-value-u32-literal-bv value 1) ")\n"))) 3269 3270 (define (%cg-load-wide-opnd-into cg op lo hi) 3271 ;; Load an RV32 i64/u64 as (low-word, high-word). Wide frame rvalues own 3272 ;; two adjacent target-word slots; wide lvalues read two adjacent words 3273 ;; from their object storage. t2 is the address scratch for indirect and 3274 ;; global lvalues, so callers must keep it distinct from LO/HI. 3275 (cond 3276 ((not (%ctype-wide-int? (opnd-type op))) 3277 (die #f "cg internal: pair load of non-wide operand" (ctype-kind (opnd-type op)))) 3278 ((or (eq? lo 't2) (eq? hi 't2)) 3279 (die #f "cg internal: t2 cannot hold a wide operand limb")) 3280 (else 3281 (pmatch op 3282 (($ opnd? (kind imm) (ext ,n)) 3283 (%cg-emit-li-wide cg lo hi n)) 3284 (($ opnd? (kind frame) (lval? #t) (ext ,off)) 3285 (guard (%cg-indirect? cg off)) 3286 (%cg-emit-ld-slot cg 't2 off) 3287 (%cg-emit-ld cg lo 't2 0) 3288 (%cg-emit-ld cg hi 't2 %CC-WORD-BYTES)) 3289 (($ opnd? (kind frame) (ext ,off)) 3290 (%cg-emit-ld-slot cg lo off) 3291 (%cg-emit-ld-slot cg hi (+ off %CC-WORD-BYTES))) 3292 (($ opnd? (kind global) (lval? #t) (ext ,lbl)) 3293 (%cg-emit-la cg 't2 lbl) 3294 (%cg-emit-ld cg lo 't2 0) 3295 (%cg-emit-ld cg hi 't2 %CC-WORD-BYTES)) 3296 (else 3297 (die #f "cg internal: unsupported wide operand" (opnd-kind op))))))) 3298 3299 (define (%cg-spill-pair cg lo hi ty) 3300 (cond ((not (%ctype-wide-int? ty)) 3301 (die #f "cg internal: pair spill of non-wide type" (ctype-kind ty)))) 3302 (let* ((off (cg-alloc-slot cg (ctype-size ty) 3303 (max %CC-WORD-BYTES (ctype-align ty)))) 3304 (op (%opnd 'frame ty off #f))) 3305 (%cg-emit-st-slot cg lo off) 3306 (%cg-emit-st-slot cg hi (+ off %CC-WORD-BYTES)) 3307 (cg-vstack-set! cg (cons op (cg-vstack cg))) 3308 op)) 3309 3310 (define (%cg-store-pair-to-lval cg lo hi lhs) 3311 (cond ((not (%ctype-wide-int? (opnd-type lhs))) 3312 (die #f "cg internal: pair store to non-wide lvalue" 3313 (ctype-kind (opnd-type lhs))))) 3314 (pmatch lhs 3315 (($ opnd? (kind frame) (ext ,off)) 3316 (guard (%cg-indirect? cg off)) 3317 (%cg-emit-ld-slot cg 't2 off) 3318 (%cg-emit-st cg lo 't2 0) 3319 (%cg-emit-st cg hi 't2 %CC-WORD-BYTES)) 3320 (($ opnd? (kind frame) (ext ,off)) 3321 (%cg-emit-st-slot cg lo off) 3322 (%cg-emit-st-slot cg hi (+ off %CC-WORD-BYTES))) 3323 (($ opnd? (kind global) (ext ,lbl)) 3324 (%cg-emit-la cg 't2 lbl) 3325 (%cg-emit-st cg lo 't2 0) 3326 (%cg-emit-st cg hi 't2 %CC-WORD-BYTES)) 3327 (else (die #f "cg-assign: unsupported wide lhs kind" (opnd-kind lhs))))) 3328 3329 (define (%cg-load-truth-into cg op reg) 3330 (cond 3331 ((%ctype-wide-int? (opnd-type op)) 3332 (%cg-load-wide-opnd-into cg op 'a0 'a1) 3333 (%cg-emit-rrr cg "or" reg 'a0 'a1)) 3334 (else (%cg-load-opnd-into cg op reg)))) 3335 3336 (define (%cg-load-opnd-into cg op reg) 3337 ;; frame lval: load at type width. frame rval is a spilled target word 3338 ;; (allocated by %cg-spill-reg) — always a target-word load. 3339 ;; global lval width > 1 byte-gathers must not alias dest with base — 3340 ;; the first %lb would otherwise clobber the address before subsequent 3341 ;; byte loads. Stage the address in t2. 3342 (%cg-fp-reject! 'load (opnd-type op)) 3343 (cond ((%ctype-wide-int? (opnd-type op)) 3344 (die #f "cg internal: wide operand used as one word" 3345 (ctype-kind (opnd-type op))))) 3346 (pmatch op 3347 (($ opnd? (kind imm) (ext ,n)) (%cg-emit-li cg reg n)) 3348 (($ opnd? (kind frame) (lval? #t) (type ,ty) (ext ,off)) 3349 (%cg-emit-ld-slot-typed cg reg ty off)) 3350 (($ opnd? (kind frame) (lval? #f) (type ,ty) (ext ,off)) 3351 ;; Frame rval: spilled as one target word, but the slot's bit-pattern may 3352 ;; not be canonical for the opnd's CURRENT type (e.g. 3353 ;; cg-arith-conv relabeled a signed slot as unsigned). Canonicalize 3354 ;; on load so downstream 64-bit ALU/compare ops see the C-semantic 3355 ;; value. 3356 (%cg-emit-ld-slot cg reg off) 3357 (%cg-canonicalize cg reg ty)) 3358 (($ opnd? (kind frame) (ext ,off)) (%cg-emit-ld-slot cg reg off)) 3359 (($ opnd? (kind global) (lval? #f) (ext ,lbl)) (%cg-emit-la cg reg lbl)) 3360 (($ opnd? (kind global) (type ,ty) (ext ,lbl)) 3361 (%cg-emit-la cg 't2 lbl) 3362 (%cg-emit-ld-typed cg reg ty 't2 0)) 3363 (else (die #f "cg internal: unknown opnd-kind" (opnd-kind op))))) 3364 3365 (define (%cg-spill-reg cg reg ty) 3366 (cond ((%ctype-wide-int? ty) 3367 (die #f "cg internal: wide value spilled from one register" 3368 (ctype-kind ty)))) 3369 (let* ((off (cg-alloc-slot cg %CC-WORD-BYTES %CC-WORD-BYTES)) 3370 (op (%opnd 'frame ty off #f))) 3371 (%cg-emit-st-slot cg reg off) 3372 (cg-vstack-set! cg (cons op (cg-vstack cg))) 3373 op)) 3374 3375 ;; Floating-point softening. Real FP arithmetic is not implemented; 3376 ;; instead the cg silently treats fp ctypes as same-sized integer 3377 ;; bit patterns (flt as 4-byte, dbl/ldbl as 8-byte). Loads, stores, 3378 ;; and same-size casts round-trip the bytes; widening int→fp casts 3379 ;; leave the int bit-pattern in the wider slot; binops use integer 3380 ;; ALU ops. tcc.flat.c contains real fp code paths (parse_number, 3381 ;; ieee_finite, …) that the bootstrap tcc-boot2 never executes when 3382 ;; compiling float-free programs, so producing valid-but-semantically- 3383 ;; wrong P1pp here is sufficient. Kept as a named no-op so the call 3384 ;; sites stay grep-able if a future bootstrap target needs real FP. 3385 (define (%cg-fp-reject! op-name ty) #t) 3386 3387 (define (%reg-by-idx i) 3388 (cond ((= i 0) 'a0) ((= i 1) 'a1) ((= i 2) 'a2) ((= i 3) 'a3) 3389 (else (die #f "cg: param idx > 3 needs ldarg path" i)))) 3390 3391 ;; -------------------------------------------------------------------- 3392 ;; Lifecycle 3393 ;; -------------------------------------------------------------------- 3394 3395 ;; cc-cg fixtures construct a cg directly via (cg-init) — they don't 3396 ;; emit ELF and don't link against another TU, so library knobs are 3397 ;; irrelevant. cc-main routes through cg-init/v with the parsed flag. 3398 (define (cg-init) (cg-init/v #f "")) 3399 3400 (define (cg-init/v lib? str-prefix) 3401 (%cg (make-buf/cap %BUF-CAP-TEXT) ; text 3402 (make-buf/cap %BUF-CAP-DATA) ; data 3403 (make-buf/cap %BUF-CAP-BSS) ; bss 3404 '() ; vstack 3405 0 ; frame-hi 3406 0 ; label-ctr 3407 (make-world) ; world (shared with pstate) 3408 '() ; fn-meta 3409 (make-buf/cap %BUF-CAP-FN) ; fn-buf (reused per fn) 3410 (make-buf/cap %BUF-CAP-PROLOGUE) ; prologue-buf (reused per fn) 3411 0 ; max-outgoing 3412 #f ; in-fn? 3413 lib? ; lib? (skip entry stub + :ELF_end) 3414 str-prefix)) ; str-prefix (cc__str_N namespacing) 3415 3416 (define (cg-finalize! cg) 3417 ;; Tentative file-scope defs (`int x;` / `static int x;` with no 3418 ;; initializer and not later defined with `=`) get their .bss slot 3419 ;; here at end of TU. C 6.9.2 — see cg-flush-tentatives!. 3420 (cg-flush-tentatives! cg) 3421 ;; Entry stub. P1's program-entry contract (docs/P1.md §Program Entry) 3422 ;; delivers argc in a0 and argv in a1 at p1_main. %call doesn't 3423 ;; clobber a0/a1, so falling straight through to main forwards 3424 ;; them unchanged. The 16-byte frame is just enough for %enter's 3425 ;; saved-fp/lr to fit; main builds its own frame on top. 3426 ;; 3427 ;; In lib mode the stub and :ELF_end are suppressed: the catm chain 3428 ;; supplies them once, from P1/entry-*.P1pp and P1/elf-end.P1pp, so 3429 ;; library TUs don't fight the executable TU for ownership of 3430 ;; :p1_main and don't truncate ELF p_filesz at the first inner 3431 ;; :ELF_end (hex2 sizes off the first one it sees). 3432 (cond 3433 ((not (cg-lib? cg)) 3434 (let ((tb (cg-text cg))) 3435 (buf-push! tb "# entry stub: forwards argc=a0, argv=a1 to main\n") 3436 (buf-push! tb "%fn(p1_main, 16, {\n") 3437 (buf-push! tb "%call(&main)\n") 3438 (buf-push! tb "})\n")))) 3439 #t) 3440 3441 (define (cg-output-size cg) 3442 (+ (buf-offset (cg-text cg)) 3443 (buf-offset (cg-data cg)) 3444 (buf-offset (cg-bss cg)) 3445 (cond ((cg-lib? cg) 0) 3446 (else (bytevector-length ":ELF_end\n"))))) 3447 3448 (define (cg-finish cg) 3449 ;; In-memory form retained for tests and small callers. Production uses 3450 ;; %cg-write-finalized-fd below so a fragmented non-moving heap need not 3451 ;; provide several multi-megabyte snapshot allocations at once. 3452 (cg-finalize! cg) 3453 (bv-cat (list (buf-flush (cg-text cg)) 3454 (buf-flush (cg-data cg)) 3455 (buf-flush (cg-bss cg)) 3456 (cond ((cg-lib? cg) "") 3457 (else ":ELF_end\n"))))) 3458 3459 (define (%cg-write-finalized-fd cg fd) 3460 (let ((text (cg-text cg)) 3461 (data (cg-data cg)) 3462 (bss (cg-bss cg))) 3463 (write-bv-range-fd fd (buf-storage text) 0 (buf-offset text)) 3464 (write-bv-range-fd fd (buf-storage data) 0 (buf-offset data)) 3465 (write-bv-range-fd fd (buf-storage bss) 0 (buf-offset bss)) 3466 (cond ((not (cg-lib? cg)) (write-bv-fd fd ":ELF_end\n"))) 3467 #t)) 3468 3469 (define (cg-fn-begin cg name params return-type) 3470 (cg-fn-begin/v cg name params return-type #f)) 3471 3472 ;; Variadic-aware variant. variadic? = #t reserves a fixed contiguous 3473 ;; target-word window, populating each slot from the 3474 ;; appropriate source: a-register for idx 0..3, LDARG slot (idx-4) for 3475 ;; later indices. va_start computes the address of the slot at index = 3476 ;; named-arg count, so va_arg walks linearly through the rest. 3477 ;; Indices beyond the actual arguments may be garbage; user 3478 ;; code stops walking based on a count or sentinel before those slots 3479 ;; are read. Kit's widest diagnostic calls require 20 total arguments. 3480 (define %CG-VARARG-WINDOW 32) 3481 3482 (define (cg-fn-begin/v cg name params return-type variadic?) 3483 (buf-reset! (cg-fn-buf cg)) 3484 (buf-reset! (cg-prologue-buf cg)) 3485 (cg-in-fn?-set! cg #t) 3486 (cg-vstack-set! cg '()) 3487 (cg-frame-hi-set! cg 0) 3488 ;; cg-label-ctr is NOT reset per-fn. Compiler-internal labels are 3489 ;; emitted as dotted hex2++ locals inside %fn's .scope (and sometimes 3490 ;; nested .scope blocks), so within-TU collisions are already prevented 3491 ;; by local lookup. Keeping the counter monotonic across functions is 3492 ;; no longer required for correctness, just for stable, readable label 3493 ;; names in expanded.M1 traces. 3494 (cg-max-outgoing-set! cg 0) 3495 (cg-fn-meta-set! cg '()) 3496 (%cg-fn-set! cg '%fn-name name) 3497 (let ((sm (%hash-ref (car (world-scope (cg-world cg))) name))) 3498 (%cg-fn-set! cg '%fn-label 3499 (cond (sm (%cg-sym-label cg sm)) (else name)))) 3500 (%cg-fn-set! cg '%fn-ret-type return-type) 3501 (%cg-fn-set! cg '%indirect-slots '()) 3502 (%cg-fn-set! cg '%fn-variadic? variadic?) 3503 ;; Return slot per P1.md §Arguments. One word → a0; two words → a0+a1; 3504 ;; wider aggregates 3505 ;; struct/union → indirect-result (A2): caller passes sret ptr in 3506 ;; a0; cg-return writes through it; sret-slot saves a0 for cg-fn-end. 3507 (let* ((rsz (cond ((eq? (ctype-kind return-type) 'void) %CC-WORD-BYTES) 3508 (else (align-up (max %CC-WORD-BYTES 3509 (ctype-size return-type)) 3510 %CC-WORD-BYTES)))) 3511 (ret-slot (cg-alloc-slot cg rsz %CC-WORD-BYTES))) 3512 (%cg-fn-set! cg '%fn-ret-slot ret-slot) 3513 (cond 3514 ((not (eq? (ctype-kind return-type) 'void)) 3515 (let zinit ((k 0)) 3516 (cond 3517 ((>= k rsz) #t) 3518 (else 3519 (buf-push! (cg-prologue-buf cg) 3520 (bv-cat (list "%li(t0, 0)\n" 3521 "%st(t0, sp, " 3522 (%cg-slot-expr cg (+ ret-slot k)) 3523 ")\n"))) 3524 (zinit (+ k %CC-WORD-BYTES)))))))) 3525 (let* ((rk (ctype-kind return-type)) 3526 (sret? (and (or (eq? rk 'struct) (eq? rk 'union)) 3527 (> (ctype-size return-type) %CC-PAIR-BYTES)))) 3528 (%cg-fn-set! cg '%fn-sret? sret?) 3529 (cond 3530 (sret? 3531 (let ((ss (cg-alloc-slot cg %CC-WORD-BYTES %CC-WORD-BYTES))) 3532 (%cg-fn-set! cg '%fn-sret-slot ss) 3533 (buf-push! (cg-prologue-buf cg) 3534 (bv-cat (list "%st(a0, sp, " 3535 (%cg-slot-expr cg ss) ")\n"))))) 3536 (else (%cg-fn-set! cg '%fn-sret-slot #f)))) 3537 ;; Reject definitions whose named parameters leave no variadic slot. 3538 ;; variadic definitions whose named-arg count would already fill or 3539 ;; exceed it (no room left for variadic reads). 3540 (let ((named-slots 3541 (let count ((xs params) (n 0)) 3542 (cond ((null? xs) n) 3543 (else (count (cdr xs) 3544 (+ n (%cg-param-reg-count (cdar xs))))))))) 3545 (cond 3546 ((and variadic? (>= named-slots %CG-VARARG-WINDOW)) 3547 (die #f "cg-fn-begin: variadic function fills save-area" 3548 name named-slots %CG-VARARG-WINDOW)))) 3549 ;; With sret, explicit arg i lives at ABI position (i+1): args 0..2 3550 ;; in a1..a3, args 3+ in slot (i-3). 3551 (let* ((sret-shift (if (%cg-fn-get cg '%fn-sret?) 1 0)) 3552 (spill (lambda (abi off) 3553 (cond 3554 ((< abi 4) 3555 (buf-push! (cg-prologue-buf cg) 3556 (bv-cat (list "%st(" (%cg-reg->bv (%reg-by-idx abi)) 3557 ", sp, " 3558 (%cg-slot-expr cg off) ")\n")))) 3559 (else 3560 (buf-push! (cg-prologue-buf cg) 3561 (bv-cat (list "%ldarg(t0, " (%n (- abi 4)) ")\n" 3562 "%st(t0, sp, " 3563 (%cg-slot-expr cg off) ")\n")))))))) 3564 (let walk ((ps params) (idx 0) (out '()) (first-slot #f)) 3565 (cond 3566 ((null? ps) 3567 (cond 3568 (variadic? 3569 (let pad ((i idx) (vfirst #f) (fs first-slot)) 3570 (cond 3571 ((>= i %CG-VARARG-WINDOW) 3572 (%cg-fn-set! cg '%fn-vararg-first-slot (or vfirst fs)) 3573 (reverse out)) 3574 (else 3575 (let ((off (cg-alloc-slot cg %CC-WORD-BYTES %CC-WORD-BYTES))) 3576 (spill (+ i sret-shift) off) 3577 (pad (+ i 1) (or vfirst off) (or fs off))))))) 3578 (else (reverse out)))) 3579 (else 3580 (let* ((p (car ps)) 3581 (nm (car p)) 3582 (ty (cdr p)) 3583 ;; Aggregates wider than two words ride one ABI slot containing 3584 ;; a pointer to a caller-owned copy (AAPCS64 / SysV class 3585 ;; MEMORY). Smaller aggregates ride one or two words inline. 3586 (n (%cg-param-reg-count ty)) 3587 (sz (cond ((%cg-param-indirect? ty) %CC-WORD-BYTES) 3588 ((or (%cg-param-aggregate? ty) 3589 (%ctype-wide-int? ty)) 3590 (align-up (ctype-size ty) %CC-WORD-BYTES)) 3591 (else %CC-WORD-BYTES))) 3592 (al (cond ((%cg-param-indirect? ty) %CC-WORD-BYTES) 3593 ((or (%cg-param-aggregate? ty) 3594 (%ctype-wide-int? ty)) 3595 (max %CC-WORD-BYTES (ctype-align ty))) 3596 (else %CC-WORD-BYTES))) 3597 (off (cg-alloc-slot cg sz al)) 3598 (psym (%sym nm 'param #f ty off #t))) 3599 (let chunk ((i 0)) 3600 (cond ((>= i n) 0) 3601 (else 3602 (spill (+ idx sret-shift i) 3603 (+ off (* i %CC-WORD-BYTES))) 3604 (chunk (+ i 1))))) 3605 (cond ((%cg-param-indirect? ty) (%cg-mark-indirect! cg off))) 3606 (walk (cdr ps) (+ idx n) (cons (cons nm psym) out) 3607 (or first-slot off)))))))) 3608 3609 ;; Number of consecutive ABI slots (regs or stack words) consumed by a 3610 ;; parameter of TY. Aggregates up to two words take ceil(size/word). 3611 (define (%cg-param-reg-count ty) 3612 (cond 3613 ((%ctype-wide-int? ty) 2) 3614 ((%cg-param-aggregate? ty) 3615 (let ((sz (ctype-size ty))) 3616 (cond 3617 ((> sz %CC-PAIR-BYTES) 1) 3618 ((> sz %CC-WORD-BYTES) 2) 3619 (else 1)))) 3620 (else 1))) 3621 3622 (define (%cg-param-aggregate? ty) 3623 (let ((k (ctype-kind ty))) 3624 (or (eq? k 'struct) (eq? k 'union)))) 3625 3626 (define (%cg-param-indirect? ty) 3627 (and (%cg-param-aggregate? ty) 3628 (> (ctype-size ty) %CC-PAIR-BYTES))) 3629 3630 (define (cg-fn-end cg) 3631 ;; Drain prologue-buf and fn-buf directly into cg-text via buf-drain! 3632 ;; (memcpy, no allocation). Header/footer pieces go through buf-push! 3633 ;; on cg-text — also memcpy. The only fresh objects here are the small 3634 ;; (%n N) bytevectors for staging-bytes / frame-size. 3635 (let* ((name (%cg-fn-get cg '%fn-name)) 3636 (ret-slot (%cg-fn-get cg '%fn-ret-slot)) 3637 (ret-type (%cg-fn-get cg '%fn-ret-type)) 3638 (locals-hi (cg-frame-hi cg)) 3639 (staging-bytes (* %CC-WORD-BYTES (cg-max-outgoing cg))) 3640 (raw-size (+ staging-bytes locals-hi)) 3641 (frame-size (align-up raw-size 16)) 3642 ;; Look up the bound sym for this fn so `static void foo(){...}` 3643 ;; emits the same cc__-mangled label that callers reference. 3644 ;; The sym was bound by parse-fn-body before the body parse, 3645 ;; so it's in the top scope frame at this point. 3646 (mangled (%cg-fn-get cg '%fn-label)) 3647 (tb (cg-text cg))) 3648 ;; Now that the body is fully emitted, leave fn dispatch so any 3649 ;; trailing emits in this function (including the ret-block below) 3650 ;; route to cg-text directly. 3651 (cg-in-fn?-set! cg #f) 3652 ;; staging-size macro 3653 (buf-push! tb "%macro ") 3654 (buf-push! tb mangled) 3655 (buf-push! tb "__SO()\n") 3656 (buf-push! tb (%n staging-bytes)) 3657 (buf-push! tb "\n%endm\n") 3658 ;; %fn header 3659 (buf-push! tb "%fn(") 3660 (buf-push! tb mangled) 3661 (buf-push! tb ", ") 3662 (buf-push! tb (%n frame-size)) 3663 (buf-push! tb ", {\n") 3664 ;; prologue + body, drained byte-for-byte 3665 (buf-drain! tb (cg-prologue-buf cg)) 3666 ;; --cc-trace-emit: emit `%trace(&LBL, LEN)` between prologue (which 3667 ;; spilled live argument regs to slots) and body, so the macro can 3668 ;; freely clobber a0..a2. The mangled name rides through the 3669 ;; regular string pool — cg-intern-string emits it with a trailing 3670 ;; NUL and pads to 8-byte alignment, so the next data label stays 3671 ;; aligned. We pass the *logical* byte length (no NUL) so the 3672 ;; runtime print stops at the actual end of the name. 3673 (cond 3674 ((trace-emit?) 3675 (let ((tag-lbl (cg-intern-string cg mangled))) 3676 (buf-push! tb "%trace(&") 3677 (buf-push! tb tag-lbl) 3678 (buf-push! tb ", ") 3679 (buf-push! tb (%n (bytevector-length mangled))) 3680 (buf-push! tb ")\n")))) 3681 (buf-drain! tb (cg-fn-buf cg)) 3682 ;; ret block: one word → a0; two words → a0+a1; wider sret → a0. 3683 (buf-push! tb ":.ret\n") 3684 (let ((rk (ctype-kind ret-type)) 3685 (sret? (%cg-fn-get cg '%fn-sret?))) 3686 (cond 3687 ((eq? rk 'void) 3688 (buf-push! tb "%li(a0, 0)\n")) 3689 (sret? 3690 (buf-push! tb "%ld(a0, sp, ") 3691 (buf-push! tb (%cg-slot-expr cg (%cg-fn-get cg '%fn-sret-slot))) 3692 (buf-push! tb ")\n")) 3693 (else 3694 (buf-push! tb "%ld(a0, sp, ") 3695 (buf-push! tb (%cg-slot-expr cg ret-slot)) 3696 (buf-push! tb ")\n") 3697 (cond 3698 ((> (ctype-size ret-type) %CC-WORD-BYTES) 3699 (buf-push! tb "%ld(a1, sp, ") 3700 (buf-push! tb (%cg-slot-expr cg 3701 (+ ret-slot %CC-WORD-BYTES))) 3702 (buf-push! tb ")\n")))))) 3703 (buf-push! tb "})\n") 3704 (cg-vstack-set! cg '()) 3705 (cg-frame-hi-set! cg 0) 3706 (cg-max-outgoing-set! cg 0) 3707 0)) 3708 3709 ;; -------------------------------------------------------------------- 3710 ;; Vstack 3711 ;; -------------------------------------------------------------------- 3712 (define (cg-push cg op) 3713 (cg-vstack-set! cg (cons op (cg-vstack cg))) 3714 op) 3715 3716 (define (cg-pop cg) 3717 (let ((s (cg-vstack cg))) 3718 (cond ((null? s) (die #f "cg-pop: empty vstack")) 3719 (else (cg-vstack-set! cg (cdr s)) (car s))))) 3720 3721 (define (cg-top cg) 3722 (let ((s (cg-vstack cg))) 3723 (cond ((null? s) (die #f "cg-top: empty vstack")) (else (car s))))) 3724 3725 (define (cg-depth cg) (length (cg-vstack cg))) 3726 3727 ;; -------------------------------------------------------------------- 3728 ;; Snapshot / rewind — discard any vstack pushes and fn-buf bytes 3729 ;; emitted between snapshot and rewind. Used by sizeof to parse its 3730 ;; operand for type information without retaining its side effects 3731 ;; (CC.md §Expressions: sizeof's operand is not evaluated). Internal- 3732 ;; only; the parser is the sole expected caller. 3733 ;; 3734 ;; vstack captures the head of the cons-list (immutable structurally). 3735 ;; fn-buf is restored by resetting buf-offset; the underlying storage 3736 ;; bytes past the new offset become garbage that the next buf-push! 3737 ;; will overwrite (buf-push! always copies into [offset, offset+len)). 3738 ;; frame-hi and max-outgoing are also restored so cg-alloc-slot calls 3739 ;; inside the rewound region don't leak frame bytes. The label counter and 3740 ;; indirect-slot set are part of the same transaction: parsing `sizeof *p` 3741 ;; mints an indirect temporary, and leaving that tag behind after frame-hi is 3742 ;; rewound would make the next ordinary local at the reused offset behave as a 3743 ;; pointer-to-storage slot. 3744 ;; -------------------------------------------------------------------- 3745 (define (cg-snapshot cg) 3746 (cond 3747 ((not (cg-in-fn? cg)) 3748 (die #f "cg-snapshot: not in fn"))) 3749 (list (cg-vstack cg) 3750 (buf-offset (cg-fn-buf cg)) 3751 (cg-frame-hi cg) 3752 (cg-max-outgoing cg) 3753 (cg-label-ctr cg) 3754 (or (%cg-fn-get cg '%indirect-slots) '()))) 3755 3756 (define (cg-rewind cg tag) 3757 (cg-vstack-set! cg (car tag)) 3758 (buf-offset-set! (cg-fn-buf cg) (cadr tag)) 3759 (cg-frame-hi-set! cg (caddr tag)) 3760 (cg-max-outgoing-set! cg (cadddr tag)) 3761 (cg-label-ctr-set! cg (car (cddddr tag))) 3762 (%cg-fn-set! cg '%indirect-slots (cadr (cddddr tag)))) 3763 3764 ;; Duplicate the top vstack entry. For lvals this is safe — the slot 3765 ;; (or label, or indirect-marked frame) backing the lval keeps existing 3766 ;; until the function ends. For rvals it duplicates the descriptor of 3767 ;; the spilled value; both copies refer to the same already-emitted 3768 ;; storage. Used for `lhs += rhs` and `++lhs` to preserve the lhs 3769 ;; across a `cg-load` so the subsequent `cg-assign` still has its 3770 ;; address. 3771 (define (cg-dup cg) 3772 (let ((p (cg-top cg))) (cg-push cg p) p)) 3773 3774 ;; -------------------------------------------------------------------- 3775 ;; Materialize 3776 ;; -------------------------------------------------------------------- 3777 (define (cg-push-imm cg ctype value) 3778 (cg-push cg (%opnd 'imm ctype value #f))) 3779 3780 (define (cg-push-string cg bv-content) 3781 (let* ((label (cg-intern-string cg bv-content)) 3782 (cp-ty (%mk-ptr %t-i8))) 3783 (cg-push cg (%opnd 'global cp-ty label #f)))) 3784 3785 (define (cg-push-sym cg sm) 3786 (pmatch sm 3787 (($ sym? (kind fn) (type ,ty)) 3788 (cg-push cg (%opnd 'global ty (%cg-sym-label cg sm) #f))) 3789 (($ sym? (kind enum-const) (type ,ty) (slot ,v)) 3790 (cg-push cg (%opnd 'imm ty v #f))) 3791 (($ sym? (kind var) (storage extern) (type ,ty)) 3792 (cg-push cg (%opnd 'global ty (%cg-sym-label cg sm) #t))) 3793 (($ sym? (kind var) (storage static) (type ,ty)) 3794 (cg-push cg (%opnd 'global ty (%cg-sym-label cg sm) #t))) 3795 (($ sym? (kind var) (type ,ty) (slot ,off)) 3796 (cg-push cg (%opnd 'frame ty off #t))) 3797 (($ sym? (kind param) (type ,ty) (slot ,off)) 3798 (cg-push cg (%opnd 'frame ty off #t))) 3799 (else (die #f "cg-push-sym: unsupported sym-kind" (sym-kind sm))))) 3800 3801 ;; A cg-push-deref result is a frame-lval whose slot HOLDS THE ADDRESS 3802 ;; (not the value). To distinguish from ordinary frame-lvals (whose 3803 ;; slot holds the value directly), we tag indirect slots in 3804 ;; %indirect-slots so cg-load and cg-assign can do the extra 3805 ;; indirection. 3806 (define (%cg-mark-indirect! cg off) 3807 (let ((cur (or (%cg-fn-get cg '%indirect-slots) '()))) 3808 (%cg-fn-set! cg '%indirect-slots (cons off cur)))) 3809 3810 (define (%cg-indirect? cg off) 3811 (let ((cur (or (%cg-fn-get cg '%indirect-slots) '()))) 3812 (let loop ((xs cur)) 3813 (cond ((null? xs) #f) ((= (car xs) off) #t) (else (loop (cdr xs))))))) 3814 3815 (define (cg-push-deref cg) 3816 (let* ((p (cg-pop cg)) 3817 (pt (opnd-type p)) 3818 (pe (cond ((eq? (ctype-kind pt) 'ptr) (ctype-ext pt)) 3819 ((eq? (ctype-kind pt) 'arr) (car (ctype-ext pt))) 3820 (else #f)))) 3821 (cond 3822 ((not pe) (die #f "cg-push-deref: not a pointer" pt)) 3823 (else 3824 (%cg-load-opnd-into cg p 't0) 3825 (let ((off (cg-alloc-slot cg %CC-WORD-BYTES %CC-WORD-BYTES))) 3826 (%cg-emit-st-slot cg 't0 off) 3827 (%cg-mark-indirect! cg off) 3828 (cg-push cg (%opnd 'frame pe off #t))))))) 3829 3830 ;; -------------------------------------------------------------------- 3831 ;; Aggregate field access (§D.1–D.4) 3832 ;; -------------------------------------------------------------------- 3833 ;; cg-push-field cg fname: 3834 ;; pop a struct/union lval; look up `fname` in the struct's fields 3835 ;; list (data.scm: ext = (tag complete? fields), where each field 3836 ;; is (name-bv ctype offset)); push a new lval at the field's 3837 ;; offset with the field's ctype. 3838 ;; 3839 ;; Three input cases: 3840 ;; - direct frame lval at slot `off` -> frame lval at off+fo 3841 ;; - indirect frame lval (slot holds addr) -> new indirect slot for 3842 ;; addr+fo 3843 ;; - global lval at label L -> indirect slot for 3844 ;; la(L)+fo 3845 ;; In all cases the resulting lval has the field's ctype. 3846 3847 ;; Look up FNAME in FIELDS. C11 §6.7.2.1: a struct/union member with no 3848 ;; declarator (e.g. `union { int a; int b; };` inside another struct) is 3849 ;; an "anonymous member" — its members are addressed as if they belonged 3850 ;; directly to the enclosing aggregate. We recurse into any name=#f 3851 ;; member of struct/union kind, composing the outer member's offset with 3852 ;; the inner field's offset, and return a synthetic (name ctype off) 3853 ;; triple so callers can stay agnostic about anonymity. 3854 (define (%cg-find-field fields fname) 3855 (let loop ((xs fields)) 3856 (cond 3857 ((null? xs) #f) 3858 (else 3859 (let* ((f (car xs)) 3860 (fn (car f))) 3861 (cond 3862 ((and fn (bv= fn fname)) f) 3863 ((and (not fn) 3864 (let ((k (ctype-kind (cadr f)))) 3865 (or (eq? k 'struct) (eq? k 'union)))) 3866 (let* ((sub-ext (ctype-ext (cadr f))) 3867 (sub-fields (car (cddr sub-ext))) 3868 (hit (%cg-find-field sub-fields fname))) 3869 (cond 3870 (hit (list (car hit) 3871 (cadr hit) 3872 (+ (car (cddr f)) (car (cddr hit))))) 3873 (else (loop (cdr xs)))))) 3874 (else (loop (cdr xs))))))))) 3875 3876 (define (cg-push-field cg fname) 3877 (let* ((s (cg-pop cg)) 3878 (sty (opnd-type s)) 3879 (k (ctype-kind sty))) 3880 (cond 3881 ((not (or (eq? k 'struct) (eq? k 'union))) 3882 (die #f "cg-push-field: not a struct/union" k)) 3883 ((not (opnd-lval? s)) 3884 (die #f "cg-push-field: not an lvalue" k)) 3885 (else 3886 (let* ((fields (car (cddr (ctype-ext sty)))) 3887 (f (%cg-find-field fields fname))) 3888 (cond 3889 ((not f) (die #f "cg-push-field: no such field" fname)) 3890 (else 3891 (let* ((fty (cadr f)) (fo (car (cddr f)))) 3892 (pmatch s 3893 ;; direct frame lval: just shift the slot offset. 3894 (($ opnd? (kind frame) (ext ,off)) 3895 (guard (not (%cg-indirect? cg off))) 3896 (cg-push cg (%opnd 'frame fty (+ off fo) #t))) 3897 ;; indirect frame lval: addr lives in the slot. Compute 3898 ;; addr+fo into a new indirect slot. 3899 (($ opnd? (kind frame) (ext ,off)) 3900 (%cg-emit-ld-slot cg 't0 off) 3901 (cond 3902 ((> fo 0) 3903 (%cg-emit-many cg (list "%addi(t0, t0, " (%n fo) ")\n")))) 3904 (let ((no (cg-alloc-slot cg %CC-WORD-BYTES %CC-WORD-BYTES))) 3905 (%cg-emit-st-slot cg 't0 no) 3906 (%cg-mark-indirect! cg no) 3907 (cg-push cg (%opnd 'frame fty no #t)))) 3908 ;; global lval: load addr, add offset, indirect slot. 3909 (($ opnd? (kind global) (ext ,lbl)) 3910 (%cg-emit-la cg 't0 lbl) 3911 (cond 3912 ((> fo 0) 3913 (%cg-emit-many cg (list "%addi(t0, t0, " (%n fo) ")\n")))) 3914 (let ((no (cg-alloc-slot cg %CC-WORD-BYTES %CC-WORD-BYTES))) 3915 (%cg-emit-st-slot cg 't0 no) 3916 (%cg-mark-indirect! cg no) 3917 (cg-push cg (%opnd 'frame fty no #t)))) 3918 (else 3919 (die #f "cg-push-field: unsupported lval kind" 3920 (opnd-kind s)))))))))))) 3921 3922 ;; cg-decay-array: 3923 ;; if top of vstack is an arr-typed lval, replace it with a ptr-rval 3924 ;; to the first element. C arrays decay to T* in most contexts; 3925 ;; parse calls this before rval-style operations. No-op otherwise. 3926 (define (cg-decay-array cg) 3927 (let ((tp (cg-top cg))) 3928 (cond 3929 ((and (opnd-lval? tp) (eq? (ctype-kind (opnd-type tp)) 'arr)) 3930 (let* ((p (cg-pop cg)) 3931 (et (car (ctype-ext (opnd-type p)))) 3932 (pty (%mk-ptr et))) 3933 (pmatch p 3934 ;; direct frame lval: address is sp+off. 3935 (($ opnd? (kind frame) (ext ,off)) 3936 (guard (not (%cg-indirect? cg off))) 3937 (%cg-emit-lea-slot cg "t0" (%cg-slot-expr cg off)) 3938 (%cg-spill-reg cg 't0 pty)) 3939 ;; indirect frame lval (rare for arrays, but support it): 3940 ;; the slot holds the address already. 3941 (($ opnd? (kind frame) (ext ,off)) 3942 (%cg-emit-ld-slot cg 't0 off) 3943 (%cg-spill-reg cg 't0 pty)) 3944 ;; global array: la(label) is the address. 3945 (($ opnd? (kind global) (ext ,lbl)) 3946 (%cg-emit-la cg 't0 lbl) 3947 (%cg-spill-reg cg 't0 pty)) 3948 (else (die #f "cg-decay-array: unsupported lval kind" 3949 (opnd-kind p)))))) 3950 (else tp)))) 3951 3952 ;; -------------------------------------------------------------------- 3953 ;; Address & deref 3954 ;; -------------------------------------------------------------------- 3955 3956 ;; Materialize the address of an lval `op` directly into `reg`. 3957 ;; Variant of cg-take-addr that doesn't spill — used by struct copy 3958 ;; primitives (cg-return on struct, cg-copy-struct, cg-assign-struct, 3959 ;; cg-call's struct receive). Caller owns the opnd (already popped). 3960 ;; 3961 ;; A frame opnd is treated as a slot whose address we want: if it's a 3962 ;; flagged-indirect lval (slot holds a pointer to the real storage), 3963 ;; load the pointer; otherwise the slot itself IS the storage and we 3964 ;; lea its address. Frame rvals are temp spills — address = &slot. A 3965 ;; global opnd's label is the address. Callers that require an lval 3966 ;; check it before calling. 3967 (define (%cg-emit-addr-of cg op reg) 3968 (let ((reg-bv (%cg-reg->bv reg))) 3969 (pmatch op 3970 (($ opnd? (kind frame) (lval? #t) (ext ,off)) 3971 (guard (%cg-indirect? cg off)) 3972 (%cg-emit-ld-slot cg reg off)) 3973 (($ opnd? (kind frame) (ext ,off)) 3974 (%cg-emit-lea-slot cg reg-bv (%cg-slot-expr cg off))) 3975 (($ opnd? (kind global) (ext ,lbl)) 3976 (%cg-emit-la cg reg lbl)) 3977 (else (die #f "cg-emit-addr-of: unsupported opnd" 3978 (opnd-kind op) (opnd-lval? op)))))) 3979 3980 ;; cg-copy-struct: pop src lval, pop dst lval, emit per-byte copy 3981 ;; from src to dst (both must be lvals of the same struct/union type). 3982 ;; Used by parser for struct-typed assignment / initializer-from-call 3983 ;; targets. Pushes nothing. 3984 (define (cg-copy-struct cg) 3985 (let* ((src (cg-pop cg)) 3986 (dst (cg-pop cg)) 3987 (sty (opnd-type dst)) 3988 (sz (ctype-size sty))) 3989 (cond 3990 ((not (opnd-lval? src)) (die #f "cg-copy-struct: src not lvalue")) 3991 ((not (opnd-lval? dst)) (die #f "cg-copy-struct: dst not lvalue"))) 3992 (%cg-emit-addr-of cg src 't0) 3993 (%cg-emit-addr-of cg dst 't2) 3994 (%cg-emit-byte-copy cg 't2 't0 't1 sz))) 3995 3996 ;; Struct/union `=` assignment: pop src lval, pop dst lval, memcpy, 3997 ;; then push dst back so the assignment expression has a result for 3998 ;; the surrounding parser to consume (parse-expr-stmt's trailing 3999 ;; cg-pop, etc.). Distinct from cg-copy-struct because the 4000 ;; initializer caller needs no result on the vstack. 4001 ;; 4002 ;; The src may be either a frame lvalue (named local slot, *p deref, 4003 ;; callee return-slot) or a frame rvalue (anonymous slot from a temp 4004 ;; spill); %cg-emit-addr-of handles both shapes by treating the slot 4005 ;; itself as the address whenever the lval indirection flag isn't set. 4006 (define (cg-assign-struct cg) 4007 (let* ((src (cg-pop cg)) 4008 (dst (cg-pop cg)) 4009 (sty (opnd-type dst)) 4010 (sz (ctype-size sty))) 4011 (cond ((not (opnd-lval? dst)) (die #f "cg-assign-struct: dst not lvalue"))) 4012 (%cg-emit-addr-of cg src 't0) 4013 (%cg-emit-addr-of cg dst 't2) 4014 (%cg-emit-byte-copy cg 't2 't0 't1 sz) 4015 (cg-push cg dst))) 4016 4017 ;; Struct copy: defer to libp1pp memcpy via %memcpy_call. dst-reg and 4018 ;; src-reg hold the addresses; size is the byte count. tmp-reg is no 4019 ;; longer needed by this helper (kept in the signature so existing 4020 ;; callers don't have to thread their scratch allocation differently), 4021 ;; but the macro itself uses a0/a1/a2 around the call. dst-reg and 4022 ;; src-reg must not be a0 (the dst move would clobber a different live 4023 ;; input register); both current callers use t-regs. 4024 (define (%cg-emit-byte-copy cg dst-reg src-reg tmp-reg size) 4025 (%cg-emit-many cg (list "%memcpy_call(" 4026 (%cg-reg->bv dst-reg) ", " 4027 (%cg-reg->bv src-reg) ", " 4028 (%n size) ")\n"))) 4029 4030 (define (cg-take-addr cg) 4031 (let* ((p (cg-pop cg)) 4032 (ty (opnd-type p)) 4033 ;; &arr yields T(*)[N] per strict C. Pointer arithmetic on 4034 ;; the result scales by sizeof(T[N]) (the whole array), so 4035 ;; &arr + 1 is one-past-end. Array-to-pointer decay happens 4036 ;; on use via cg-decay-array, not at the & operator. 4037 (pty (%mk-ptr ty))) 4038 (pmatch p 4039 ;; &function: a function designator (rval of fn type pushed by 4040 ;; cg-push-sym) already evaluates to its entry-point address. The 4041 ;; `&` is a no-op semantically — re-tag the operand as ptr-to-fn. 4042 (($ opnd? (kind global) (type ,t) (ext ,lbl) (lval? #f)) 4043 (guard (eq? (ctype-kind t) 'fn)) 4044 (cg-push cg (%opnd 'global pty lbl #f))) 4045 (($ opnd? (lval? #f)) (die #f "cg-take-addr: not an lvalue")) 4046 ;; The address itself lives at sp+slot — &*p degenerates to p. 4047 (($ opnd? (kind frame) (ext ,off)) 4048 (guard (%cg-indirect? cg off)) 4049 (%cg-emit-ld-slot cg 't0 off) 4050 (%cg-spill-reg cg 't0 pty)) 4051 ;; %lea_slot wraps the "%mov(rd, sp); %addi(rd, rd, slot)" idiom; 4052 ;; the backend hides any frame-header offset inside %mov(rd, sp). 4053 (($ opnd? (kind frame) (ext ,off)) 4054 (%cg-emit-lea-slot cg "t0" (%cg-slot-expr cg off)) 4055 (%cg-spill-reg cg 't0 pty)) 4056 (($ opnd? (kind global) (ext ,lbl)) 4057 (%cg-emit-la cg 't0 lbl) 4058 (%cg-spill-reg cg 't0 pty)) 4059 (else (die #f "cg-take-addr: non-addressable" (opnd-kind p)))))) 4060 4061 (define (cg-load cg) 4062 (let* ((p (cg-pop cg)) (ty (opnd-type p))) 4063 (cond 4064 ((not (opnd-lval? p)) (die #f "cg-load: not an lvalue")) 4065 ;; Array lvalues decay to a ptr-rval addressing the first 4066 ;; element (C array-to-pointer decay). We push the lval back 4067 ;; and route through cg-decay-array for a single source of truth. 4068 ((eq? (ctype-kind ty) 'arr) 4069 (cg-push cg p) (cg-decay-array cg)) 4070 ;; Struct/union lvalues stay as lvalues — there is no 4071 ;; register-sized rvalue form for an aggregate, and the 4072 ;; historical one-word spill path silently truncated wider aggregates 4073 ;; (the bug that broke `c = cond ? a : b`). Surrounding machinery 4074 ;; (cg-ifelse-merge / cg-assign-struct / cg-call) consumes 4075 ;; aggregate operands as lvalues already. 4076 ((or (eq? (ctype-kind ty) 'struct) (eq? (ctype-kind ty) 'union)) 4077 (cg-push cg p)) 4078 ((%ctype-wide-int? ty) 4079 (%cg-load-wide-opnd-into cg p 't0 't1) 4080 (%cg-spill-pair cg 't0 't1 ty)) 4081 ((and (eq? (opnd-kind p) 'frame) 4082 (%cg-indirect? cg (opnd-ext p))) 4083 ;; Indirect frame-lval: slot holds the address. Stage the 4084 ;; address in t2 so multi-byte gathers don't alias dest with 4085 ;; base. 4086 (%cg-emit-ld-slot cg 't2 (opnd-ext p)) 4087 (%cg-emit-ld-typed cg 't0 ty 't2 0) 4088 (%cg-spill-reg cg 't0 ty)) 4089 (else (%cg-load-opnd-into cg p 't0) (%cg-spill-reg cg 't0 ty))))) 4090 4091 ;; -------------------------------------------------------------------- 4092 ;; Type conversions 4093 ;; -------------------------------------------------------------------- 4094 (define (cg-cast cg to-type) 4095 (let* ((p (cg-pop cg)) 4096 (from-ty (opnd-type p)) 4097 (from-sz (ctype-size from-ty)) 4098 (to-sz (ctype-size to-type)) 4099 (to-kind (ctype-kind to-type))) 4100 (%cg-fp-reject! 'cast-to to-type) 4101 (%cg-fp-reject! 'cast-from from-ty) 4102 (cond 4103 ((eq? to-kind 'bool) 4104 (cond 4105 ((%ctype-wide-int? from-ty) 4106 (%cg-load-wide-opnd-into cg p 't0 't1) 4107 (%cg-emit-rrr cg "or" 't0 't0 't1)) 4108 (else (%cg-load-opnd-into cg p 't0))) 4109 (%cg-emit-many cg (list "%bool(t0, t0)\n")) 4110 (%cg-spill-reg cg 't0 to-type)) 4111 ;; Pointer-to-pointer casts preserve the one-word representation. 4112 ((and (eq? to-kind 'ptr) 4113 (or (eq? (ctype-kind from-ty) 'ptr) 4114 (eq? (ctype-kind from-ty) 'arr) 4115 (eq? (ctype-kind from-ty) 'fn))) 4116 (cg-push cg (%opnd (opnd-kind p) to-type (opnd-ext p) (opnd-lval? p)))) 4117 ;; Any RV32 narrowing conversion consumes only the low limb, then 4118 ;; canonicalizes it for the destination type. 4119 ((and (%ctype-wide-int? from-ty) (not (%ctype-wide-int? to-type))) 4120 (%cg-load-wide-opnd-into cg p 't0 't1) 4121 (%cg-canonicalize cg 't0 to-type) 4122 (%cg-spill-reg cg 't0 to-type)) 4123 ;; Same-width signedness changes do not alter an i64 bit pattern. 4124 ((and (%ctype-wide-int? from-ty) (%ctype-wide-int? to-type)) 4125 (cg-push cg (%opnd (opnd-kind p) to-type (opnd-ext p) (opnd-lval? p)))) 4126 ;; Widen a one-word RV32 value into a real two-word integer. The low 4127 ;; limb is already canonical; signed sources replicate their sign bit. 4128 ((%ctype-wide-int? to-type) 4129 (%cg-load-opnd-into cg p 't0) 4130 (cond 4131 ((%ctype-unsigned? from-ty) 4132 (%cg-emit-many cg (list "%li(t1, 0)\n"))) 4133 (else 4134 (%cg-emit-many cg 4135 (list "%sari(t1, t0, " 4136 (%n (- %CC-WORD-BITS 1)) ")\n")))) 4137 (%cg-spill-pair cg 't0 't1 to-type)) 4138 ;; Same-size or widening cast — retag only when the canonical 4139 ;; 64-bit slot form for FROM-TY is also canonical for TO-TYPE. 4140 ;; That holds unless we're crossing from a signed type into an 4141 ;; unsigned one of the same or wider width: the source's 4142 ;; sign-extended high bits would leak past the unsigned width 4143 ;; and corrupt later 64-bit operands (compares, wider casts). 4144 ;; Same applies to same-size unsigned→signed at narrow widths 4145 ;; (the narrow branch's sign-extend turns 0xCA back into the 4146 ;; canonical i8 slot 0xFF…FFCA). 4147 ((and (>= to-sz from-sz) 4148 (not (and (not (%ctype-unsigned? from-ty)) 4149 (%ctype-unsigned? to-type))) 4150 (not (and (= to-sz from-sz) 4151 (%ctype-unsigned? from-ty) 4152 (not (%ctype-unsigned? to-type))))) 4153 (cg-push cg (%opnd (opnd-kind p) to-type (opnd-ext p) (opnd-lval? p)))) 4154 (else 4155 ;; Narrowing cast OR same/widening with signedness flip. 4156 ;; Signed targets (i8/i16/i32) shli/sari to truncate-and- 4157 ;; sign-extend in one step, so the slot holds the canonical 4158 ;; 64-bit form and a subsequent widening cast (which is 4159 ;; relabel-only) restores the value. Unsigned targets mask 4160 ;; off high bits to zero-extend. 4161 (%cg-load-opnd-into cg p 't0) 4162 (%cg-canonicalize cg 't0 to-type) 4163 (%cg-spill-reg cg 't0 to-type))))) 4164 4165 (define (cg-promote cg) 4166 (let* ((p (cg-pop cg)) 4167 (ty (opnd-type p)) 4168 (sz (ctype-size ty))) 4169 (cond 4170 ;; C 6.3.1.1: _Bool, char, short, and any narrower int type 4171 ;; promote to (signed) int — every representable value fits 4172 ;; in i32. Treating narrow unsigned types as u32 here would 4173 ;; drag the subsequent arith-conv into picking the unsigned 4174 ;; common type, flipping signedness of `>>`, comparisons, 4175 ;; division, etc. against the C rule. Canonical form for any 4176 ;; in-range narrow value already matches i32, so the cast is 4177 ;; relabel-only. 4178 ((< sz 4) 4179 (cg-push cg (%opnd (opnd-kind p) %t-i32 (opnd-ext p) (opnd-lval? p)))) 4180 (else (cg-push cg p))))) 4181 4182 (define (cg-arith-conv cg) 4183 ;; Usual arithmetic conversions on arithmetic operands. When either 4184 ;; operand is a pointer (or array, 4185 ;; which behaves as a pointer in arithmetic), the pair is a 4186 ;; pointer-arith case — leave the types alone so cg-binop can detect 4187 ;; the ptr operand and apply the right scaling. 4188 (let* ((b (cg-pop cg)) 4189 (a (cg-pop cg)) 4190 (ta (opnd-type a)) 4191 (tb (opnd-type b)) 4192 (sa (ctype-size ta)) 4193 (sb (ctype-size tb))) 4194 (cond 4195 ;; Pointer/array arithmetic: leave types alone so cg-binop's 4196 ;; ptr-aware add/sub branch fires with the correct pointee type 4197 ;; (and doesn't see two pointers, which would skip scaling). 4198 ((or (%ctype-ptr? ta) (%ctype-ptr? tb)) 4199 (cg-push cg a) 4200 (cg-push cg b)) 4201 (else 4202 (let ((common (cond 4203 ((> sa sb) ta) 4204 ((> sb sa) tb) 4205 ((%ctype-unsigned? ta) ta) 4206 ((%ctype-unsigned? tb) tb) 4207 (else ta)))) 4208 ;; Route through cg-cast (rather than relabel only) so the 4209 ;; canonical target-word slot form lines up with COMMON. Same-size 4210 ;; cross-signedness conversions (i32→u32, u32→i32, …) need an 4211 ;; actual zext/sext to canonicalize; otherwise an i32 -3 4212 ;; relabeled to u32 keeps its sign-extended slot bits and 4213 ;; compares unequal to a u32 imm with the same C value. 4214 (cg-push cg a) (cg-cast cg common) 4215 (let ((a* (cg-pop cg))) 4216 (cg-push cg b) (cg-cast cg common) 4217 (let ((b* (cg-pop cg))) 4218 (cg-push cg a*) 4219 (cg-push cg b*)))))))) 4220 4221 ;; -------------------------------------------------------------------- 4222 ;; Operators 4223 ;; -------------------------------------------------------------------- 4224 (define (%cg-emit-rrr cg op rd ra rb) 4225 (%cg-emit-many cg (list "%" op "(" (%cg-reg->bv rd) ", " 4226 (%cg-reg->bv ra) ", " (%cg-reg->bv rb) ")\n"))) 4227 4228 (define (%cg-emit-cmp cg cc ra rb rd) 4229 (%cg-emit-many cg (list "%cmpset_" cc "(" 4230 (%cg-reg->bv rd) ", " 4231 (%cg-reg->bv ra) ", " (%cg-reg->bv rb) 4232 ")\n"))) 4233 4234 (define (cg-binop cg op) 4235 (let* ((b (cg-pop cg)) 4236 (a (cg-pop cg)) 4237 (ta (opnd-type a)) 4238 (tb (opnd-type b)) 4239 (unsigned? (or (%ctype-unsigned? ta) (%ctype-unsigned? tb))) 4240 (a-ptr? (%ctype-ptr? ta)) 4241 (b-ptr? (%ctype-ptr? tb)) 4242 (result-ty 4243 (cond 4244 ((or (eq? op 'eq) (eq? op 'ne) 4245 (eq? op 'lt) (eq? op 'le) (eq? op 'gt) (eq? op 'ge)) 4246 %t-i32) 4247 ((and a-ptr? b-ptr? (eq? op 'sub)) %t-word-i) 4248 (a-ptr? ta) 4249 (b-ptr? tb) 4250 (else ta)))) 4251 (cond 4252 ((%ctype-wide-int? ta) 4253 (%cg-load-wide-opnd-into cg a 'a0 'a1) 4254 (cond 4255 ((or (eq? op 'shl) (eq? op 'shr)) 4256 (cond 4257 ((%ctype-wide-int? tb) 4258 (%cg-load-wide-opnd-into cg b 'a2 'a3)) 4259 (else (%cg-load-opnd-into cg b 'a2))) 4260 (%cg-emit-many 4261 cg 4262 (list (cond ((eq? op 'shl) "%i64_shl(") 4263 ((%ctype-unsigned? ta) "%i64_shr(") 4264 (else "%i64_sar(")) 4265 "t0, t1, a0, a1, a2, t2)\n")) 4266 (%cg-spill-pair cg 't0 't1 result-ty)) 4267 (else 4268 (%cg-load-wide-opnd-into cg b 'a2 'a3) 4269 (cond 4270 ((eq? op 'add) 4271 (%cg-emit-many cg 4272 (list "%i64_add(t0, t1, a0, a1, a2, a3, t2)\n"))) 4273 ((eq? op 'sub) 4274 (%cg-emit-many cg 4275 (list "%i64_sub(t0, t1, a0, a1, a2, a3, t2)\n"))) 4276 ((eq? op 'mul) 4277 (%cg-emit-many cg 4278 (list "%i64_mul(t0, t1, a0, a1, a2, a3, t2)\n"))) 4279 ((eq? op 'and) 4280 (%cg-emit-rrr cg "and" 't0 'a0 'a2) 4281 (%cg-emit-rrr cg "and" 't1 'a1 'a3)) 4282 ((eq? op 'or) 4283 (%cg-emit-rrr cg "or" 't0 'a0 'a2) 4284 (%cg-emit-rrr cg "or" 't1 'a1 'a3)) 4285 ((eq? op 'xor) 4286 (%cg-emit-rrr cg "xor" 't0 'a0 'a2) 4287 (%cg-emit-rrr cg "xor" 't1 'a1 'a3)) 4288 ((or (eq? op 'div) (eq? op 'rem)) 4289 (%cg-emit-many 4290 cg 4291 (list "%call(&" 4292 (if (%ctype-unsigned? ta) 4293 "p1_i64_udivmod" "p1_i64_divmod") 4294 ")\n"))) 4295 ((or (eq? op 'eq) (eq? op 'ne) 4296 (eq? op 'lt) (eq? op 'le) (eq? op 'gt) (eq? op 'ge)) 4297 (%cg-emit-many 4298 cg 4299 (list "%i64_cmpset_" 4300 (cond ((eq? op 'eq) "eq") 4301 ((eq? op 'ne) "ne") 4302 ((eq? op 'lt) (if unsigned? "ltu" "lt")) 4303 ((eq? op 'le) (if unsigned? "leu" "le")) 4304 ((eq? op 'gt) (if unsigned? "gtu" "gt")) 4305 (else (if unsigned? "geu" "ge"))) 4306 "(t0, a0, a1, a2, a3, t1)\n"))) 4307 (else (die #f "cg-binop: unknown wide op" op))) 4308 (cond 4309 ((or (eq? op 'eq) (eq? op 'ne) 4310 (eq? op 'lt) (eq? op 'le) (eq? op 'gt) (eq? op 'ge)) 4311 (%cg-spill-reg cg 't0 %t-i32)) 4312 ((eq? op 'div) 4313 (%cg-spill-pair cg 'a0 'a1 result-ty)) 4314 ((eq? op 'rem) 4315 (%cg-spill-pair cg 'a2 'a3 result-ty)) 4316 (else (%cg-spill-pair cg 't0 't1 result-ty)))))) 4317 ((and a-ptr? (or (eq? op 'add) (eq? op 'sub)) (not b-ptr?)) 4318 (%cg-load-opnd-into cg a 'a0) 4319 (%cg-load-opnd-into cg b 'a1) 4320 (let ((sz (ctype-size (%ctype-pointee ta))) 4321 (mac (if (eq? op 'add) "%ptr_add(" "%ptr_sub("))) 4322 (%cg-emit-many cg (list mac "t0, a0, a1, " (%n sz) ", t1)\n"))) 4323 (%cg-spill-reg cg 't0 result-ty)) 4324 ((and b-ptr? (eq? op 'add) (not a-ptr?)) 4325 (%cg-load-opnd-into cg a 'a0) 4326 (%cg-load-opnd-into cg b 'a1) 4327 (let ((sz (ctype-size (%ctype-pointee tb)))) 4328 (%cg-emit-many cg (list "%ptr_add(t0, a1, a0, " (%n sz) ", t1)\n"))) 4329 (%cg-spill-reg cg 't0 result-ty)) 4330 ((and a-ptr? b-ptr? (eq? op 'sub)) 4331 (%cg-load-opnd-into cg a 'a0) 4332 (%cg-load-opnd-into cg b 'a1) 4333 (let ((sz (ctype-size (%ctype-pointee ta)))) 4334 (%cg-emit-many cg (list "%ptr_diff(t0, a0, a1, " (%n sz) ", t1)\n"))) 4335 (%cg-spill-reg cg 't0 result-ty)) 4336 (else 4337 (%cg-load-opnd-into cg a 'a0) 4338 (%cg-load-opnd-into cg b 'a1) 4339 (cond 4340 ((eq? op 'add) (%cg-emit-rrr cg "add" 't0 'a0 'a1)) 4341 ((eq? op 'sub) (%cg-emit-rrr cg "sub" 't0 'a0 'a1)) 4342 ((eq? op 'mul) (%cg-emit-rrr cg "mul" 't0 'a0 'a1)) 4343 ((eq? op 'and) (%cg-emit-rrr cg "and" 't0 'a0 'a1)) 4344 ((eq? op 'or) (%cg-emit-rrr cg "or" 't0 'a0 'a1)) 4345 ((eq? op 'xor) (%cg-emit-rrr cg "xor" 't0 'a0 'a1)) 4346 ((eq? op 'shl) (%cg-emit-rrr cg "shl" 't0 'a0 'a1)) 4347 ((eq? op 'shr) 4348 ;; Shift result type is the promoted LEFT operand's type 4349 ;; (C 6.5.7); arithmetic vs logical shift must follow that 4350 ;; signedness alone, not the rhs's. cg-arith-conv may have 4351 ;; relabeled ta to match an unsigned rhs — guard against 4352 ;; that by checking the original `a` opnd's signedness. 4353 (if (%ctype-unsigned? ta) 4354 (%cg-emit-rrr cg "shr" 't0 'a0 'a1) 4355 (%cg-emit-rrr cg "sar" 't0 'a0 'a1))) 4356 ((eq? op 'div) 4357 (%cg-emit-rrr cg (if unsigned? "udiv" "div") 't0 'a0 'a1)) 4358 ((eq? op 'rem) 4359 (%cg-emit-rrr cg (if unsigned? "urem" "rem") 't0 'a0 'a1)) 4360 ((eq? op 'eq) (%cg-emit-cmp cg "eq" 'a0 'a1 't0)) 4361 ((eq? op 'ne) (%cg-emit-cmp cg "ne" 'a0 'a1 't0)) 4362 ((eq? op 'lt) (%cg-emit-cmp cg (if unsigned? "ltu" "lt") 'a0 'a1 't0)) 4363 ((eq? op 'gt) (%cg-emit-cmp cg (if unsigned? "ltu" "lt") 'a1 'a0 't0)) 4364 ((eq? op 'le) (%cg-emit-cmp cg (if unsigned? "leu" "le") 'a0 'a1 't0)) 4365 ((eq? op 'ge) (%cg-emit-cmp cg (if unsigned? "geu" "ge") 'a0 'a1 't0)) 4366 (else (die #f "cg-binop: unknown op" op))) 4367 ;; Canonicalize narrow integer results to their type's bit width 4368 ;; before spilling, so the slot's bit-pattern matches result-ty. 4369 ;; Compare ops already yield 0/1; skip them. Pointer-arith branches 4370 ;; above don't reach here. 4371 (cond 4372 ((or (eq? op 'eq) (eq? op 'ne) 4373 (eq? op 'lt) (eq? op 'le) (eq? op 'gt) (eq? op 'ge)) 0) 4374 (else (%cg-canonicalize cg 't0 result-ty))) 4375 (%cg-spill-reg cg 't0 result-ty))))) 4376 4377 ;; Post-increment / post-decrement on the top-of-vstack lval. 4378 ;; Pushes the OLD value (per C semantics) and emits the +1 / -1 store. 4379 ;; Uses cg-dup + cg-load to capture the old rval (which is then in a 4380 ;; never-reused spill slot), then runs the regular dup+load+add+assign 4381 ;; pattern for the store. Pointer scaling falls out of cg-binop add. 4382 (define (%cg-post-inc-dec cg op) 4383 (cg-dup cg) 4384 (cg-load cg) 4385 (let ((old (cg-pop cg))) 4386 (cg-dup cg) 4387 (cg-load cg) 4388 (cg-push-imm cg %t-i32 1) 4389 (cg-binop cg op) 4390 (cg-assign cg) 4391 (cg-pop cg) 4392 (cg-push cg old))) 4393 4394 (define (cg-postinc cg) (%cg-post-inc-dec cg 'add)) 4395 (define (cg-postdec cg) (%cg-post-inc-dec cg 'sub)) 4396 4397 (define (cg-unop cg op) 4398 (let* ((p (cg-pop cg)) (ty (opnd-type p))) 4399 (cond 4400 ((%ctype-wide-int? ty) 4401 (%cg-load-wide-opnd-into cg p 'a0 'a1) 4402 (cond 4403 ((eq? op 'neg) 4404 (%cg-emit-many cg (list "%i64_neg(t0, t1, a0, a1, t2)\n")) 4405 (%cg-spill-pair cg 't0 't1 ty)) 4406 ((eq? op 'bnot) 4407 (%cg-emit-many cg (list "%bnot(t0, a0, t2)\n" 4408 "%bnot(t1, a1, t2)\n")) 4409 (%cg-spill-pair cg 't0 't1 ty)) 4410 ((eq? op 'lnot) 4411 (%cg-emit-rrr cg "or" 't0 'a0 'a1) 4412 (%cg-emit-many cg (list "%cmpset_eqz(t0, t0)\n")) 4413 (%cg-spill-reg cg 't0 %t-i32)) 4414 (else (die #f "cg-unop: unknown wide op" op)))) 4415 (else 4416 (%cg-load-opnd-into cg p 't0) 4417 (cond 4418 ((eq? op 'neg) 4419 (%cg-emit-many cg (list "%neg(t0, t0, t1)\n")) 4420 (%cg-spill-reg cg 't0 ty)) 4421 ((eq? op 'bnot) 4422 (%cg-emit-many cg (list "%bnot(t0, t0, t1)\n")) 4423 (%cg-spill-reg cg 't0 ty)) 4424 ((eq? op 'lnot) 4425 (%cg-emit-many cg (list "%cmpset_eqz(t0, t0)\n")) 4426 (%cg-spill-reg cg 't0 %t-i32)) 4427 (else (die #f "cg-unop: unknown op" op))))))) 4428 4429 (define (cg-assign cg) 4430 ;; Pops rhs, pops lhs, casts rhs to lhs's type (parser cannot peek 4431 ;; deeper than vstack top to do this itself), emits the store, pushes 4432 ;; the assigned value as the result rval. 4433 (let* ((rhs0 (cg-pop cg)) 4434 (lhs (cg-pop cg)) 4435 (ty (opnd-type lhs))) 4436 (cond ((not (opnd-lval? lhs)) (die #f "cg-assign: lhs not lvalue"))) 4437 ;; Cast rhs to lhs's type (no-op when the types already match). 4438 (cg-push cg rhs0) 4439 (cg-cast cg ty) 4440 (let ((rhs (cg-pop cg))) 4441 (cond 4442 ((%ctype-wide-int? ty) 4443 (%cg-load-wide-opnd-into cg rhs 'a0 'a1) 4444 (%cg-store-pair-to-lval cg 'a0 'a1 lhs) 4445 (%cg-spill-pair cg 'a0 'a1 ty)) 4446 (else 4447 (%cg-load-opnd-into cg rhs 'a0) 4448 (pmatch lhs 4449 (($ opnd? (kind frame) (ext ,off)) 4450 (guard (%cg-indirect? cg off)) 4451 (%cg-emit-ld-slot cg 't0 off) 4452 (%cg-emit-st-typed cg 'a0 ty 't0 0)) 4453 (($ opnd? (kind frame) (ext ,off)) 4454 (%cg-emit-st-slot-typed cg 'a0 ty off)) 4455 (($ opnd? (kind global) (ext ,lbl)) 4456 (%cg-emit-la cg 't0 lbl) 4457 (%cg-emit-st-typed cg 'a0 ty 't0 0)) 4458 (else (die #f "cg-assign: unsupported lhs kind" (opnd-kind lhs)))) 4459 (%cg-spill-reg cg 'a0 ty)))))) 4460 4461 ;; -------------------------------------------------------------------- 4462 ;; Calls 4463 ;; -------------------------------------------------------------------- 4464 (define (cg-call cg arity has-result?) 4465 (let* ((args (let loop ((i 0) (acc '())) 4466 (cond ((= i arity) acc) 4467 (else (loop (+ i 1) (cons (cg-pop cg) acc)))))) 4468 (fn-op (cg-pop cg)) 4469 ;; sret = struct/union wider than two target words; shift args by one reg 4470 ;; and place a0 last so it's not clobbered by arg loads. 4471 (fty (opnd-type fn-op)) 4472 (rty (cond 4473 ((eq? (ctype-kind fty) 'fn) (car (ctype-ext fty))) 4474 ((eq? (ctype-kind fty) 'ptr) 4475 (let ((p (ctype-ext fty))) 4476 (if (eq? (ctype-kind p) 'fn) (car (ctype-ext p)) %t-word-i))) 4477 (else %t-word-i))) 4478 (rk (ctype-kind rty)) 4479 (sret? (and has-result? 4480 (or (eq? rk 'struct) (eq? rk 'union)) 4481 (> (ctype-size rty) %CC-PAIR-BYTES))) 4482 ;; If the callee is variadic, the callee's save area caps total 4483 ;; incoming ABI words. Reject silent miscompiles up front. 4484 (callee-fty (cond 4485 ((eq? (ctype-kind fty) 'fn) fty) 4486 ((and (eq? (ctype-kind fty) 'ptr) 4487 (eq? (ctype-kind (ctype-ext fty)) 'fn)) 4488 (ctype-ext fty)) 4489 (else #f))) 4490 (callee-variadic? (and callee-fty 4491 (let ((ext (ctype-ext callee-fty))) 4492 (and (pair? ext) (pair? (cdr ext)) 4493 (pair? (cddr ext)) 4494 (car (cddr ext)))))) 4495 (arg-slots 4496 (let count ((xs args) (n 0)) 4497 (cond ((null? xs) n) 4498 (else (count (cdr xs) 4499 (+ n (%cg-param-reg-count 4500 (opnd-type (car xs))))))))) 4501 (_cap-check (cond 4502 ((and callee-variadic? 4503 (> arg-slots %CG-VARARG-WINDOW)) 4504 (die #f "cg-call: variadic call exceeds save-area" 4505 arg-slots %CG-VARARG-WINDOW)) 4506 (else 0))) 4507 (sret-shift (if sret? 1 0)) 4508 (recv-slot (cond 4509 (sret? 4510 (cg-alloc-slot cg 4511 (align-up (ctype-size rty) %CC-WORD-BYTES) 4512 (max %CC-WORD-BYTES (ctype-align rty)))) 4513 (else #f)))) 4514 ;; Copy every indirect aggregate before loading any ABI argument register: 4515 ;; %memcpy_call uses a0-a2, so interleaving a later copy with register 4516 ;; staging would clobber earlier arguments. The parallel list records the 4517 ;; private frame slot for each large aggregate and #f for direct args. 4518 (let ((indirect-copies 4519 (let materialize ((xs args) (out '())) 4520 (cond 4521 ((null? xs) (reverse out)) 4522 (else 4523 (let* ((arg (car xs)) 4524 (aty (opnd-type arg))) 4525 (cond 4526 ((%cg-param-indirect? aty) 4527 (let* ((sz (ctype-size aty)) 4528 (al (max %CC-WORD-BYTES (ctype-align aty))) 4529 (slot (cg-alloc-slot 4530 cg (align-up sz %CC-WORD-BYTES) al))) 4531 (%cg-emit-addr-of cg arg 't0) 4532 (%cg-emit-lea-slot cg "t2" (%cg-slot-expr cg slot)) 4533 (%cg-emit-byte-copy cg 't2 't0 't1 sz) 4534 (materialize (cdr xs) (cons slot out)))) 4535 (else 4536 (materialize (cdr xs) (cons #f out)))))))))) 4537 (let stage ((xs args) (copies indirect-copies) (idx 0)) 4538 (cond 4539 ((null? xs) 0) 4540 (else 4541 (let* ((arg (car xs)) 4542 (aty (opnd-type arg)) 4543 (n (%cg-param-reg-count aty))) 4544 (cond 4545 ;; Large aggregate: pass the address of the private copy through 4546 ;; one pointer ABI slot. The callee marks its pointer slot 4547 ;; indirect, so ordinary struct lvalue operations address it. 4548 ((%cg-param-indirect? aty) 4549 (let ((slot (car copies)) 4550 (abi (+ idx sret-shift))) 4551 (cond 4552 ((< abi 4) 4553 (%cg-emit-lea-slot cg 4554 (%cg-reg->bv (%reg-by-idx abi)) 4555 (%cg-slot-expr cg slot))) 4556 (else 4557 (%cg-emit-lea-slot cg "t0" (%cg-slot-expr cg slot)) 4558 (%cg-emit-st cg 't0 'sp 4559 (* %CC-WORD-BYTES (- abi 4))))) 4560 (stage (cdr xs) (cdr copies) (+ idx 1)))) 4561 ;; RV32 i64/u64 values consume two consecutive ABI words. 4562 ((%ctype-wide-int? aty) 4563 (%cg-load-wide-opnd-into cg arg 't0 't1) 4564 (let chunk ((i 0)) 4565 (cond 4566 ((= i 2) 0) 4567 (else 4568 (let ((tabi (+ idx sret-shift i)) 4569 (src (if (= i 0) 't0 't1))) 4570 (cond 4571 ((< tabi 4) 4572 (%cg-emit-many 4573 cg 4574 (list "%mov(" 4575 (%cg-reg->bv (%reg-by-idx tabi)) ", " 4576 (%cg-reg->bv src) ")\n"))) 4577 (else 4578 (%cg-emit-st cg src 'sp 4579 (* %CC-WORD-BYTES (- tabi 4))))) 4580 (chunk (+ i 1)))))) 4581 (stage (cdr xs) (cdr copies) (+ idx 2))) 4582 ;; Direct aggregate: load its target-word chunks into successive 4583 ;; arg regs / stack slots. This path also handles a one-word 4584 ;; aggregate: unlike the generic scalar load below, addr-of knows 4585 ;; when an aggregate lvalue is represented by an indirect frame 4586 ;; slot (for example `p->loc`) and follows that slot before the 4587 ;; chunk load. 4588 ((%cg-param-aggregate? aty) 4589 (%cg-emit-addr-of cg arg 't0) 4590 (let chunk ((i 0)) 4591 (cond 4592 ((>= i n) 0) 4593 (else 4594 (let ((tabi (+ idx sret-shift i))) 4595 (cond 4596 ((< tabi 4) 4597 (%cg-emit-many cg 4598 (list "%ld(" 4599 (%cg-reg->bv (%reg-by-idx tabi)) 4600 ", t0, " 4601 (%n (* i %CC-WORD-BYTES)) ")\n"))) 4602 (else 4603 (%cg-emit-many cg 4604 (list "%ld(t1, t0, " 4605 (%n (* i %CC-WORD-BYTES)) ")\n")) 4606 (%cg-emit-st cg 't1 'sp 4607 (* %CC-WORD-BYTES (- tabi 4)))))) 4608 (chunk (+ i 1))))) 4609 (stage (cdr xs) (cdr copies) (+ idx n))) 4610 (else 4611 (let ((abi (+ idx sret-shift))) 4612 (cond 4613 ((< abi 4) 4614 (%cg-load-opnd-into cg arg (%reg-by-idx abi)) 4615 (stage (cdr xs) (cdr copies) (+ idx 1))) 4616 (else 4617 (%cg-load-opnd-into cg arg 't0) 4618 (%cg-emit-st cg 't0 'sp 4619 (* %CC-WORD-BYTES (- abi 4))) 4620 (stage (cdr xs) (cdr copies) (+ idx 1)))))))))))) 4621 ;; Stack-arg footprint accounts for the extra ABI slot any 4622 ;; >8B-aggregate arg consumed beyond its single-position cousin. 4623 (let* ((nabi (let count ((xs args) (n sret-shift)) 4624 (cond ((null? xs) n) 4625 (else (count (cdr xs) 4626 (+ n (%cg-param-reg-count 4627 (opnd-type (car xs))))))))) 4628 (sa (max 0 (- nabi 4)))) 4629 (cond ((> sa 0) (%cg-bump-outgoing! cg sa)) (else 0))) 4630 (cond 4631 (sret? 4632 (%cg-emit-lea-slot cg "a0" (%cg-slot-expr cg recv-slot)))) 4633 (cond 4634 ((and (eq? (opnd-kind fn-op) 'global) (not (opnd-lval? fn-op))) 4635 (%cg-emit-many cg (list "%call(&" (opnd-ext fn-op) ")\n"))) 4636 (else 4637 (%cg-load-opnd-into cg fn-op 't0) 4638 (%cg-emit-many cg (list "%callr(t0)\n")))) 4639 (cond 4640 (has-result? 4641 (cond 4642 ((%ctype-wide-int? rty) 4643 (%cg-spill-pair cg 'a0 'a1 rty)) 4644 ;; Wider-than-two-word sret (A2): a0 holds recv-slot; push as struct lval. 4645 (sret? (cg-push cg (%opnd 'frame rty recv-slot #t))) 4646 ;; At-most-two-word struct/union (A1): fresh slot, spill from a0/a1. 4647 ((and (or (eq? rk 'struct) (eq? rk 'union)) 4648 (<= (ctype-size rty) %CC-PAIR-BYTES)) 4649 (let* ((sz (ctype-size rty)) 4650 (al (max %CC-WORD-BYTES (ctype-align rty))) 4651 (slot (cg-alloc-slot cg 4652 (align-up sz %CC-WORD-BYTES) al))) 4653 (%cg-emit-st-slot cg 'a0 slot) 4654 (cond ((> sz %CC-WORD-BYTES) 4655 (%cg-emit-st-slot cg 'a1 (+ slot %CC-WORD-BYTES)))) 4656 (cg-push cg (%opnd 'frame rty slot #t)))) 4657 (else 4658 (%cg-spill-reg cg 'a0 rty)))) 4659 (else #f)))) 4660 4661 ;; -------------------------------------------------------------------- 4662 ;; Return 4663 ;; -------------------------------------------------------------------- 4664 (define (cg-return cg) 4665 (let* ((ret-slot (%cg-fn-get cg '%fn-ret-slot)) 4666 (ret-type (%cg-fn-get cg '%fn-ret-type)) 4667 (rk (ctype-kind ret-type)) 4668 (sret? (%cg-fn-get cg '%fn-sret?))) 4669 (cond 4670 ((eq? rk 'void) 4671 (%cg-emit-many cg (list "%b(&.ret)\n"))) 4672 ((or (eq? rk 'struct) (eq? rk 'union)) 4673 ;; struct-by-value: at most two words (A1) → ret-slot; wider 4674 ;; (A2 sret) → *sret-slot. 4675 (let* ((p (cg-pop cg)) (sz (ctype-size ret-type))) 4676 (cond ((not (opnd-lval? p)) 4677 (die #f "cg-return: struct value must be an lvalue"))) 4678 (%cg-emit-addr-of cg p 't0) 4679 (cond 4680 (sret? 4681 (%cg-emit-ld-slot cg 't2 (%cg-fn-get cg '%fn-sret-slot))) 4682 (else 4683 (%cg-emit-lea-slot cg "t2" (%cg-slot-expr cg ret-slot)))) 4684 (%cg-emit-byte-copy cg 't2 't0 't1 sz) 4685 (%cg-emit-many cg (list "%b(&.ret)\n")))) 4686 ((%ctype-wide-int? ret-type) 4687 (let ((p (cg-pop cg))) 4688 (%cg-load-wide-opnd-into cg p 'a0 'a1) 4689 (%cg-emit-st-slot cg 'a0 ret-slot) 4690 (%cg-emit-st-slot cg 'a1 (+ ret-slot %CC-WORD-BYTES)) 4691 (%cg-emit-many cg (list "%b(&.ret)\n")))) 4692 (else 4693 (let ((p (cg-pop cg))) 4694 (%cg-load-opnd-into cg p 'a0) 4695 (%cg-emit-st-slot cg 'a0 ret-slot) 4696 (%cg-emit-many cg (list "%b(&.ret)\n"))))))) 4697 4698 ;; -------------------------------------------------------------------- 4699 ;; Structured control flow 4700 ;; -------------------------------------------------------------------- 4701 (define (cg-if cg then-thunk) 4702 (let ((p (cg-pop cg))) 4703 (%cg-load-truth-into cg p 't0) 4704 (%cg-emit-many cg (list "%if_nez(t0, {\n")) 4705 (then-thunk) 4706 (%cg-emit-many cg (list "})\n")))) 4707 4708 (define (cg-ifelse cg then-thunk else-thunk) 4709 (let ((p (cg-pop cg))) 4710 (%cg-load-truth-into cg p 't0) 4711 (%cg-emit-many cg (list "%ifelse_nez(t0, {\n")) 4712 (then-thunk) 4713 (%cg-emit-many cg (list "}, {\n")) 4714 (else-thunk) 4715 (%cg-emit-many cg (list "})\n")))) 4716 4717 ;; Conditionals-as-values: `cg-ifelse` is correct for if-statements 4718 ;; (thunks push nothing) but each thunk for ternary / `&&` / `||` ends 4719 ;; with one rval on top of the vstack — and after both branches run, 4720 ;; we'd be left with TWO opnds, which breaks the type contract for 4721 ;; the surrounding expression. `cg-ifelse-merge` solves that: pop the 4722 ;; cond, allocate one result slot, and after each thunk runs, pop its 4723 ;; rval and store into the slot. Push the slot as one frame rval. 4724 ;; 4725 ;; Result type follows C11 §6.5.15 ¶5 for ternary: the usual arithmetic 4726 ;; conversions over the two arms' types. The slot stores the raw target-word 4727 ;; payload (per cc.scm's canonical-form discipline); %cg-load-opnd-into 4728 ;; then re-canonicalizes on read against whatever common type we picked. 4729 ;; For `&&` / `||` callers both arms are pre-cast to %t-i32 by the 4730 ;; parser, so the merge is a no-op on type. 4731 (define (cg-ifelse-merge cg then-thunk else-thunk) 4732 (let* ((cond-op (cg-pop cg))) 4733 (%cg-load-truth-into cg cond-op 't0) 4734 (%cg-emit-many cg (list "%ifelse_nez(t0, {\n")) 4735 (then-thunk) 4736 (let* ((p (cg-pop cg)) 4737 (rty1 (opnd-type p)) 4738 (rk1 (ctype-kind rty1)) 4739 ;; Struct/union arms can't ride the canonical target-word 4740 ;; slot — the arm's bytes have to land in a slot sized to 4741 ;; the struct, and each arm memcpys its lvalue in. tcc's 4742 ;; expr_cond does this exact `type = bt1 == 6 ? type1 : type2` 4743 ;; pattern across CType structs, so without this case 4744 ;; cc.scm-compiled tcc-boot2 self-corrupts. 4745 (aggr? (or (eq? rk1 'struct) (eq? rk1 'union))) 4746 ;; On RV32 reserve both words for scalar merges. The second arm 4747 ;; can widen the common type to i64/u64 after the first arm has 4748 ;; already been emitted. 4749 (pair-slot? (and (not aggr?) (= %CC-WORD-BYTES 4))) 4750 (slot (cond (aggr? 4751 (cg-alloc-slot cg 4752 (align-up (ctype-size rty1) 4753 %CC-WORD-BYTES) 4754 (max %CC-WORD-BYTES 4755 (ctype-align rty1)))) 4756 (pair-slot? 4757 (cg-alloc-slot cg 4758 %CC-PAIR-BYTES %CC-WORD-BYTES)) 4759 (else 4760 (cg-alloc-slot cg 4761 %CC-WORD-BYTES %CC-WORD-BYTES))))) 4762 (%cg-merge-write-arm cg p slot aggr? pair-slot?) 4763 (%cg-emit-many cg (list "}, {\n")) 4764 (else-thunk) 4765 (let* ((q (cg-pop cg)) 4766 (rty2 (opnd-type q))) 4767 (%cg-merge-write-arm cg q slot aggr? pair-slot?) 4768 (%cg-emit-many cg (list "})\n")) 4769 ;; Aggregate result is pushed as a frame lval so cg-copy-struct 4770 ;; (which asserts src must be lval) accepts it; %cg-emit-addr-of 4771 ;; falls through the `lval? #t` guard (slot is direct, not 4772 ;; indirect) and returns the slot's address either way. 4773 (cg-push cg (%opnd 'frame 4774 (%cg-merge-arith-type rty1 rty2) 4775 slot 4776 aggr?)))))) 4777 4778 (define (%cg-merge-write-arm cg op slot aggr? pair-slot?) 4779 (cond 4780 (aggr? 4781 (%cg-emit-addr-of cg op 't0) 4782 (%cg-emit-lea-slot cg "t2" (%cg-slot-expr cg slot)) 4783 (%cg-emit-byte-copy cg 't2 't0 't1 (ctype-size (opnd-type op)))) 4784 ((and pair-slot? (%ctype-wide-int? (opnd-type op))) 4785 (%cg-load-wide-opnd-into cg op 'a0 'a1) 4786 (%cg-emit-st-slot cg 'a0 slot) 4787 (%cg-emit-st-slot cg 'a1 (+ slot %CC-WORD-BYTES))) 4788 (pair-slot? 4789 (%cg-load-opnd-into cg op 'a0) 4790 (cond 4791 ((%ctype-unsigned? (opnd-type op)) 4792 (%cg-emit-many cg (list "%li(a1, 0)\n"))) 4793 (else 4794 (%cg-emit-many cg 4795 (list "%sari(a1, a0, " 4796 (%n (- %CC-WORD-BITS 1)) ")\n")))) 4797 (%cg-emit-st-slot cg 'a0 slot) 4798 (%cg-emit-st-slot cg 'a1 (+ slot %CC-WORD-BYTES))) 4799 (else 4800 (%cg-load-opnd-into cg op 'a0) 4801 (%cg-emit-st-slot cg 'a0 slot)))) 4802 4803 ;; Conditional-expression common type (C11 §6.5.15): preserve a pointer 4804 ;; operand regardless of arm order (the other valid mixed operand is a null 4805 ;; pointer constant), otherwise apply the usual arithmetic conversions. 4806 ;; Aggregate conditionals retain the first arm's type as before. 4807 (define (%cg-merge-arith-type t1 t2) 4808 (cond 4809 ((%ctype-ptr? t1) t1) 4810 ((%ctype-ptr? t2) t2) 4811 ((and (%ctype-arith? t1) (%ctype-arith? t2)) 4812 (let ((p1 (cond ((< (ctype-size t1) 4) %t-i32) (else t1))) 4813 (p2 (cond ((< (ctype-size t2) 4) %t-i32) (else t2)))) 4814 (cond 4815 ((> (ctype-size p1) (ctype-size p2)) p1) 4816 ((> (ctype-size p2) (ctype-size p1)) p2) 4817 ((%ctype-unsigned? p1) p1) 4818 ((%ctype-unsigned? p2) p2) 4819 (else p1)))) 4820 (else t1))) 4821 4822 (define (cg-loop cg head-thunk body-thunk) 4823 ;; body-thunk receives the loop tag as its argument; parser uses 4824 ;; that tag for cg-break / cg-continue inside the body. 4825 (let ((tag (%cg-fresh-loop-tag cg))) 4826 (%cg-emit-many cg (list ".scope\n" 4827 ":.top\n")) 4828 (head-thunk) 4829 (cond 4830 ((zero? (cg-depth cg)) 0) 4831 (else 4832 (let ((c (cg-pop cg))) 4833 (%cg-load-truth-into cg c 't0) 4834 (%cg-emit-many cg (list "%if_eqz(t0, { %break })\n"))))) 4835 (body-thunk tag) 4836 (%cg-emit-many cg (list "%b(&.top)\n" 4837 ":.end\n" 4838 ".endscope\n")) 4839 tag)) 4840 4841 (define (cg-break cg tag) 4842 (%cg-emit-many cg (list "%break\n"))) 4843 4844 (define (cg-continue cg tag) 4845 (%cg-emit-many cg (list "%continue\n"))) 4846 4847 ;; -------------------------------------------------------------------- 4848 ;; Variadic receive (§G.2). Layout: cg-fn-begin/v reserves a fixed 4849 ;; target-word save area at known frame offsets, populating each 4850 ;; slot from the appropriate ABI source — a-register for indices 0..3, 4851 ;; LDARG for later indices. va_start sets ap to the address of the 4852 ;; first slot past the named-arg count; va_arg reads *ap, advances ap 4853 ;; by one target word, and pushes the value as the requested type. 4854 ;; 4855 ;; ap is an lval (typically a `va_list` local). cg-va-start pops it, 4856 ;; computes the address, stores into *ap (or the slot directly), and 4857 ;; pushes nothing. cg-va-arg pops ap-lval, loads ap, dereferences for 4858 ;; the value, advances ap, stores back, pushes the loaded value. 4859 ;; 4860 ;; Cap: total incoming ABI words (named + variadic) must fit in the 4861 ;; %CG-VARARG-WINDOW-slot save area. Variadic call sites exceeding this die in cg-call; 4862 ;; variadic definitions whose named-arg count exceeds it die in 4863 ;; cg-fn-begin/v. 4864 ;; -------------------------------------------------------------------- 4865 (define (%cg-vararg-first-slot cg) 4866 (let ((s (%cg-fn-get cg '%fn-vararg-first-slot))) 4867 (cond ((not s) (die #f "cg-va-start: not a variadic function")) 4868 (else s)))) 4869 4870 (define (cg-va-start cg) 4871 ;; Pop ap-lval. Materialize "&sp + vararg-first-slot" into a0, 4872 ;; store through ap-lval. Pushes nothing. 4873 (let* ((ap-lv (cg-pop cg)) 4874 (vsl (%cg-vararg-first-slot cg))) 4875 (cond ((not (opnd-lval? ap-lv)) 4876 (die #f "cg-va-start: ap not lvalue"))) 4877 (%cg-emit-lea-slot cg "a0" (%cg-slot-expr cg vsl)) 4878 (%cg-emit-addr-of cg ap-lv 't0) 4879 (%cg-emit-st cg 'a0 't0 0))) 4880 4881 (define (cg-va-arg cg ctype) 4882 ;; Pop ap-lval. Load ap into a0. Read one word at [a0] into a1. 4883 ;; Advance a0 by one word and store back through ap-lval. Push a1 as rval 4884 ;; of type ctype (caller cg-cast's if needed). 4885 (let ((ap-lv (cg-pop cg))) 4886 (cond ((not (opnd-lval? ap-lv)) 4887 (die #f "cg-va-arg: ap not lvalue"))) 4888 ;; Address of the storage that holds ap → t0; ap value → a0. 4889 (%cg-emit-addr-of cg ap-lv 't0) 4890 (%cg-emit-ld cg 'a0 't0 0) 4891 ;; Read one or two ABI words, advance ap by the consumed width, and 4892 ;; store the updated cursor back through the va_list lvalue. 4893 (%cg-emit-ld cg 'a1 'a0 0) 4894 (cond 4895 ((%ctype-wide-int? ctype) 4896 (%cg-emit-ld cg 'a2 'a0 %CC-WORD-BYTES) 4897 (%cg-emit-many cg 4898 (list "%addi(a0, a0, " 4899 (%n %CC-PAIR-BYTES) ")\n")) 4900 (%cg-emit-st cg 'a0 't0 0) 4901 (%cg-spill-pair cg 'a1 'a2 ctype)) 4902 (else 4903 (%cg-emit-many cg 4904 (list "%addi(a0, a0, " 4905 (%n %CC-WORD-BYTES) ")\n")) 4906 (%cg-emit-st cg 'a0 't0 0) 4907 ;; Spill the loaded value (a1) to a fresh frame slot under ctype. 4908 (%cg-spill-reg cg 'a1 ctype))))) 4909 4910 (define (cg-va-end cg) 4911 ;; va_end is a no-op in this design. Pop and discard ap-lval. 4912 (cg-pop cg) 4913 0) 4914 4915 ;; -------------------------------------------------------------------- 4916 ;; Labels and unconditional goto. 4917 ;; C labels have function scope, even when the labelled statement appears 4918 ;; inside a nested block/loop. Emit them as function-qualified global 4919 ;; labels rather than dotted hex2++ locals, because dotted definitions 4920 ;; inside a nested `.scope` would be invisible to gotos outside it. 4921 ;; -------------------------------------------------------------------- 4922 (define (%cg-user-label cg name-bv) 4923 (let ((fn (%cg-fn-get cg '%fn-label))) 4924 (bv-cat (list fn "__user_" name-bv)))) 4925 4926 (define (cg-emit-label cg name-bv) 4927 (%cg-emit-many cg (list ":" (%cg-user-label cg name-bv) "\n"))) 4928 4929 (define (cg-goto cg name-bv) 4930 (%cg-emit-many cg (list "%b(&" (%cg-user-label cg name-bv) ")\n"))) 4931 4932 ;; -------------------------------------------------------------------- 4933 ;; switch 4934 ;; -------------------------------------------------------------------- 4935 (define-record-type swctx 4936 (%swctx ctrl-slot ctrl-type end-tag default-lbl) 4937 swctx? 4938 (ctrl-slot swctx-ctrl-slot) 4939 (ctrl-type swctx-ctrl-type) 4940 (end-tag swctx-end-tag) 4941 (default-lbl swctx-default-lbl swctx-default-lbl-set!)) 4942 4943 (define (cg-switch-begin cg) 4944 (let* ((p (cg-pop cg)) 4945 (ty (opnd-type p)) 4946 (wide? (%ctype-wide-int? ty)) 4947 (off (cg-alloc-slot cg 4948 (if wide? %CC-PAIR-BYTES %CC-WORD-BYTES) 4949 %CC-WORD-BYTES)) 4950 (tag (%cg-fresh-loop-tag cg)) 4951 (disp-lbl (bytevector-append "sw_disp_" tag))) 4952 (cond 4953 (wide? 4954 (%cg-load-wide-opnd-into cg p 't0 't1) 4955 (%cg-emit-st-slot cg 't0 off) 4956 (%cg-emit-st-slot cg 't1 (+ off %CC-WORD-BYTES))) 4957 (else 4958 (%cg-load-opnd-into cg p 't0) 4959 (%cg-emit-st-slot cg 't0 off))) 4960 (%cg-emit-many cg (list ".scope\n" 4961 "%b(&." disp-lbl ")\n")) 4962 (%swctx off ty tag #f))) 4963 4964 (define (cg-switch-case cg sw const-int) 4965 (let* ((lbl (%cg-fresh-lbl cg)) 4966 (key (string->symbol 4967 (bytevector-append "%sw_cases__" (swctx-end-tag sw)))) 4968 (cur (or (%cg-fn-get cg key) '())) 4969 (entry (cons const-int lbl))) 4970 (%cg-fn-set! cg key (cons entry cur)) 4971 (%cg-emit-many cg (list ":." lbl "\n")))) 4972 4973 (define (cg-switch-default cg sw) 4974 (let ((lbl (%cg-fresh-lbl cg))) 4975 (swctx-default-lbl-set! sw lbl) 4976 (%cg-emit-many cg (list ":." lbl "\n")))) 4977 4978 (define (cg-switch-end cg sw) 4979 (let* ((tag (swctx-end-tag sw)) 4980 (key (string->symbol (bytevector-append "%sw_cases__" tag))) 4981 (cases (reverse (or (%cg-fn-get cg key) '()))) 4982 (default-lbl (swctx-default-lbl sw)) 4983 (disp-lbl (bytevector-append "sw_disp_" tag))) 4984 (%cg-emit-many cg (list "%break\n" 4985 ":." disp-lbl "\n")) 4986 (cond 4987 ((%ctype-wide-int? (swctx-ctrl-type sw)) 4988 (%cg-emit-ld-slot cg 't0 (swctx-ctrl-slot sw)) 4989 (%cg-emit-ld-slot cg 't1 4990 (+ (swctx-ctrl-slot sw) %CC-WORD-BYTES)) 4991 (for-each 4992 (lambda (c) 4993 (%cg-emit-li-wide cg 'a0 'a1 (car c)) 4994 (%cg-emit-many 4995 cg 4996 (list "%i64_cmpset_eq(t2, t0, t1, a0, a1, a2)\n" 4997 "%bnez(t2, &." (cdr c) ")\n"))) 4998 cases)) 4999 (else 5000 (%cg-emit-many cg (list "%ld(t0, sp, " 5001 (%cg-slot-expr cg (swctx-ctrl-slot sw)) ")\n")) 5002 (for-each 5003 (lambda (c) 5004 (%cg-emit-many cg (list "%switch_case(t0, t1, " 5005 (%n (car c)) ", &." (cdr c) ")\n"))) 5006 cases))) 5007 (cond 5008 (default-lbl (%cg-emit-many cg (list "%b(&." default-lbl ")\n"))) 5009 (else 0)) 5010 (%cg-emit-many cg (list "%break\n" 5011 ":.end\n" 5012 ".endscope\n")))) 5013 5014 ;; -------------------------------------------------------------------- 5015 ;; Globals and data 5016 ;; -------------------------------------------------------------------- 5017 ;; cg-emit-global: emit a global symbol into either .data (initialized) 5018 ;; or .bss (zero-init). 5019 ;; 5020 ;; init can be: 5021 ;; #f — zero-init in .bss (size from sym's ctype). 5022 ;; (piece ...) — initialized in .data; pieces concatenated. 5023 ;; 5024 ;; Each piece is either: 5025 ;; <bytevector> — raw bytes; emitted as bare hex chunks 5026 ;; (64 bytes / 128 hex chars per line). 5027 ;; (label-ref . <label-bv>) — target-word pointer slot containing &label; 5028 ;; (`&label` on RV32; `&label %(0)` on LP64). 5029 (define (%cg-init-piece->bv piece) 5030 (cond 5031 ((bytes? piece) 5032 (bv-cat (%cg-bv->hex-lines piece #f))) 5033 ((and (pair? piece) (eq? (car piece) 'label-ref)) 5034 (bv-cat (list "&" (cdr piece) 5035 (if (= %CC-WORD-BYTES 4) "\n" " %(0)\n")))) 5036 (else (die #f "cg-emit-global: bad init piece" piece)))) 5037 5038 (define (cg-emit-global cg sym init) 5039 (let* ((lbl (%cg-sym-label cg sym)) 5040 (sz (ctype-size (sym-type sym))) 5041 (size (if (< sz 0) %CC-WORD-BYTES sz)) 5042 (al (max 1 (ctype-align (sym-type sym))))) 5043 (cond 5044 (init 5045 (buf-push! (cg-data cg) (bv-cat (list "\n.align " (%n al) "\n:" 5046 lbl "\n"))) 5047 (let walk ((ps init)) 5048 (cond 5049 ((null? ps) 0) 5050 (else 5051 (buf-push! (cg-data cg) (%cg-init-piece->bv (car ps))) 5052 (walk (cdr ps)))))) 5053 (else 5054 (buf-push! (cg-bss cg) 5055 (bv-cat (list "\n.align " (%n al) "\n:" lbl "\n" 5056 (let zero-loop ((rem size) (acc '())) 5057 (cond 5058 ((<= rem 0) (bv-cat (reverse acc))) 5059 ((>= rem 8) 5060 (zero-loop (- rem 8) (cons "$(0)\n" acc))) 5061 (else 5062 (zero-loop (- rem 1) (cons "!(0)\n" acc)))))))))) 5063 0)) 5064 5065 (define (cg-emit-extern cg sym) 0) 5066 5067 ;; Record `n` as a tentative file-scope definition: don't emit BSS yet, 5068 ;; but if no full definition appears by end of TU, cg-finish will emit 5069 ;; zero-init storage for it. The pair in world-tentatives holds the names in 5070 ;; its car and a membership hash in its cdr, avoiding a quadratic `member` 5071 ;; scan across translation units with many tentative declarations. 5072 (define (cg-add-tentative! cg n) 5073 (let* ((w (cg-world cg)) 5074 (pending (world-tentatives w)) 5075 (seen (cdr pending))) 5076 (cond 5077 ((%hash-ref seen n) #t) 5078 (else 5079 (%hash-set! seen n #t) 5080 (set-car! pending (cons n (car pending))))))) 5081 5082 ;; End-of-TU pass: for each pending tentative, look up the latest sym 5083 ;; binding. If it's still `defined?=#f`, no real definition replaced it, 5084 ;; so emit zero-init storage now. Otherwise the .data emission already 5085 ;; covered it. 5086 (define (cg-flush-tentatives! cg) 5087 (let* ((w (cg-world cg)) 5088 (top (car (world-scope w)))) 5089 (for-each 5090 (lambda (n) 5091 (let ((sm (%hash-ref top n))) 5092 (cond 5093 ((and sm 5094 (eq? (sym-kind sm) 'var) 5095 (not (sym-defined? sm))) 5096 (cg-emit-global cg sm #f))))) 5097 (car (world-tentatives w))))) 5098 5099 (define (cg-intern-string cg bv-content) 5100 (let ((p (%hash-ref (cg-str-pool cg) bv-content))) 5101 (cond 5102 (p p) 5103 (else 5104 (let* ((n (%hash-size (cg-str-pool cg))) 5105 (lbl (bytevector-append 5106 (cg-str-prefix cg) "cc__str_" (%n n)))) 5107 (%hash-set! (cg-str-pool cg) bv-content lbl) 5108 (buf-push! (cg-data cg) 5109 (bv-cat (append (list "\n.align " (%n %CG-STR-ALIGN) 5110 "\n:" lbl "\n") 5111 (%cg-bv->hex-lines bv-content #t) 5112 (list ".align " (%n %CG-STR-ALIGN) "\n")))) 5113 lbl))))) 5114 5115 ;; Mint a fresh, never-recurring label for an unnamed file-scope 5116 ;; compound literal. Mirrors cg-intern-string's namer pattern (prefix + 5117 ;; "cc__cl_" + N), with N drawn from cg-label-ctr — the same monotonic 5118 ;; counter the per-fn label minters use. Different prefix → no collision 5119 ;; with `Lcc__N` / `lbl_N`. 5120 (define (%cg-fresh-cl-label cg) 5121 (let* ((n (cg-label-ctr cg)) 5122 (lbl (bytevector-append (cg-str-prefix cg) "cc__cl_" (%n n)))) 5123 (cg-label-ctr-set! cg (+ n 1)) 5124 lbl)) 5125 5126 ;; Render BV's bytes as bare hex accepted directly by hex2++. Lines are 5127 ;; chunked to ≤128 hex chars (= 64 bytes) to keep generated P1pp readable. 5128 ;; 5129 ;; If TRAILING-NUL? is #t, an extra 0x00 byte is appended to terminate 5130 ;; a C string. Alignment is emitted explicitly by callers with .align 5131 ;; so hex2++ owns padding instead of cc.scm manufacturing zero bytes. 5132 ;; The other caller (%cg-init-piece->bv) emits arbitrary initializer 5133 ;; bytes whose length is sized exactly to the C-visible field; padding a 5134 ;; 4-byte int slot to 8 would shift every following struct field. 5135 ;; Returns a list of bytevectors ready for bv-cat. 5136 (define %CG-HEX-CHUNK-BYTES 64) 5137 (define %CG-STR-ALIGN 8) 5138 5139 (define (%cg-bv->hex-lines bv trailing-nul?) 5140 (let* ((len (bytevector-length bv)) 5141 (logical (cond (trailing-nul? (+ len 1)) (else len))) 5142 (total logical)) 5143 (cond 5144 ((= total 0) '()) 5145 (else 5146 (let loop ((i 0) (acc '())) 5147 (cond 5148 ((>= i total) (reverse acc)) 5149 (else 5150 (let ((end (cond ((< (+ i %CG-HEX-CHUNK-BYTES) total) 5151 (+ i %CG-HEX-CHUNK-BYTES)) 5152 (else total)))) 5153 (loop end (cons (%cg-hex-line bv i end len) acc)))))))))) 5154 5155 ;; One `XXXX...XX\n` line covering BV bytes [START, END). Indices 5156 ;; >= LEN render as 0x00 (used for the trailing NUL terminator). 5157 (define (%cg-hex-line bv start end len) 5158 (let* ((nbytes (- end start)) 5159 (out (make-bytevector (+ (* 2 nbytes) 1)))) 5160 (let loop ((j start) (k 0)) 5161 (cond 5162 ((= j end) 5163 (bytevector-u8-set! out k (char->integer #\newline)) 5164 out) 5165 (else 5166 (let ((b (cond ((< j len) (bytevector-u8-ref bv j)) 5167 (else 0)))) 5168 (bytevector-u8-set! out k (%cg-hex-digit 5169 (arithmetic-shift b -4))) 5170 (bytevector-u8-set! out (+ k 1) (%cg-hex-digit (bit-and b 15))) 5171 (loop (+ j 1) (+ k 2)))))))) 5172 5173 (define (%cg-hex-digit n) 5174 (cond ((< n 10) (+ n (char->integer #\0))) 5175 (else (+ (- n 10) (char->integer #\A))))) 5176 5177 ;; -------------------------------------------------------------------- 5178 ;; Frame 5179 ;; -------------------------------------------------------------------- 5180 (define (cg-alloc-slot cg bytes align) 5181 (let* ((aligned (align-up (cg-frame-hi cg) align)) 5182 (new-hi (+ aligned bytes))) 5183 (cg-frame-hi-set! cg new-hi) 5184 aligned)) 5185 ;; cc/parse.scm — recursive-descent + Pratt parser. Minimal scheme1. 5186 5187 (define (make-pstate iter cg) 5188 (%pstate iter (cg-world cg) '() #f cg)) 5189 5190 (define (peek ps) (iter-peek (ps-iter ps))) 5191 (define (peek2 ps) (iter-peek2 (ps-iter ps))) 5192 (define (advance ps) (iter-next (ps-iter ps))) 5193 (define (at-kw? ps s) 5194 (pmatch (peek ps) 5195 (($ tok? (kind KW) (value ,v)) (eq? v s)) 5196 (else #f))) 5197 (define (at-punct? ps s) 5198 (pmatch (peek ps) 5199 (($ tok? (kind PUNCT) (value ,v)) (eq? v s)) 5200 (else #f))) 5201 (define (expect-kw ps s) 5202 (let ((t (peek ps))) 5203 (pmatch t 5204 (($ tok? (kind KW) (value ,v)) (guard (eq? v s)) (advance ps)) 5205 (else (die (tok-loc t) "expected kw" s))))) 5206 (define (expect-punct ps s) 5207 (let ((t (peek ps))) 5208 (pmatch t 5209 (($ tok? (kind PUNCT) (value ,v)) (guard (eq? v s)) (advance ps)) 5210 (else (die (tok-loc t) "expected punct" s))))) 5211 5212 (define (scope-enter! ps) 5213 (ps-scope-set! ps (cons (%make-hash-table 16) (ps-scope ps))) 5214 (ps-tags-set! ps (cons (%make-hash-table 8) (ps-tags ps)))) 5215 (define (scope-leave! ps) 5216 (ps-scope-set! ps (cdr (ps-scope ps))) 5217 (ps-tags-set! ps (cdr (ps-tags ps)))) 5218 (define (ctype-compat? a b) 5219 (cond 5220 ((eq? a b) #t) 5221 ((not (eq? (ctype-kind a) (ctype-kind b))) #f) 5222 (else 5223 (let ((k (ctype-kind a))) 5224 (cond 5225 ((eq? k 'ptr) (ctype-compat? (ctype-ext a) (ctype-ext b))) 5226 ((eq? k 'arr) 5227 (let ((ea (ctype-ext a)) (eb (ctype-ext b))) 5228 (and (ctype-compat? (car ea) (car eb)) 5229 (or (= (cdr ea) (cdr eb)) 5230 (< (cdr ea) 0) (< (cdr eb) 0))))) 5231 ((eq? k 'fn) (%fn-ctype-compat? (ctype-ext a) (ctype-ext b))) 5232 ((or (eq? k 'struct) (eq? k 'union) (eq? k 'enum)) #f) 5233 (else #t)))))) 5234 5235 (define (%fn-ctype-compat? a b) 5236 (and (ctype-compat? (car a) (car b)) 5237 (eq? (car (cddr a)) (car (cddr b))) 5238 (%fn-params-compat? (cadr a) (cadr b)))) 5239 5240 (define (%fn-params-compat? pa pb) 5241 (cond 5242 ((and (null? pa) (null? pb)) #t) 5243 ((or (null? pa) (null? pb)) #f) 5244 ((ctype-compat? (cdar pa) (cdar pb)) 5245 (%fn-params-compat? (cdr pa) (cdr pb))) 5246 (else #f))) 5247 5248 (define (sym-merge old new) 5249 (cond 5250 ((not (eq? (sym-kind old) (sym-kind new))) 5251 (die #f "redecl: kind mismatch" (sym-name old))) 5252 ((not (ctype-compat? (sym-type old) (sym-type new))) 5253 (die #f "redecl: type mismatch" (sym-name old))) 5254 ((eq? (sym-kind old) 'typedef) old) 5255 ((eq? (sym-kind old) 'enum-const) 5256 (cond ((equal? (sym-slot old) (sym-slot new)) old) 5257 (else (die #f "enum-const redecl" (sym-name old))))) 5258 ((and (sym-defined? old) (sym-defined? new)) 5259 (die #f "redefinition" (sym-name old))) 5260 ;; Linkage inherits from the first declaration (C 6.2.2 ¶4): if a 5261 ;; later decl/def of the same identifier doesn't carry a storage 5262 ;; class, it picks up the prior one. tcc.c relies on this with 5263 ;; `static T f(); ... T f() {…}` — the prior `static` makes both 5264 ;; the decl and the def internal-linkage. Without this carry- 5265 ;; through cc.scm split them across two label namespaces. 5266 ((sym-defined? new) 5267 (cond 5268 ((eq? (sym-storage old) 'static) 5269 (%sym (sym-name new) (sym-kind new) 'static 5270 (sym-type new) (sym-slot new) #t)) 5271 (else new))) 5272 (else old))) 5273 5274 (define (scope-bind! ps n s) 5275 (let* ((top (car (ps-scope ps))) 5276 (old (%hash-ref top n))) 5277 (cond 5278 ((not old) 5279 (%hash-set! top n s)) 5280 (else 5281 (let ((merged (sym-merge old s))) 5282 (cond 5283 ((eq? merged old) #t) 5284 (else (%hash-set! top n merged)))))))) 5285 (define (scope-lookup ps n) 5286 (let loop ((f (ps-scope ps))) 5287 (cond ((null? f) #f) 5288 (else 5289 (let ((v (%hash-ref (car f) n))) 5290 (if v v (loop (cdr f)))))))) 5291 (define (scope-lookup-current ps n) 5292 (%hash-ref (car (ps-scope ps)) n)) 5293 (define (tag-bind! ps n c) 5294 (%hash-set! (car (ps-tags ps)) n c)) 5295 (define (tag-lookup ps n) 5296 (let loop ((f (ps-tags ps))) 5297 (cond ((null? f) #f) 5298 (else (let ((v (%hash-ref (car f) n))) 5299 (if v v (loop (cdr f)))))))) 5300 (define (typedef? ps n) 5301 (let ((sm (scope-lookup ps n))) 5302 (and sm (eq? (sym-kind sm) 'typedef)))) 5303 5304 (define (%mk-ptr p) (%ctype 'ptr %CC-WORD-BYTES %CC-WORD-BYTES p)) 5305 (define (%mk-arr e n) 5306 (%ctype 'arr (if (< n 0) -1 (* n (ctype-size e))) 5307 (ctype-align e) (cons e n))) 5308 (define (%mk-fn r p v) (%ctype 'fn -1 -1 (list r p v))) 5309 (define (ctype-is-ptr? t) (eq? (ctype-kind t) 'ptr)) 5310 (define (ctype-is-fn? t) (eq? (ctype-kind t) 'fn)) 5311 (define (ctype-is-arr? t) (eq? (ctype-kind t) 'arr)) 5312 5313 (define (eat-cv-quals! ps) 5314 (cond ((at-kw? ps '__attribute__) 5315 (skip-gnu-attribute! ps) (eat-cv-quals! ps)) 5316 ((or (at-kw? ps 'const) (at-kw? ps 'volatile) 5317 (at-kw? ps 'restrict)) 5318 (advance ps) (eat-cv-quals! ps)) 5319 (else #t))) 5320 5321 ;; Consume a GNU `__attribute__ (( ... ))` spec and discard. The keyword 5322 ;; has been peeked but not yet consumed. tcc.c's prototypes use these 5323 ;; for noreturn / format / aligned annotations that the bootstrap doesn't 5324 ;; need to honour semantically — same softening pattern as floats and 5325 ;; rejected-but-accepted type specifiers. 5326 (define (skip-gnu-attribute! ps) 5327 (advance ps) 5328 (expect-punct ps 'lparen) 5329 (let loop ((depth 1)) 5330 (let ((t (peek ps))) 5331 (cond 5332 ((eq? (tok-kind t) 'EOF) 5333 (die (tok-loc t) "EOF in __attribute__")) 5334 ((and (eq? (tok-kind t) 'PUNCT) (eq? (tok-value t) 'lparen)) 5335 (advance ps) (loop (+ depth 1))) 5336 ((and (eq? (tok-kind t) 'PUNCT) (eq? (tok-value t) 'rparen)) 5337 (advance ps) 5338 (cond ((= depth 1) #t) 5339 (else (loop (- depth 1))))) 5340 (else (advance ps) (loop depth)))))) 5341 5342 (define (eat-gnu-attributes! ps) 5343 (cond ((at-kw? ps '__attribute__) 5344 (skip-gnu-attribute! ps) (eat-gnu-attributes! ps)) 5345 (else #t))) 5346 5347 ;; Parse the universally layout-neutral C11 alignment requests: zero (the 5348 ;; standard's no-effect spelling) and one/char alignment (which cannot weaken 5349 ;; any object's natural alignment). cc.scm does not carry a pending alignment 5350 ;; specifier into its declarator layout, so accepting larger values could 5351 ;; silently under-align an object; reject them instead. 5352 (define (skip-c11-alignas! ps) 5353 (expect-kw ps '_Alignas) 5354 (expect-punct ps 'lparen) 5355 (cond 5356 ((%const-tok-is-decl? ps) 5357 (let*-values (((_sto bty) (parse-decl-spec ps)) 5358 ((_n ty) (parse-declarator ps bty))) 5359 (let ((al (ctype-align ty))) 5360 (cond ((<= al 0) 5361 (die (tok-loc (peek ps)) "_Alignas of incomplete type")) 5362 ((> al 1) 5363 (die (tok-loc (peek ps)) 5364 "_Alignas over-alignment unsupported" al)))))) 5365 (else 5366 (let ((n (parse-const-int ps))) 5367 (cond ((or (< n 0) (> n 1)) 5368 (die (tok-loc (peek ps)) 5369 "_Alignas over-alignment unsupported" n)))))) 5370 (expect-punct ps 'rparen)) 5371 5372 (define (parse-decl-spec ps) 5373 (let loop ((sto #f) (sn #f) (lg 0) (b #f) (saw #f)) 5374 (let ((t (peek ps))) 5375 (cond 5376 ((at-kw? ps '__attribute__) 5377 (skip-gnu-attribute! ps) (loop sto sn lg b saw)) 5378 ((or (at-kw? ps 'auto) (at-kw? ps 'register)) 5379 (advance ps) (loop sto sn lg b #t)) 5380 ((at-kw? ps 'static) (advance ps) (loop 'static sn lg b #t)) 5381 ((at-kw? ps 'extern) (advance ps) (loop 'extern sn lg b #t)) 5382 ((at-kw? ps 'typedef) (advance ps) (loop 'typedef sn lg b #t)) 5383 ((or (at-kw? ps 'const) (at-kw? ps 'volatile) 5384 (at-kw? ps 'restrict) (at-kw? ps 'inline) 5385 (at-kw? ps '_Noreturn)) 5386 (advance ps) (loop sto sn lg b #t)) 5387 ((at-kw? ps '_Alignas) 5388 (skip-c11-alignas! ps) (loop sto sn lg b #t)) 5389 ((at-kw? ps 'signed) (advance ps) (loop sto 'signed lg b #t)) 5390 ((at-kw? ps 'unsigned) (advance ps) (loop sto 'unsigned lg b #t)) 5391 ((at-kw? ps 'short) (advance ps) (loop sto sn -1 b #t)) 5392 ((at-kw? ps 'long) (advance ps) (loop sto sn (+ lg 1) b #t)) 5393 ((at-kw? ps 'void) (advance ps) (loop sto sn lg 'void #t)) 5394 ((at-kw? ps 'char) (advance ps) (loop sto sn lg 'char #t)) 5395 ((at-kw? ps 'int) (advance ps) (loop sto sn lg 'int #t)) 5396 ((at-kw? ps '_Bool) (advance ps) (loop sto sn lg 'bool #t)) 5397 ;; Floats: parsed as type specifiers so prototypes and struct 5398 ;; layouts in the flattened TU don't trip the parser. The cg 5399 ;; rejects fp loads/arith/casts at use, see %cg-fp-reject!. 5400 ;; _Complex / _Imaginary are eaten silently — tcc.c only mentions 5401 ;; them inside HAVE_FLOAT-gated paths. 5402 ((at-kw? ps 'float) (advance ps) (loop sto sn lg 'float #t)) 5403 ((at-kw? ps 'double) (advance ps) (loop sto sn lg 'double #t)) 5404 ((or (at-kw? ps '_Complex) (at-kw? ps '_Imaginary)) 5405 (advance ps) (loop sto sn lg b #t)) 5406 ((or (at-kw? ps '_Atomic) (at-kw? ps '_Thread_local) 5407 (at-kw? ps '_Generic)) 5408 (die (tok-loc t) "rejected" (tok-value t))) 5409 ((at-kw? ps 'struct) 5410 (loop sto sn lg (parse-aggregate-spec ps 'struct) #t)) 5411 ((at-kw? ps 'union) 5412 (loop sto sn lg (parse-aggregate-spec ps 'union) #t)) 5413 ((at-kw? ps 'enum) 5414 (loop sto sn lg (parse-enum-spec ps) #t)) 5415 ;; __builtin_va_list — gcc/clang builtin type. We don't model 5416 ;; it as a struct; for our P1 ABI a va_list is just a char* 5417 ;; into the stack save area (cg-va-start/arg/end work over an 5418 ;; target-word slot). Letting __builtin_va_list mean `char *` here 5419 ;; lets a single header source — `typedef __builtin_va_list 5420 ;; va_list;` — compile cleanly under both cc.scm and stock 5421 ;; gcc/clang (where it's their native struct). 5422 ((and (not b) (eq? (tok-kind t) 'IDENT) 5423 (bv= (tok-value t) "__builtin_va_list")) 5424 (advance ps) 5425 (loop sto sn lg (%mk-ptr %t-i8) #t)) 5426 ((and (not b) (eq? (tok-kind t) 'IDENT) 5427 (let ((sm (scope-lookup ps (tok-value t)))) 5428 (and sm (eq? (sym-kind sm) 'typedef)))) 5429 (let* ((tk (advance ps)) (sm (scope-lookup ps (tok-value tk)))) 5430 (loop sto sn lg (sym-type sm) #t))) 5431 (else 5432 (cond ((not saw) (die (tok-loc t) "expected decl-spec" 5433 (tok-value t))) 5434 (else (values sto (resolve-base t sn lg b))))))))) 5435 5436 (define (resolve-base loc sn lg b) 5437 (cond 5438 ((eq? b 'void) 5439 (if (or sn (not (zero? lg))) (die loc "void+qual") %t-void)) 5440 ((eq? b 'bool) 5441 (if (or sn (not (zero? lg))) (die loc "bool+qual") %t-bool)) 5442 ((eq? b 'char) 5443 (cond ((eq? sn 'unsigned) %t-u8) (else %t-i8))) 5444 ((or (eq? b 'int) (and (not b) (or sn (not (zero? lg))))) 5445 (cond ((= lg -1) (if (eq? sn 'unsigned) %t-u16 %t-i16)) 5446 ((= lg 0) (if (eq? sn 'unsigned) %t-u32 %t-i32)) 5447 ((= lg 1) (if (eq? sn 'unsigned) %t-word-u %t-word-i)) 5448 (else (if (eq? sn 'unsigned) %t-u64 %t-i64)))) 5449 ((eq? b 'float) 5450 (if (or sn (not (zero? lg))) (die loc "float+qual") %t-flt)) 5451 ((eq? b 'double) 5452 (cond (sn (die loc "double+sign")) 5453 ((= lg 0) %t-dbl) 5454 ((= lg 1) %t-ldbl) 5455 (else (die loc "double+long*" lg)))) 5456 ((ctype? b) 5457 (if (or sn (not (zero? lg))) (die loc "type+qual") b)) 5458 (else (die loc "unknown decl-spec")))) 5459 5460 (define (parse-aggregate-spec ps kind) 5461 (advance ps) 5462 ;; GCC `__attribute__((...))` may sit between `struct/union` and 5463 ;; the tag/`{`. Eat and discard. 5464 (eat-gnu-attributes! ps) 5465 (let ((tag (pmatch (peek ps) 5466 (($ tok? (kind IDENT)) (tok-value (advance ps))) 5467 (else #f)))) 5468 (eat-gnu-attributes! ps) 5469 (cond 5470 ((at-punct? ps 'lbrace) 5471 (advance ps) 5472 ;; A `struct/union TAG { ... }` declaration introduces (or 5473 ;; completes) the tag in the *current* scope. Looking up in 5474 ;; outer scopes via tag-lookup would let an inner-scope 5475 ;; definition mutate an outer-scope same-tag ctype via 5476 ;; complete-agg!. Restrict the reuse to the top frame, and 5477 ;; only when the existing tag is still incomplete (size < 0); 5478 ;; otherwise this is an attempted redefinition. 5479 (let* ((ex (and tag (%hash-ref (car (ps-tags ps)) tag))) 5480 (ct (cond ((and ex (eq? (ctype-kind ex) kind) 5481 (< (ctype-size ex) 0)) ex) 5482 ((and ex (eq? (ctype-kind ex) kind)) 5483 (die (tok-loc (peek ps)) "agg redefinition" tag)) 5484 (else (let ((c (%ctype kind -1 -1 5485 (list (or tag #f) #f '())))) 5486 (if tag (tag-bind! ps tag c)) c)))) 5487 (fields (parse-struct-fields ps kind))) 5488 (expect-punct ps 'rbrace) 5489 (complete-agg! ct kind tag fields) ct)) 5490 (tag (let ((ex (tag-lookup ps tag))) 5491 (cond (ex ex) 5492 (else (let ((c (%ctype kind -1 -1 5493 (list tag #f '())))) 5494 (tag-bind! ps tag c) c))))) 5495 (else (die (tok-loc (peek ps)) "anon agg"))))) 5496 5497 (define (parse-struct-fields ps kind) 5498 ;; For unions, every field stays at offset 0; complete-agg! takes the 5499 ;; max of field sizes for the union's overall size. 5500 (let ((struct? (eq? kind 'struct))) 5501 (let loop ((acc '()) (off 0)) 5502 (cond 5503 ((at-punct? ps 'rbrace) (reverse acc)) 5504 ((at-kw? ps '_Static_assert) 5505 (parse-static-assert! ps) (loop acc off)) 5506 (else 5507 (let-values (((_sto bty) (parse-decl-spec ps))) 5508 (let dl ((acc2 acc) (o2 off)) 5509 (let*-values (((nm ty) (parse-declarator ps bty))) 5510 (let* ((al (max (ctype-align ty) 1)) 5511 (sz (ctype-size ty)) 5512 (oa (if struct? (align-up o2 al) 0)) 5513 (next (if struct? (+ oa (max sz 0)) 0))) 5514 (cond 5515 ((at-punct? ps 'comma) 5516 (advance ps) 5517 (dl (cons (list nm ty oa) acc2) next)) 5518 ((at-punct? ps 'semi) 5519 (advance ps) 5520 (loop (cons (list nm ty oa) acc2) next)) 5521 (else (die (tok-loc (peek ps)) "field")))))))))))) 5522 5523 (define (complete-agg! ct k tag fs) 5524 (let* ((ma (let m ((xs fs) (a 1)) 5525 (if (null? xs) a 5526 (m (cdr xs) (max a (ctype-align (cadr (car xs)))))))) 5527 (last (let l ((xs fs) (e 0)) 5528 (if (null? xs) e 5529 (let* ((f (car xs)) (off (car (cddr f))) 5530 (sz (ctype-size (cadr f)))) 5531 (l (cdr xs) (max e (+ off (max sz 0)))))))) 5532 (sz (cond ((eq? k 'union) 5533 (let u ((xs fs) (s 0)) 5534 (if (null? xs) s 5535 (u (cdr xs) 5536 (max s (ctype-size (cadr (car xs)))))))) 5537 (else (align-up last ma))))) 5538 (ctype-size-set! ct sz) 5539 (ctype-align-set! ct ma) 5540 (ctype-ext-set! ct (list tag #t fs)))) 5541 5542 (define (parse-enum-spec ps) 5543 (advance ps) 5544 (let ((tag (pmatch (peek ps) 5545 (($ tok? (kind IDENT)) (tok-value (advance ps))) 5546 (else #f)))) 5547 (cond 5548 ((at-punct? ps 'lbrace) 5549 (advance ps) 5550 ;; Parse all members first, then construct the enum ctype with 5551 ;; the final members list and tag-bind it. Members reference 5552 ;; earlier enum-consts via scope-lookup (not via the enum tag), 5553 ;; so deferring tag-bind! is safe. 5554 (let loop ((vs '()) (nv 0)) 5555 (cond 5556 ((at-punct? ps 'rbrace) 5557 (advance ps) 5558 (let ((ct (%ctype 'enum 4 4 (list tag (reverse vs))))) 5559 (if tag (tag-bind! ps tag ct)) 5560 ct)) 5561 (else 5562 (let* ((nt (advance ps)) (nm (tok-value nt)) 5563 (val (cond ((at-punct? ps 'assign) 5564 (advance ps) (parse-const-int ps)) 5565 (else nv)))) 5566 (scope-bind! ps nm 5567 (%sym nm 'enum-const #f %t-i32 val #t)) 5568 (cond ((at-punct? ps 'comma) (advance ps)) 5569 ((at-punct? ps 'rbrace) #t) 5570 (else (die (tok-loc (peek ps)) "enum"))) 5571 (loop (cons (cons nm val) vs) (%c-value-add val 1))))))) 5572 (tag (let ((e (tag-lookup ps tag))) 5573 (cond (e e) 5574 (else (let ((c (%ctype 'enum 4 4 (list tag '())))) 5575 (tag-bind! ps tag c) c))))) 5576 (else (die (tok-loc (peek ps)) "enum"))))) 5577 5578 ;; ==================================================================== 5579 ;; Integer constant expressions (C99 §6.6). 5580 ;; 5581 ;; parse-const-expr ps -> (value . ctype) 5582 ;; A self-contained walker that never touches cg. The four call sites 5583 ;; that demand an integer constant expression — array bounds, enum 5584 ;; initializers, case labels, file-scope/static initializers — all go 5585 ;; through here. Returns a (value . ctype) pair so a final cast can 5586 ;; truncate at the target type's width (e.g. `(int)(unsigned char)257` 5587 ;; needs the inner cast to mask off to u8 before the outer relabel). 5588 ;; 5589 ;; Operand surface: integer / character literals, enum constants, 5590 ;; sizeof(TYPENAME), unary + - ~ !, binary + - * / % << >> & | ^, 5591 ;; compare < <= > >= == !=, logical && || (short-circuit at the value 5592 ;; layer; both sides are still parsed so the token stream advances), 5593 ;; ternary ?:, cast to integer type, parenthesization. Anything else 5594 ;; dies. Floats / function calls / address-of / non-const idents / VLAs 5595 ;; are out of scope. 5596 ;; ==================================================================== 5597 5598 ;; Truncate VALUE to the width and signedness of CT. Integer types only 5599 ;; — pointer/array/etc. operands abort upstream. 5600 (define (%const-trunc value ct) 5601 (let* ((sz (ctype-size ct)) 5602 (k (ctype-kind ct))) 5603 (cond 5604 ;; bool: 0 or 1. 5605 ((eq? k 'bool) (if (%c-value-zero? value) 0 1)) 5606 ((<= sz 0) 0) 5607 (else (%c-value-trunc value sz (not (%ctype-unsigned? ct))))))) 5608 5609 ;; Usual arithmetic conversions on (value . ctype) pairs. Both operands 5610 ;; have already been integer-promoted (≤ int → int) by the caller. 5611 ;; Returns three values: truncated a, truncated b, and the shared result 5612 ;; ctype. 5613 (define (%const-arith-conv ap bp) 5614 (let* ((av (car ap)) (at (cdr ap)) 5615 (bv (car bp)) (bt (cdr bp)) 5616 (rt (%const-arith-conv-type at bt))) 5617 (values (%const-trunc av rt) (%const-trunc bv rt) rt))) 5618 5619 (define (%const-arith-conv-type at bt) 5620 ;; Pick the wider type; tie-break on unsigned. Caller has already 5621 ;; promoted both to >= int width. 5622 (let ((sa (ctype-size at)) (sb (ctype-size bt))) 5623 (cond 5624 ((> sa sb) at) 5625 ((> sb sa) bt) 5626 ((%ctype-unsigned? at) at) 5627 ((%ctype-unsigned? bt) bt) 5628 (else at)))) 5629 5630 (define (%const-promote vp) 5631 ;; Integer promotion (C11 §6.3.1.1): types narrower than int 5632 ;; (i8/u8/i16/u16/bool) widen to (signed) int — every value of an 5633 ;; unsigned sub-int type fits in int on this target, so the promotion 5634 ;; rank picks signed int, not unsigned int. This matters for the 5635 ;; usual arithmetic conversions in cross-signedness comparisons, 5636 ;; e.g. ((unsigned char)-1 < (int)-1) must promote LHS to int 255 5637 ;; (not u32 0xff) so the result is 0, not 1. 5638 (let* ((v (car vp)) (ct (cdr vp)) 5639 (sz (ctype-size ct))) 5640 (cond 5641 ((< sz 4) (cons (%const-trunc v %t-i32) %t-i32)) 5642 (else vp)))) 5643 5644 (define (%const-bool? vp) (not (%c-value-zero? (car vp)))) 5645 5646 (define (parse-const-expr ps) (parse-const-cond ps)) 5647 5648 ;; Ternary (right-associative). Per C11 §6.6 ¶3 + §6.5.15/4 only the 5649 ;; chosen branch is evaluated; the other need not be a valid constant 5650 ;; expression (e.g. `1 ? 2 : 1/0` must yield 2, not abort). The dead 5651 ;; arm is skipped via %const-skip-cond-{mid,rhs}, like the &&/|| 5652 ;; short-circuit paths above. 5653 (define (parse-const-cond ps) 5654 (let ((c (parse-const-lor ps))) 5655 (cond 5656 ((at-punct? ps 'qmark) 5657 (advance ps) 5658 (cond 5659 ((%const-bool? c) 5660 (let* ((t (parse-const-expr ps)) 5661 (_ (expect-punct ps 'colon))) 5662 (%const-skip-dead-arm ps) 5663 t)) 5664 (else 5665 (%const-skip-dead-arm ps) 5666 (expect-punct ps 'colon) 5667 (parse-const-cond ps)))) 5668 (else c)))) 5669 5670 ;; Generic top-level punct scanner used by skip-rhs / skip-cond helpers. 5671 ;; Walks paren/bracket depth (a closing bracket at d=0 always stops) and 5672 ;; optionally tracks ternary `?` depth. STOP? receives the punct value 5673 ;; v at top-level (d=0, q=0 when q-aware?) and returns #t to stop. With 5674 ;; Q-AWARE? = #t, a `?` at top-level opens a nested ternary and a 5675 ;; matching `:` (q>0) closes it; the scanner stops on `:` only when q=0. 5676 (define (%punct-scan ps stop? q-aware?) 5677 (let lp ((d 0) (q 0)) 5678 (let ((t (peek ps))) 5679 (cond 5680 ((eq? (tok-kind t) 'EOF) #t) 5681 ((not (eq? (tok-kind t) 'PUNCT)) 5682 (advance ps) (lp d q)) 5683 (else 5684 (let ((v (tok-value t))) 5685 (cond 5686 ((or (eq? v 'lparen) (eq? v 'lbrack)) 5687 (advance ps) (lp (+ d 1) q)) 5688 ((or (eq? v 'rparen) (eq? v 'rbrack)) 5689 (cond ((zero? d) #t) 5690 (else (advance ps) (lp (- d 1) q)))) 5691 ((and q-aware? (zero? d) (eq? v 'qmark)) 5692 (advance ps) (lp d (+ q 1))) 5693 ((and q-aware? (zero? d) (> q 0) (eq? v 'colon)) 5694 (advance ps) (lp d (- q 1))) 5695 ((and (zero? d) (or (not q-aware?) (zero? q)) (stop? v)) 5696 #t) 5697 (else (advance ps) (lp d q))))))))) 5698 5699 ;; Skip the dead arm of a ternary. Same scanner whether we're skipping 5700 ;; the middle (cond was false; will then expect-punct `:` and parse arm 5701 ;; 3) or the third (cond was true; arm 2 already parsed and `:` already 5702 ;; consumed). Both stop at top-level `:` / `,` / `;` / `}` with no 5703 ;; open inner `?`; nested `?:` pairs are absorbed. 5704 (define (%const-skip-dead-arm ps) 5705 (%punct-scan ps 5706 (lambda (v) 5707 (or (eq? v 'colon) (eq? v 'comma) (eq? v 'semi) (eq? v 'rbrace))) 5708 #t)) 5709 5710 ;; Generic left-associative binary level. 5711 ;; ops: alist of punct-sym → (vp vp → vp). 5712 (define (%const-binl ps next ops) 5713 (let lp ((a (next ps))) 5714 (let* ((t (peek ps)) 5715 (hit (and (eq? (tok-kind t) 'PUNCT) 5716 (alist-ref/eq (tok-value t) ops)))) 5717 (cond ((not hit) a) 5718 (else (advance ps) (lp (hit a (next ps)))))))) 5719 5720 ;; Arithmetic combiner: promote both, arith-conv, apply op, truncate. 5721 (define (%const-arith-op op a b) 5722 (let-values (((av bv rt) (%const-arith-conv (%const-promote a) (%const-promote b)))) 5723 (cons (%const-trunc 5724 (cond ((eq? op 'add) (%c-value-add av bv)) 5725 ((eq? op 'sub) (%c-value-sub av bv)) 5726 ((eq? op 'mul) (%c-value-mul av bv)) 5727 ((eq? op 'and) (%c-value-and av bv)) 5728 ((eq? op 'or) (%c-value-or av bv)) 5729 ((eq? op 'xor) (%c-value-xor av bv)) 5730 (else (die #f "const-expr: bad arithmetic op" op))) 5731 rt) 5732 rt))) 5733 5734 ;; Like %const-arith-op but rejects a zero divisor. 5735 (define (%const-div-op want-rem? a b) 5736 (let-values (((av bv rt) (%const-arith-conv (%const-promote a) (%const-promote b)))) 5737 (cond ((%c-value-zero? bv) (die #f "const-expr: divide by zero"))) 5738 (let ((qr (%c-value-divmod av bv (ctype-size rt) (%ctype-unsigned? rt)))) 5739 (cons (%const-trunc (if want-rem? (cdr qr) (car qr)) rt) rt)))) 5740 5741 ;; Comparison combiner: result is always (0-or-1 . %t-i32). 5742 (define (%const-cmp-op op a b) 5743 (let-values (((av bv rt) (%const-arith-conv (%const-promote a) (%const-promote b)))) 5744 (let ((c (%c-value-cmp av bv (ctype-size rt) (%ctype-unsigned? rt)))) 5745 (cons 5746 (if (cond ((eq? op 'eq) (= c 0)) 5747 ((eq? op 'ne) (not (= c 0))) 5748 ((eq? op 'lt) (< c 0)) 5749 ((eq? op 'le) (<= c 0)) 5750 ((eq? op 'gt) (> c 0)) 5751 ((eq? op 'ge) (>= c 0)) 5752 (else (die #f "const-expr: bad comparison op" op))) 5753 1 0) 5754 %t-i32)))) 5755 5756 ;; Short-circuit per C11 §6.5.13/14 ¶4: rhs is not evaluated when the 5757 ;; lhs determines the result. Required so `1 || (1/0)` and 5758 ;; `0 && (1/0)` yield 1/0 rather than aborting on divide-by-zero. 5759 (define (parse-const-lor ps) 5760 (let lp ((a (parse-const-land ps))) 5761 (cond 5762 ((at-punct? ps 'lor) 5763 (advance ps) 5764 (cond 5765 ((%const-bool? a) 5766 (%const-skip-lor-rhs ps) 5767 (lp (cons 1 %t-i32))) 5768 (else 5769 (let ((b (parse-const-land ps))) 5770 (lp (cons (if (%const-bool? b) 1 0) %t-i32)))))) 5771 (else a)))) 5772 5773 (define (parse-const-land ps) 5774 (let lp ((a (parse-const-bor ps))) 5775 (cond 5776 ((at-punct? ps 'land) 5777 (advance ps) 5778 (cond 5779 ((not (%const-bool? a)) 5780 (%const-skip-land-rhs ps) 5781 (lp (cons 0 %t-i32))) 5782 (else 5783 (let ((b (parse-const-bor ps))) 5784 (lp (cons (if (%const-bool? b) 1 0) %t-i32)))))) 5785 (else a)))) 5786 5787 ;; Skip the rhs of a short-circuited && / ||. The rhs grammar is 5788 ;; the operand level of the operator: parse-const-bor for &&, 5789 ;; parse-const-land for ||. We can't just call those parsers because 5790 ;; the rhs may itself be invalid as a constant expression (e.g. 5791 ;; `1/0`); instead, scan tokens at paren/brack depth 0 until we hit 5792 ;; another operator at the same-or-lower binding level, comma, 5793 ;; semicolon, colon, qmark, rbrace, rbrack, rparen, or EOF. 5794 (define (%const-skip-land-rhs ps) 5795 ;; rhs of && is a parse-const-bor — stop on `&&`, `||`, `?`, `:`, 5796 ;; `,`, `;`, `}`, and any closing/separator at depth 0. 5797 (%punct-scan ps 5798 (lambda (v) 5799 (or (eq? v 'land) (eq? v 'lor) (eq? v 'qmark) (eq? v 'colon) 5800 (eq? v 'comma) (eq? v 'semi) (eq? v 'rbrace))) 5801 #f)) 5802 (define (%const-skip-lor-rhs ps) 5803 ;; rhs of || is a parse-const-land — stop on `||` (left-assoc), 5804 ;; `?`, `:`, `,`, `;`, `}`. `&&` binds TIGHTER than `||`, so it is 5805 ;; absorbed into the rhs and we do NOT stop on it. 5806 (%punct-scan ps 5807 (lambda (v) 5808 (or (eq? v 'lor) (eq? v 'qmark) (eq? v 'colon) 5809 (eq? v 'comma) (eq? v 'semi) (eq? v 'rbrace))) 5810 #f)) 5811 5812 (define (parse-const-bor ps) 5813 (%const-binl ps parse-const-bxor (list (cons 'bar (lambda (a b) (%const-arith-op 'or a b)))))) 5814 (define (parse-const-bxor ps) 5815 (%const-binl ps parse-const-band (list (cons 'caret (lambda (a b) (%const-arith-op 'xor a b)))))) 5816 (define (parse-const-band ps) 5817 (%const-binl ps parse-const-eq (list (cons 'amp (lambda (a b) (%const-arith-op 'and a b)))))) 5818 5819 (define (parse-const-eq ps) 5820 (%const-binl ps parse-const-rel 5821 (list (cons 'eq2 (lambda (a b) (%const-cmp-op 'eq a b))) 5822 (cons 'ne (lambda (a b) (%const-cmp-op 'ne a b)))))) 5823 5824 (define (parse-const-rel ps) 5825 (%const-binl ps parse-const-shift 5826 (list (cons 'lt (lambda (a b) (%const-cmp-op 'lt a b))) 5827 (cons 'le (lambda (a b) (%const-cmp-op 'le a b))) 5828 (cons 'gt (lambda (a b) (%const-cmp-op 'gt a b))) 5829 (cons 'ge (lambda (a b) (%const-cmp-op 'ge a b)))))) 5830 5831 ;; Shift combiner: result type is the (promoted) lhs type — rhs is 5832 ;; just a count, promoted independently. SIGN selects shl (+1) or shr (-1). 5833 (define (%const-shift-op sign a b) 5834 (let* ((ap (%const-promote a)) 5835 (bp (%const-promote b)) 5836 (rt (cdr ap)) 5837 (count (%c-value->fixnum (car bp) "const-expr shift"))) 5838 (cons (%const-trunc 5839 (%c-value-shift (car ap) (* (- 0 sign) count) 5840 (and (= sign -1) (not (%ctype-unsigned? rt)))) 5841 rt) 5842 rt))) 5843 5844 (define (parse-const-shift ps) 5845 (%const-binl ps parse-const-add 5846 (list (cons 'shl (lambda (a b) (%const-shift-op 1 a b))) 5847 (cons 'shr (lambda (a b) (%const-shift-op -1 a b)))))) 5848 5849 (define (parse-const-add ps) 5850 (%const-binl ps parse-const-mul 5851 (list (cons 'plus (lambda (a b) (%const-arith-op 'add a b))) 5852 (cons 'minus (lambda (a b) (%const-arith-op 'sub a b)))))) 5853 5854 (define (parse-const-mul ps) 5855 (%const-binl ps parse-const-cast 5856 (list (cons 'star (lambda (a b) (%const-arith-op 'mul a b))) 5857 (cons 'slash (lambda (a b) (%const-div-op #f a b))) 5858 (cons 'pct (lambda (a b) (%const-div-op #t a b)))))) 5859 5860 (define (parse-const-cast ps) 5861 ;; (typename) operand — distinguished from ( expr ) by paren-is-group?. 5862 ;; Pointer casts are accepted only as a type re-tag — the integer 5863 ;; offset rides through unchanged. This is what the offsetof idiom 5864 ;; `(T *)0` and the outer `(size_t) <ptr-const>` need; we do not 5865 ;; admit general pointer arithmetic in const-expr. 5866 (cond 5867 ((at-punct? ps 'lparen) 5868 (cond 5869 ((%const-paren-is-cast? ps) 5870 (advance ps) 5871 (let*-values (((_sto bty) (parse-decl-spec ps)) 5872 ((_n ty) (parse-declarator ps bty))) 5873 (expect-punct ps 'rparen) 5874 (cond 5875 ((%ctype-int? ty) 5876 (let ((v (parse-const-cast ps))) 5877 (cons (%const-trunc (car v) ty) ty))) 5878 ((eq? (ctype-kind ty) 'ptr) 5879 (let ((v (parse-const-cast ps))) 5880 (cons (car v) ty))) 5881 (else 5882 (die (tok-loc (peek ps)) 5883 "const-expr: cast must be integer or pointer" 5884 (ctype-kind ty)))))) 5885 (else (parse-const-unary ps)))) 5886 (else (parse-const-unary ps)))) 5887 5888 (define (%const-paren-is-cast? ps) 5889 ;; A '(' starts a cast iff the following token kicks off a type-name. 5890 (%tok-decl-start? ps (peek2 ps))) 5891 5892 (define (%ctype-int? ty) 5893 (let ((k (ctype-kind ty))) 5894 (or (eq? k 'i8) (eq? k 'u8) (eq? k 'i16) (eq? k 'u16) 5895 (eq? k 'i32) (eq? k 'u32) (eq? k 'i64) (eq? k 'u64) 5896 (eq? k 'bool) (eq? k 'enum)))) 5897 5898 (define (parse-const-unary ps) 5899 (let ((t (peek ps))) 5900 (pmatch t 5901 (($ tok? (kind PUNCT) (value plus)) 5902 (advance ps) (%const-promote (parse-const-cast ps))) 5903 (($ tok? (kind PUNCT) (value minus)) 5904 (advance ps) 5905 (let* ((vp (%const-promote (parse-const-cast ps))) 5906 (rt (cdr vp))) 5907 (cons (%const-trunc (%c-value-negate (car vp)) rt) rt))) 5908 (($ tok? (kind PUNCT) (value tilde)) 5909 (advance ps) 5910 (let* ((vp (%const-promote (parse-const-cast ps))) 5911 (rt (cdr vp))) 5912 (cons (%const-trunc (%c-value-not (car vp)) rt) rt))) 5913 (($ tok? (kind PUNCT) (value bang)) 5914 (advance ps) 5915 (let ((vp (parse-const-cast ps))) 5916 (cons (if (%const-bool? vp) 0 1) %t-i32))) 5917 (($ tok? (kind PUNCT) (value amp)) 5918 ;; Address-of in const-expr context. Restricted to the offsetof 5919 ;; idiom: a null-pointer-typed base reached via (T *)0 (with 5920 ;; optional grouping/deref) followed by ->/. field selectors. 5921 ;; The integer value is the running byte offset; '&' wraps the 5922 ;; designator's type in a pointer for any outer integer cast to 5923 ;; consume. 5924 (advance ps) 5925 (let* ((dp (%const-parse-addrof-postfix ps))) 5926 (cons (car dp) (%mk-ptr (cdr dp))))) 5927 (($ tok? (kind KW) (value sizeof)) 5928 (advance ps) 5929 (cond 5930 ((at-punct? ps 'lparen) 5931 (advance ps) 5932 (cond 5933 ((%const-tok-is-decl? ps) 5934 (let*-values (((_sto bty) (parse-decl-spec ps)) 5935 ((_n ty) (parse-declarator ps bty))) 5936 (expect-punct ps 'rparen) 5937 (cons (max (ctype-size ty) 0) %t-word-u))) 5938 (else 5939 ;; sizeof(EXPR) in const-expr context. Operand is not 5940 ;; evaluated (C11 §6.5.3.4) — snapshot the cg, parse the 5941 ;; expr through the regular parser to recover its ctype, 5942 ;; then rewind to discard any emission/vstack pushes. 5943 (cons (%const-sizeof-expr ps #t) %t-word-u)))) 5944 (else 5945 ;; `sizeof EXPR` (no parens). Same no-eval rule. 5946 (cons (%const-sizeof-expr ps #f) %t-word-u)))) 5947 (($ tok? (kind KW) (value _Alignof)) 5948 (advance ps) 5949 (expect-punct ps 'lparen) 5950 (cond 5951 ((%const-tok-is-decl? ps) 5952 (let*-values (((_sto bty) (parse-decl-spec ps)) 5953 ((_n ty) (parse-declarator ps bty))) 5954 (expect-punct ps 'rparen) 5955 (cons (max (ctype-align ty) 1) %t-word-u))) 5956 (else 5957 (cons (%const-alignof-expr ps) %t-word-u)))) 5958 (else (parse-const-primary ps))))) 5959 5960 ;; Does TOK begin a type-name? Type specifiers, qualifiers, 5961 ;; struct/union/enum tags, and typedef-name idents. Storage classes 5962 ;; (auto/register/static/extern/typedef) are NOT included — those 5963 ;; appear only at declaration position; callers that need them 5964 ;; (e.g. stmt-starts-decl?) check separately. 5965 (define (%tok-decl-start? ps t) 5966 (pmatch t 5967 (($ tok? (kind KW) (value ,v)) 5968 (or (eq? v 'void) (eq? v 'char) (eq? v 'short) (eq? v 'int) 5969 (eq? v 'long) (eq? v 'signed) (eq? v 'unsigned) 5970 (eq? v '_Bool) (eq? v 'float) (eq? v 'double) 5971 (eq? v '_Complex) (eq? v '_Imaginary) 5972 (eq? v 'struct) (eq? v 'union) (eq? v 'enum) 5973 (eq? v 'const) (eq? v 'volatile) (eq? v 'restrict) 5974 (eq? v 'inline) (eq? v '_Noreturn))) 5975 (($ tok? (kind IDENT) (value ,n)) (typedef? ps n)) 5976 (else #f))) 5977 5978 (define (%const-tok-is-decl? ps) (%tok-decl-start? ps (peek ps))) 5979 5980 (define (%const-offsetof-field ps ty offset) 5981 (let ((nt (advance ps))) 5982 (cond ((not (eq? (tok-kind nt) 'IDENT)) 5983 (die (tok-loc nt) "__builtin_offsetof expects a member"))) 5984 (let ((f (%cg-find-field (%init-struct-fields ty) (tok-value nt)))) 5985 (cond ((not f) 5986 (die (tok-loc nt) "__builtin_offsetof: no such member" 5987 (tok-value nt)))) 5988 (cons (+ offset (car (cddr f))) (car (cdr f)))))) 5989 5990 (define (%const-builtin-offsetof ps) 5991 ;; `__builtin_offsetof(T, member[const].nested)` is an integer constant. 5992 ;; Reuse the aggregate field metadata and array element sizes already used by 5993 ;; normal designators; no pointer expression or code generation is needed. 5994 (expect-punct ps 'lparen) 5995 (let*-values (((_sto bty) (parse-decl-spec ps)) 5996 ((_n ty) (parse-declarator ps bty))) 5997 (expect-punct ps 'comma) 5998 (let lp ((p (%const-offsetof-field ps ty 0))) 5999 (pmatch (peek ps) 6000 (($ tok? (kind PUNCT) (value dot)) 6001 (advance ps) (lp (%const-offsetof-field ps (cdr p) (car p)))) 6002 (($ tok? (kind PUNCT) (value lbrack)) 6003 (advance ps) 6004 (let* ((index (parse-const-int ps)) 6005 (_ (expect-punct ps 'rbrack)) 6006 (aty (cdr p))) 6007 (cond ((not (eq? (ctype-kind aty) 'arr)) 6008 (die (tok-loc (peek ps)) 6009 "__builtin_offsetof: subscript on non-array"))) 6010 (let ((elem (car (ctype-ext aty)))) 6011 (lp (cons (+ (car p) (* index (ctype-size elem))) elem))))) 6012 (else 6013 (expect-punct ps 'rparen) 6014 (cons (car p) %t-word-u)))))) 6015 6016 (define (parse-const-primary ps) 6017 (let ((t (peek ps))) 6018 (pmatch t 6019 (($ tok? (kind INT) (value ,v)) 6020 (advance ps) 6021 (cons (%c-int-raw v) (%c-int-type v))) 6022 (($ tok? (kind CHAR) (value ,v)) 6023 (advance ps) 6024 ;; Character constants have type int in C. 6025 (cons v %t-i32)) 6026 (($ tok? (kind PUNCT) (value lparen)) 6027 (advance ps) 6028 (let ((v (parse-const-expr ps))) 6029 (expect-punct ps 'rparen) v)) 6030 (($ tok? (kind IDENT) (value ,n)) 6031 (cond 6032 ((bv= n "__builtin_offsetof") 6033 (advance ps) (%const-builtin-offsetof ps)) 6034 (else 6035 (let ((sm (scope-lookup ps n))) 6036 (cond ((and sm (eq? (sym-kind sm) 'enum-const)) 6037 (advance ps) (cons (sym-slot sm) %t-i32)) 6038 (else 6039 (die (tok-loc t) "const-expr: not a constant" n))))))) 6040 (else (die (tok-loc t) "const-expr: bad operand" 6041 (tok-value t)))))) 6042 6043 ;; ==================================================================== 6044 ;; offsetof support inside const-expr. 6045 ;; 6046 ;; Recognises `&((T *)0)->FIELD`, `&(*(T *)0).FIELD`, and chains thereof 6047 ;; — the only address-of idioms that show up in static initializers 6048 ;; (tcc.c options_W[] / options_f[] / options_m[] tables, and any 6049 ;; offsetof macro expansion of the same shape). Each helper threads a 6050 ;; (offset . ctype) pair: integer byte offset of the running designator 6051 ;; from the null base, plus the lvalue's ctype. Field lookup reuses 6052 ;; %cg-find-field, so anonymous union/struct members work the same way 6053 ;; as in regular field access. 6054 ;; ==================================================================== 6055 6056 (define (%const-parse-addrof-postfix ps) 6057 ;; postfix: primary ( -> FIELD | . FIELD )* 6058 (let lp ((p (%const-parse-addrof-primary ps))) 6059 (pmatch (peek ps) 6060 (($ tok? (kind PUNCT) (value arrow)) 6061 (advance ps) (lp (%const-addrof-arrow ps p))) 6062 (($ tok? (kind PUNCT) (value dot)) 6063 (advance ps) (lp (%const-addrof-dot ps p))) 6064 (else p)))) 6065 6066 (define (%const-parse-addrof-primary ps) 6067 ;; primary: ( T )expr ; pointer cast — the offsetof base 6068 ;; | ( postfix ) ; grouping 6069 ;; | * primary ; deref 6070 (cond 6071 ((at-punct? ps 'lparen) 6072 (cond 6073 ((%const-paren-is-cast? ps) 6074 (let ((cv (parse-const-cast ps))) 6075 (cond 6076 ((not (eq? (ctype-kind (cdr cv)) 'ptr)) 6077 (die #f "const-expr: addr-of: head must be a pointer cast" 6078 (ctype-kind (cdr cv))))) 6079 cv)) 6080 (else 6081 (advance ps) 6082 (let ((r (%const-parse-addrof-postfix ps))) 6083 (expect-punct ps 'rparen) r)))) 6084 ((at-punct? ps 'star) 6085 (advance ps) 6086 (let ((h (%const-parse-addrof-primary ps))) 6087 (cond 6088 ((not (eq? (ctype-kind (cdr h)) 'ptr)) 6089 (die #f "const-expr: addr-of: '*' on non-pointer" 6090 (ctype-kind (cdr h))))) 6091 (cons (car h) (ctype-ext (cdr h))))) 6092 (else 6093 (die (tok-loc (peek ps)) "const-expr: addr-of: unexpected token" 6094 (tok-value (peek ps)))))) 6095 6096 (define (%const-addrof-arrow ps p) 6097 (let* ((off (car p)) (ty (cdr p))) 6098 (cond ((not (eq? (ctype-kind ty) 'ptr)) 6099 (die (tok-loc (peek ps)) "const-expr: -> on non-pointer" 6100 (ctype-kind ty)))) 6101 (let* ((sty (ctype-ext ty)) (sk (ctype-kind sty))) 6102 (cond ((not (or (eq? sk 'struct) (eq? sk 'union))) 6103 (die (tok-loc (peek ps)) 6104 "const-expr: -> target not aggregate" sk))) 6105 (%const-addrof-field ps sty off)))) 6106 6107 (define (%const-addrof-dot ps p) 6108 (let* ((off (car p)) (ty (cdr p)) (k (ctype-kind ty))) 6109 (cond ((not (or (eq? k 'struct) (eq? k 'union))) 6110 (die (tok-loc (peek ps)) 6111 "const-expr: . on non-aggregate" k))) 6112 (%const-addrof-field ps ty off))) 6113 6114 (define (%const-addrof-field ps sty base-off) 6115 (let ((nt (peek ps))) 6116 (cond ((not (eq? (tok-kind nt) 'IDENT)) 6117 (die (tok-loc nt) 6118 "const-expr: field selector needs an identifier" 6119 (tok-value nt)))) 6120 (advance ps) 6121 (let* ((fields (car (cddr (ctype-ext sty)))) 6122 (f (%cg-find-field fields (tok-value nt)))) 6123 (cond ((not f) (die (tok-loc nt) 6124 "const-expr: no such field" 6125 (tok-value nt)))) 6126 (cons (+ base-off (car (cddr f))) (cadr f))))) 6127 6128 ;; sizeof EXPR / sizeof(EXPR) in const-expr context. Delegates to the 6129 ;; regular expression parser under a cg snapshot/rewind — same contract 6130 ;; as parse-unary's sizeof: the operand is parsed to learn its type but 6131 ;; not evaluated, so any emission or vstack push from the parse is 6132 ;; discarded. Returns the operand's byte size as a non-negative int. 6133 ;; If `paren?`, consumes the closing `)` after parsing. 6134 (define (%const-unevaluated-primary-type ps) 6135 ;; File-scope sizeof/_Alignof has no active function buffer to snapshot. 6136 ;; Cover the ordinary unevaluated-designator grammar directly so static 6137 ;; initializers such as `sizeof table / sizeof table[0]` remain constant 6138 ;; expressions without inventing a synthetic function. 6139 (let ((t (peek ps))) 6140 (pmatch t 6141 (($ tok? (kind IDENT) (value ,n)) 6142 (let ((sm (scope-lookup ps n))) 6143 (cond ((not sm) (die (tok-loc t) "unevaluated: undecl" n))) 6144 (advance ps) 6145 (sym-type sm))) 6146 (($ tok? (kind INT) (value ,v)) 6147 (advance ps) (%c-int-type v)) 6148 (($ tok? (kind CHAR)) (advance ps) %t-i32) 6149 (($ tok? (kind STR) (value ,v)) 6150 (advance ps) (%mk-arr %t-i8 (+ (bytevector-length v) 1))) 6151 (($ tok? (kind PUNCT) (value lparen)) 6152 (cond 6153 ((%const-paren-is-cast? ps) 6154 ;; In an unevaluated operand the cast expression still has to be 6155 ;; consumed, but only its result type matters. This covers the 6156 ;; standard `sizeof(((T *)0)->field)` spelling without asking for an 6157 ;; active function/codegen buffer at file scope. 6158 (advance ps) 6159 (let*-values (((_sto bty) (parse-decl-spec ps)) 6160 ((_n ty) (parse-declarator ps bty))) 6161 (expect-punct ps 'rparen) 6162 (%const-unevaluated-unary-type ps) 6163 ty)) 6164 (else 6165 (advance ps) 6166 (let ((ty (%const-unevaluated-unary-type ps))) 6167 (expect-punct ps 'rparen) 6168 ty)))) 6169 (else (die (tok-loc t) "unevaluated: unsupported operand" 6170 (tok-value t)))))) 6171 6172 (define (%const-unevaluated-postfix-type ps) 6173 (let lp ((ty (%const-unevaluated-primary-type ps))) 6174 (pmatch (peek ps) 6175 (($ tok? (kind PUNCT) (value lbrack)) 6176 (advance ps) 6177 ;; The index is itself unevaluated by sizeof, but parsing it as a 6178 ;; constant keeps token consumption deterministic for the bootstrap 6179 ;; subset (array tables conventionally use [0]). 6180 (parse-const-int ps) 6181 (expect-punct ps 'rbrack) 6182 (let ((k (ctype-kind ty))) 6183 (cond ((eq? k 'arr) (lp (car (ctype-ext ty)))) 6184 ((eq? k 'ptr) (lp (ctype-ext ty))) 6185 (else (die #f "unevaluated: subscript on non-array" k))))) 6186 (($ tok? (kind PUNCT) (value dot)) 6187 (advance ps) 6188 (let ((nt (peek ps))) 6189 (cond ((not (eq? (tok-kind nt) 'IDENT)) 6190 (die (tok-loc nt) "unevaluated: field"))) 6191 (advance ps) 6192 (let ((f (%cg-find-field (car (cddr (ctype-ext ty))) 6193 (tok-value nt)))) 6194 (cond ((not f) (die (tok-loc nt) "unevaluated: no field" 6195 (tok-value nt)))) 6196 (lp (cadr f))))) 6197 (($ tok? (kind PUNCT) (value arrow)) 6198 (advance ps) 6199 (cond ((not (eq? (ctype-kind ty) 'ptr)) 6200 (die #f "unevaluated: arrow on non-pointer"))) 6201 (let* ((sty (ctype-ext ty)) (nt (peek ps))) 6202 (cond ((not (eq? (tok-kind nt) 'IDENT)) 6203 (die (tok-loc nt) "unevaluated: field"))) 6204 (advance ps) 6205 (let ((f (%cg-find-field (car (cddr (ctype-ext sty))) 6206 (tok-value nt)))) 6207 (cond ((not f) (die (tok-loc nt) "unevaluated: no field" 6208 (tok-value nt)))) 6209 (lp (cadr f))))) 6210 (else ty)))) 6211 6212 (define (%const-unevaluated-unary-type ps) 6213 (pmatch (peek ps) 6214 (($ tok? (kind PUNCT) (value amp)) 6215 (advance ps) (%mk-ptr (%const-unevaluated-unary-type ps))) 6216 (($ tok? (kind PUNCT) (value star)) 6217 (advance ps) 6218 (let ((ty (%const-unevaluated-unary-type ps))) 6219 (cond ((not (eq? (ctype-kind ty) 'ptr)) 6220 (die #f "unevaluated: dereference of non-pointer"))) 6221 (ctype-ext ty))) 6222 (($ tok? (kind PUNCT) (value plus)) 6223 (advance ps) 6224 (cdr (%const-promote (cons 0 (%const-unevaluated-unary-type ps))))) 6225 (($ tok? (kind PUNCT) (value minus)) 6226 (advance ps) 6227 (cdr (%const-promote (cons 0 (%const-unevaluated-unary-type ps))))) 6228 (($ tok? (kind PUNCT) (value tilde)) 6229 (advance ps) 6230 (cdr (%const-promote (cons 0 (%const-unevaluated-unary-type ps))))) 6231 (($ tok? (kind PUNCT) (value bang)) 6232 (advance ps) (%const-unevaluated-unary-type ps) %t-i32) 6233 (else (%const-unevaluated-postfix-type ps)))) 6234 6235 (define (%const-sizeof-expr ps paren?) 6236 (cond 6237 ((not (ps-cg ps)) 6238 (die #f "#if: sizeof of expression not valid in preprocessor context")) 6239 ((cg-in-fn? (ps-cg ps)) 6240 (let ((tag (cg-snapshot (ps-cg ps)))) 6241 (cond (paren? (parse-expr ps) (expect-punct ps 'rparen)) 6242 (else (parse-unary ps))) 6243 (let* ((tp (cg-top (ps-cg ps))) 6244 (sz (max (ctype-size (opnd-type tp)) 0))) 6245 (cg-rewind (ps-cg ps) tag) 6246 sz))) 6247 (else 6248 (let ((ty (%const-unevaluated-unary-type ps))) 6249 (cond (paren? (expect-punct ps 'rparen))) 6250 (max (ctype-size ty) 0))))) 6251 6252 (define (%const-alignof-expr ps) 6253 (cond 6254 ((not (ps-cg ps)) 6255 (die #f "#if: _Alignof of expression not valid in preprocessor context")) 6256 ((not (cg-in-fn? (ps-cg ps))) 6257 (let ((ty (%const-unevaluated-unary-type ps))) 6258 (expect-punct ps 'rparen) 6259 (max (ctype-align ty) 1))) 6260 (else 6261 (let ((tag (cg-snapshot (ps-cg ps)))) 6262 (parse-expr ps) 6263 (expect-punct ps 'rparen) 6264 (let* ((tp (cg-top (ps-cg ps))) 6265 (al (max (ctype-align (opnd-type tp)) 1))) 6266 (cg-rewind (ps-cg ps) tag) 6267 al))))) 6268 6269 ;; Convenience: returns the integer value alone (callers that don't 6270 ;; need the type half of parse-const-expr's (value . ctype) result). 6271 (define (parse-const-int ps) (car (parse-const-expr ps))) 6272 6273 (define (parse-declarator ps base) 6274 ;; Returns (values name type). 6275 ((cdr (parse-decl-cont ps)) base 6276 (lambda (n t) (values n t)))) 6277 6278 (define (parse-decl-cont ps) 6279 (pmatch (peek ps) 6280 (($ tok? (kind KW) (value __attribute__)) 6281 (skip-gnu-attribute! ps) (parse-decl-cont ps)) 6282 (($ tok? (kind PUNCT) (value star)) 6283 (advance ps) (eat-cv-quals! ps) 6284 (let* ((r (parse-decl-cont ps)) (rf (cdr r))) 6285 (cons (car r) (lambda (b k) (rf (%mk-ptr b) k))))) 6286 (($ tok? (kind PUNCT) (value lparen)) 6287 (guard (paren-is-group? ps)) 6288 (advance ps) 6289 (let* ((i (parse-decl-cont ps)) (if- (cdr i))) 6290 (expect-punct ps 'rparen) 6291 (let ((s (parse-decl-suf-cont ps))) 6292 (cons (car i) (lambda (b k) (if- (s b) k)))))) 6293 (($ tok? (kind IDENT) (value ,n)) 6294 (advance ps) 6295 (let ((s (parse-decl-suf-cont ps))) 6296 (cons n (lambda (b k) (k n (s b)))))) 6297 (else 6298 (let ((s (parse-decl-suf-cont ps))) 6299 (cons #f (lambda (b k) (k #f (s b)))))))) 6300 6301 (define (parse-decl-suf-cont ps) 6302 ;; C declarator suffixes apply RIGHT-TO-LEFT (innermost first): 6303 ;; int a[2][3] ⇒ arr (arr int 3) 2 (outer dim 2) 6304 ;; not arr (arr int 2) 3 (which would treat the leftmost suffix as 6305 ;; outermost). The recursive structure builds the inner suffix's 6306 ;; result first, then this level wraps. 6307 (pmatch (peek ps) 6308 (($ tok? (kind PUNCT) (value lbrack)) 6309 (advance ps) 6310 ;; C99 §6.7.5.2 allows `static`, type qualifiers (const / 6311 ;; volatile / restrict), and `*` (variable length array 6312 ;; placeholder) inside array-of-T brackets in function 6313 ;; parameter declarators. We don't honour the qualifier 6314 ;; semantics — just consume them so the dimension expression 6315 ;; that follows parses. 6316 (let lp () 6317 (cond 6318 ((or (at-kw? ps 'const) (at-kw? ps 'volatile) 6319 (at-kw? ps 'restrict) (at-kw? ps 'static)) 6320 (advance ps) (lp)) 6321 (else #t))) 6322 (let* ((ln (cond ((at-punct? ps 'rbrack) -1) 6323 ((at-punct? ps 'star) (advance ps) -1) 6324 (else (parse-const-int ps)))) 6325 (_ (expect-punct ps 'rbrack)) 6326 (r (parse-decl-suf-cont ps))) 6327 (lambda (b) (%mk-arr (r b) ln)))) 6328 (($ tok? (kind PUNCT) (value lparen)) 6329 (advance ps) 6330 (let-values (((p v) (parse-fn-params ps))) 6331 (expect-punct ps 'rparen) 6332 (let ((r (parse-decl-suf-cont ps))) 6333 (lambda (b) (%mk-fn (r b) p v))))) 6334 (($ tok? (kind KW) (value __attribute__)) 6335 (skip-gnu-attribute! ps) (parse-decl-suf-cont ps)) 6336 (else (lambda (b) b)))) 6337 6338 (define (paren-is-group? ps) 6339 (pmatch (peek2 ps) 6340 (($ tok? (kind KW) (value ,v)) 6341 (cond ((or (eq? v 'void) (eq? v 'char) (eq? v 'short) 6342 (eq? v 'int) (eq? v 'long) (eq? v 'signed) 6343 (eq? v 'unsigned) (eq? v '_Bool) 6344 (eq? v 'float) (eq? v 'double) 6345 (eq? v '_Complex) (eq? v '_Imaginary) 6346 (eq? v 'struct) (eq? v 'union) (eq? v 'enum) 6347 (eq? v 'const) (eq? v 'volatile) 6348 (eq? v 'restrict) (eq? v 'static) 6349 (eq? v 'extern) (eq? v 'register)) #f) 6350 (else #t))) 6351 (($ tok? (kind IDENT) (value ,n)) 6352 (cond ((typedef? ps n) #f) (else #t))) 6353 (($ tok? (kind PUNCT) (value rparen)) #f) 6354 (($ tok? (kind PUNCT) (value star)) #t) 6355 (($ tok? (kind PUNCT) (value lparen)) #t) 6356 (($ tok? (kind PUNCT) (value lbrack)) #t) 6357 (else #f))) 6358 6359 (define (parse-fn-params ps) 6360 ;; Returns (values params variadic?). 6361 (cond 6362 ((at-punct? ps 'rparen) (values '() #f)) 6363 ((and (at-kw? ps 'void) 6364 (eq? (tok-kind (peek2 ps)) 'PUNCT) 6365 (eq? (tok-value (peek2 ps)) 'rparen)) 6366 (advance ps) (values '() #f)) 6367 (else 6368 (let loop ((acc '())) 6369 (cond 6370 ((at-punct? ps 'ellipsis) 6371 (advance ps) (values (reverse acc) #t)) 6372 (else 6373 (let*-values (((_sto bty) (parse-decl-spec ps)) 6374 ((nm ty) (parse-declarator ps bty))) 6375 (let ((ty2 (cond ((ctype-is-arr? ty) 6376 (%mk-ptr (car (ctype-ext ty)))) 6377 ((ctype-is-fn? ty) (%mk-ptr ty)) 6378 (else ty)))) 6379 (cond 6380 ((at-punct? ps 'comma) 6381 (advance ps) (loop (cons (cons nm ty2) acc))) 6382 ((at-punct? ps 'rparen) 6383 (values (reverse (cons (cons nm ty2) acc)) #f)) 6384 (else (die (tok-loc (peek ps)) "param"))))))))))) 6385 6386 (define (parse-translation-unit ps) 6387 (let loop () 6388 (cond 6389 ((eq? (tok-kind (peek ps)) 'EOF) #t) 6390 (else 6391 (cond 6392 ((debug-log?) 6393 (let ((loc (tok-loc (peek ps)))) 6394 (debug-log "decl" "line" (loc-line loc) 6395 "heap" (heap-usage))))) 6396 (cond ((at-kw? ps '_Static_assert) (parse-static-assert! ps)) 6397 (else (parse-decl-or-fn ps))) 6398 ;; Function-local metadata is not part of the persistent world. 6399 (cg-fn-meta-set! (ps-cg ps) '()) 6400 (loop))))) 6401 6402 (define (parse-static-assert! ps) 6403 (let ((loc (tok-loc (peek ps)))) 6404 (expect-kw ps '_Static_assert) 6405 (expect-punct ps 'lparen) 6406 (let ((v (parse-const-int ps))) 6407 (expect-punct ps 'comma) 6408 (cond ((not (eq? (tok-kind (peek ps)) 'STR)) 6409 (die (tok-loc (peek ps)) "_Static_assert needs a string"))) 6410 (let strings () 6411 (cond ((eq? (tok-kind (peek ps)) 'STR) 6412 (advance ps) (strings)))) 6413 (expect-punct ps 'rparen) 6414 (expect-punct ps 'semi) 6415 (cond ((%c-value-zero? v) (die loc "static assertion failed")) 6416 (else #t))))) 6417 6418 (define (parse-decl-or-fn ps) 6419 (let-values (((sto b) (parse-decl-spec ps))) 6420 (cond 6421 ((at-punct? ps 'semi) (advance ps) 'decl) 6422 (else 6423 (let-values (((n t) (parse-declarator ps b))) 6424 (cond 6425 ((and (ctype-is-fn? t) (at-punct? ps 'lbrace)) 6426 (parse-fn-body ps sto n t) 'fn) 6427 (else 6428 (handle-decl ps sto n t) 6429 (let lp () 6430 (cond 6431 ((at-punct? ps 'comma) 6432 (advance ps) 6433 (let-values (((n2 t2) (parse-declarator ps b))) 6434 (handle-decl ps sto n2 t2) (lp))) 6435 (else (expect-punct ps 'semi) 'decl)))))))))) 6436 6437 ;; ---- Block-scope inferred-length array length resolution ------------- 6438 ;; The token iterator buffers lookahead in a list (see tok-iter); we 6439 ;; can pull arbitrarily many tokens, then push them all back via 6440 ;; iter-unget!. We use that to peek the initializer that follows `=` 6441 ;; (without consuming it) and count its elements so cg-alloc-slot can 6442 ;; reserve the right number of bytes BEFORE the initializer-emission 6443 ;; loop runs (and starts spilling intermediate values into newly- 6444 ;; allocated frame slots). 6445 ;; 6446 ;; Only the OUTERMOST length is inferred per C99 6.7.8/22, so for 6447 ;; `int x[][3] = {{1,2,3},{4,5,6}};` we just count top-level 6448 ;; brace-or-comma groups; the inner brace groups don't matter. 6449 6450 (define (%peek-inferred-arr-init? ps) 6451 ;; Check whether the next-after-`=` token starts a brace-init or a 6452 ;; string-literal — the only initializer shapes that can resolve a 6453 ;; block-scope inferred-length array. We do NOT consume `=`; we 6454 ;; peek2 instead. 6455 (let ((t2 (peek2 ps))) 6456 (or (and (eq? (tok-kind t2) 'PUNCT) (eq? (tok-value t2) 'lbrace)) 6457 (eq? (tok-kind t2) 'STR)))) 6458 6459 (define (%resolve-inferred-arr-len ps ty) 6460 ;; Returns a fresh array ctype with the resolved length. Does NOT 6461 ;; consume the `=` or any of the initializer tokens — every token 6462 ;; pulled is unget back in original order. 6463 (let* ((eq-tok (iter-next (ps-iter ps))) ; consume `=` (will unget) 6464 (first (iter-next (ps-iter ps))) ; consume `{` or STR 6465 (collected (list first eq-tok)) ; head order: revs at end 6466 (count 6467 (cond 6468 ((eq? (tok-kind first) 'STR) 6469 ;; String length + NUL. 6470 (+ (bytevector-length (tok-value first)) 1)) 6471 (else 6472 ;; first is `{`. Count top-level commas + 1, ignoring a 6473 ;; trailing comma before `}`. Track brace depth so nested 6474 ;; `{` for sub-aggregates are skipped. 6475 (let lp ((depth 1) (n 0) (saw-elem? #f) (last-was-comma? #f) 6476 (acc collected)) 6477 (let ((t (iter-next (ps-iter ps)))) 6478 (let ((acc2 (cons t acc))) 6479 (cond 6480 ((eq? (tok-kind t) 'EOF) 6481 ;; Bail; let the real parser report the error 6482 ;; after we restore tokens. 6483 (%inferred-arr-restore! ps acc2) 6484 (die #f "init: unterminated brace")) 6485 ((and (eq? (tok-kind t) 'PUNCT) 6486 (eq? (tok-value t) 'lbrace)) 6487 (lp (+ depth 1) n #t #f acc2)) 6488 ((and (eq? (tok-kind t) 'PUNCT) 6489 (eq? (tok-value t) 'rbrace)) 6490 (cond 6491 ((= depth 1) 6492 ;; Done. Restore tokens (acc2 includes the 6493 ;; closing `}`). 6494 (%inferred-arr-restore! ps acc2) 6495 (cond ((not saw-elem?) 0) 6496 (last-was-comma? n) 6497 (else (+ n 1)))) 6498 (else (lp (- depth 1) n saw-elem? #f acc2)))) 6499 ((and (eq? (tok-kind t) 'PUNCT) 6500 (eq? (tok-value t) 'comma) 6501 (= depth 1)) 6502 (lp depth (+ n 1) saw-elem? #t acc2)) 6503 (else 6504 (lp depth n #t #f acc2))))))))) 6505 ) 6506 (cond 6507 ((eq? (tok-kind first) 'STR) 6508 (%inferred-arr-restore! ps collected))) 6509 (%init-fixed-arr-type ty count))) 6510 6511 (define (%inferred-arr-restore! ps acc) 6512 ;; acc is a stack of tokens in REVERSE consume order (most-recent 6513 ;; first). iter-unget! prepends one at a time, so iterating acc in 6514 ;; its current order pushes them back in the right sequence — 6515 ;; i.e. the oldest-consumed token ends up at the front of the 6516 ;; lookahead buffer. 6517 (let lp ((xs acc)) 6518 (cond 6519 ((null? xs) #t) 6520 (else (iter-unget! (ps-iter ps) (car xs)) (lp (cdr xs)))))) 6521 6522 (define (handle-decl ps sto n ty) 6523 (cond 6524 ((not n) (die #f "no name")) 6525 ((eq? sto 'typedef) 6526 (scope-bind! ps n (%sym n 'typedef #f ty #f #t))) 6527 ((ctype-is-fn? ty) 6528 (scope-bind! ps n 6529 (%sym n 'fn (or sto 'extern) ty #f #f))) 6530 ;; §I: block-scope `static` routes to a global with a declaration-id 6531 ;; name under the enclosing function. Function + identifier is not 6532 ;; enough: separate nested scopes may legally reuse the same identifier. 6533 ;; A same-scope redeclaration reuses its existing mangled name; a new 6534 ;; declaration consumes the function's monotonic static counter. The 6535 ;; scope-bind! key remains the original identifier for source lookup. 6536 ((and (eq? sto 'static) (ps-fn-ctx ps)) 6537 (let* ((fc (ps-fn-ctx ps)) 6538 (old (scope-lookup-current ps n)) 6539 (mangled 6540 (cond 6541 ((and old (eq? (sym-kind old) 'var) 6542 (eq? (sym-storage old) 'static)) 6543 (sym-name old)) 6544 (else 6545 (let ((id (fn-ctx-static-counter fc))) 6546 (fn-ctx-static-counter-set! fc (+ id 1)) 6547 (bytevector-append (fn-ctx-name fc) "__static_" 6548 (number->string id 10) "__" n)))))) 6549 (cond 6550 ((at-punct? ps 'assign) 6551 (advance ps) 6552 ;; Parse init first so an inferred-length array picks up its 6553 ;; resolved type before sm is constructed (sym is immutable). 6554 (let-values (((pieces ty2) (parse-init-global ps ty))) 6555 (let ((sm (%sym mangled 'var 'static ty2 #f #t))) 6556 (scope-bind! ps n sm) 6557 (cg-emit-global (ps-cg ps) sm pieces)))) 6558 (else 6559 (let ((sm (%sym mangled 'var 'static ty #f #t))) 6560 (scope-bind! ps n sm) 6561 (cg-emit-global (ps-cg ps) sm #f)))))) 6562 (else 6563 (cond 6564 ((not (ps-fn-ctx ps)) 6565 ;; File-scope decls. Three cases: 6566 ;; (a) initializer present -> full external definition. 6567 ;; (b) `extern` no init -> declaration only. 6568 ;; (c) no init, no `extern` -> tentative definition. 6569 ;; (a) emits to .data immediately. (b) is recorded but emits 6570 ;; nothing. (c) is recorded as `defined?=#f` and added to 6571 ;; world-tentatives; cg-finish emits .bss at end of TU only if 6572 ;; no full definition appeared. This lets two `static int x;` 6573 ;; or a `static int x;` followed by `static int x = 1;` 6574 ;; coexist (C 6.9.2 tentative-def merge). 6575 (cond 6576 ((at-punct? ps 'assign) 6577 (advance ps) 6578 (let-values (((pieces ty2) (parse-init-global ps ty))) 6579 (let ((sm (%sym n 'var (or sto 'extern) ty2 #f #t))) 6580 (scope-bind! ps n sm) 6581 (cg-emit-global (ps-cg ps) sm pieces)))) 6582 ((eq? sto 'extern) 6583 (let ((sm (%sym n 'var 'extern ty #f #f))) 6584 (scope-bind! ps n sm) 6585 (cg-emit-extern (ps-cg ps) sm))) 6586 (else 6587 (let ((sm (%sym n 'var (or sto 'extern) ty #f #f))) 6588 (scope-bind! ps n sm) 6589 (cg-add-tentative! (ps-cg ps) n))))) 6590 (else 6591 ;; Block-scope inferred-length array (`int a[] = {…};` or 6592 ;; `char s[] = "…";`): peek the initializer past `=` to count 6593 ;; elements / measure the string and rebuild `ty` with the 6594 ;; resolved length BEFORE cg-alloc-slot. Otherwise the slot 6595 ;; is sized off a -1 / 0 ctype-size (capped to 1 byte) and 6596 ;; the per-element stores in parse-init-local-aggregate write 6597 ;; past frame-hi — the next %cg-spill-reg then allocates 6598 ;; right inside the array, clobbering elements. 6599 (let* ((ty (cond 6600 ((and (eq? (ctype-kind ty) 'arr) 6601 (< (cdr (ctype-ext ty)) 0) 6602 (at-punct? ps 'assign) 6603 (%peek-inferred-arr-init? ps)) 6604 (%resolve-inferred-arr-len ps ty)) 6605 (else ty))) 6606 (sz (max (ctype-size ty) 1)) 6607 (al (max (ctype-align ty) 1)) 6608 (sl (cg-alloc-slot (ps-cg ps) sz al)) 6609 (sm (%sym n 'var (or sto 'auto) ty sl #t))) 6610 (scope-bind! ps n sm) 6611 (cond 6612 ((at-punct? ps 'assign) 6613 (advance ps) 6614 (cond 6615 ;; Aggregate locals get the per-element store treatment. 6616 ((or (at-punct? ps 'lbrace) 6617 (and (eq? (ctype-kind ty) 'arr) 6618 (eq? (tok-kind (peek ps)) 'STR))) 6619 (parse-init-local-aggregate ps sm ty)) 6620 ;; Struct/union initializer from a non-brace expression 6621 ;; (typically a function call returning by-value). The 6622 ;; expr produces a struct lval; we copy bytes into the 6623 ;; destination slot. 6624 ((or (eq? (ctype-kind ty) 'struct) 6625 (eq? (ctype-kind ty) 'union)) 6626 (cg-push-sym (ps-cg ps) sm) 6627 (parse-expr-bp ps 4) 6628 (cg-copy-struct (ps-cg ps))) 6629 (else 6630 (cg-push-sym (ps-cg ps) sm) 6631 (parse-expr-bp ps 4) (rval! ps) 6632 (cg-cast (ps-cg ps) ty) 6633 (cg-assign (ps-cg ps)) 6634 (cg-pop (ps-cg ps))))) 6635 (else #t)))))))) 6636 6637 ;; ==================================================================== 6638 ;; Initializers (see CC.md §Variable initializers). 6639 ;; 6640 ;; parse-init-global ps ty 6641 ;; Reads the initializer following `=` for a file-scope or block-scope 6642 ;; static var of static-storage type `ty` and returns a list of 6643 ;; pieces suitable for cg-emit-global. See cg.scm §cg-emit-global for 6644 ;; the piece grammar. 6645 ;; 6646 ;; parse-init-local ps sm ty 6647 ;; Reads the initializer for an auto-storage variable bound to slot 6648 ;; sym `sm` and emits per-element store cg ops. Returns unspecified. 6649 ;; ==================================================================== 6650 6651 (define (%int->le-bv n nbytes) 6652 ;; N-byte little-endian encoding of integer n into a fresh bv. Bytes 6653 ;; >= sign-bit are filled by repeated >>8 (works for both signed and 6654 ;; unsigned because we only keep the low N bytes). 6655 (let* ((out (make-bytevector nbytes 0)) 6656 (src (c-value-bytes (%c-value-coerce n)))) 6657 (let loop ((i 0)) 6658 (cond 6659 ((= i nbytes) out) 6660 (else 6661 (bytevector-u8-set! out i 6662 (if (< i %C-VALUE-BYTES) (bytevector-u8-ref src i) 0)) 6663 (loop (+ i 1))))))) 6664 6665 ;; File-scope compound literal (C99 §6.5.2.5). The bracketed initializer 6666 ;; following a typename in a static-storage initializer (or behind `&` 6667 ;; in same) is an unnamed object with static storage duration. Drive 6668 ;; the existing parse-init-global → cg-emit-global pipeline against a 6669 ;; synthetic sym whose label is freshly minted via %cg-fresh-cl-label. 6670 ;; Returns the emitted label; the caller wraps it in a (label-ref . LBL) 6671 ;; piece. The leading `(T)` and the storage-class disambiguation belong 6672 ;; to the caller — this entry point assumes peek = `{`. 6673 (define (%emit-fs-compound-literal ps ty) 6674 (let-values (((pieces ty2) (parse-init-global ps ty))) 6675 (let* ((lbl (%cg-fresh-cl-label (ps-cg ps))) 6676 ;; storage 'extern → %cg-sym-label returns the bare name 6677 ;; unchanged (no extra "cc__" prefix), so the emitted label 6678 ;; matches what we hand back to the caller. 6679 (sm (%sym lbl 'var 'extern ty2 #f #t))) 6680 (cg-emit-global (ps-cg ps) sm pieces) 6681 lbl))) 6682 6683 (define (%const-init-piece ps ty) 6684 ;; Parse a non-brace initializer expression for scalar type `ty` and 6685 ;; return a single piece. Recognised forms: 6686 ;; - INT (with optional unary +/-) -> N-byte LE bv 6687 ;; - enum-const IDENT -> N-byte LE bv 6688 ;; - &IDENT (address of a global var/fn) -> (label-ref . cc__name) 6689 ;; - &(T){...} (address of file-scope literal) -> (label-ref . cc__cl_N) 6690 ;; - IDENT (function name; decays to fn ptr) -> (label-ref . cc__name) 6691 ;; - STR (only for char* targets) -> (label-ref . string-pool-label) 6692 ;; - (T){...} (file-scope compound literal) -> (label-ref . cc__cl_N) 6693 (let ((t (peek ps))) 6694 (cond 6695 ;; Redundant expression grouping around a static initializer. Macro 6696 ;; APIs commonly spell literals as `((T){...})` and strings as 6697 ;; `("...")`; peel one non-cast pair and let the regular initializer 6698 ;; paths consume the enclosed value. 6699 ((and (eq? (tok-kind t) 'PUNCT) (eq? (tok-value t) 'lparen) 6700 (eq? (tok-kind (peek2 ps)) 'STR)) 6701 (advance ps) 6702 (let ((p (%const-init-piece ps ty))) 6703 (expect-punct ps 'rparen) 6704 p)) 6705 ;; Address initializer: &ident -> label-ref 6706 ((and (eq? (tok-kind t) 'PUNCT) (eq? (tok-value t) 'amp)) 6707 (advance ps) 6708 (let ((it (peek ps))) 6709 (cond 6710 ((eq? (tok-kind it) 'IDENT) 6711 (advance ps) 6712 (let ((sm (scope-lookup ps (tok-value it)))) 6713 (cond 6714 ((not sm) (die (tok-loc it) "init: undecl" (tok-value it))) 6715 ((or (eq? (sym-kind sm) 'fn) 6716 (and (eq? (sym-kind sm) 'var) 6717 (or (eq? (sym-storage sm) 'static) 6718 (eq? (sym-storage sm) 'extern)))) 6719 (cons 'label-ref (%cg-sym-label (ps-cg ps) sm))) 6720 (else 6721 (die (tok-loc it) "init: &x must reference a global" 6722 (tok-value it)))))) 6723 ;; &(T){...} — address of an unnamed file-scope compound 6724 ;; literal. Parse the typename, expect `{`, drive the 6725 ;; literal into .data, and yield its label. 6726 ((and (eq? (tok-kind it) 'PUNCT) (eq? (tok-value it) 'lparen) 6727 (%const-paren-is-cast? ps)) 6728 (advance ps) 6729 (let*-values (((_sto bty) (parse-decl-spec ps)) 6730 ((_n ty2) (parse-declarator ps bty))) 6731 (expect-punct ps 'rparen) 6732 (cond 6733 ((not (at-punct? ps 'lbrace)) 6734 (die (tok-loc (peek ps)) 6735 "init: &(T) must be followed by { ... }" 6736 (tok-value (peek ps))))) 6737 (cons 'label-ref (%emit-fs-compound-literal ps ty2)))) 6738 (else (die (tok-loc it) "init: &?" (tok-value it)))))) 6739 ;; (T){...} — file-scope compound literal. The literal is an 6740 ;; lvalue of array/struct/union type; assignment to a pointer 6741 ;; target decays it via its label address (label = first byte). 6742 ((and (eq? (tok-kind t) 'PUNCT) (eq? (tok-value t) 'lparen) 6743 (%const-paren-is-cast? ps) 6744 ;; Speculatively look past `(T)` for `{`. Since we have no 6745 ;; 3-token peek, we have to commit to the (T) parse; if the 6746 ;; following token isn't `{` it's a plain cast, so we fall 6747 ;; back to the const-int path with the type already consumed. 6748 #t) 6749 ;; Take the (T) ourselves so we can dispatch on the next token. 6750 (advance ps) 6751 (let*-values (((_sto bty) (parse-decl-spec ps)) 6752 ((_n ty2) (parse-declarator ps bty))) 6753 (expect-punct ps 'rparen) 6754 (cond 6755 ((at-punct? ps 'lbrace) 6756 (cons 'label-ref (%emit-fs-compound-literal ps ty2))) 6757 (else 6758 ;; Not a compound literal — it's a constant cast, e.g. 6759 ;; `(int)(unsigned char)257`. Mirror parse-const-cast's 6760 ;; cast arm with the already-parsed type. 6761 (cond 6762 ((%ctype-int? ty2) 6763 (let ((v (parse-const-cast ps))) 6764 (%int->le-bv (%const-trunc (car v) ty2) 6765 (max (ctype-size ty) 1)))) 6766 ((eq? (ctype-kind ty2) 'ptr) 6767 ;; Pointer cast in const-expr: type-retag only. We expect 6768 ;; the operand to be an integer-shaped const (e.g. 0) and 6769 ;; emit it as the target's byte width. 6770 (let ((v (parse-const-cast ps))) 6771 (%int->le-bv (car v) (max (ctype-size ty) 1)))) 6772 (else 6773 (die (tok-loc (peek ps)) 6774 "init: cast to non-scalar non-compound-literal" 6775 (ctype-kind ty2)))))))) 6776 ;; Function name or array name as a label-ref initializer. 6777 ;; (Both decay to a pointer when used as a value.) 6778 ((and (eq? (tok-kind t) 'IDENT) 6779 (let ((sm (scope-lookup ps (tok-value t)))) 6780 (and sm 6781 (or (eq? (sym-kind sm) 'fn) 6782 (and (eq? (sym-kind sm) 'var) 6783 (eq? (ctype-kind (sym-type sm)) 'arr) 6784 (or (eq? (sym-storage sm) 'static) 6785 (eq? (sym-storage sm) 'extern))))))) 6786 (advance ps) 6787 (let ((sm (scope-lookup ps (tok-value t)))) 6788 (cons 'label-ref (%cg-sym-label (ps-cg ps) sm)))) 6789 ;; Plain string literal as char* initializer. 6790 ((eq? (tok-kind t) 'STR) 6791 (advance ps) 6792 (let ((lbl (cg-intern-string (ps-cg ps) (tok-value t)))) 6793 (cons 'label-ref lbl))) 6794 ;; Otherwise it's a const integer. 6795 (else 6796 (let ((v (parse-const-int ps))) 6797 (%int->le-bv v (max (ctype-size ty) 1))))))) 6798 6799 ;; Parse a file-scope aggregate compound literal, accepting redundant outer 6800 ;; grouping: `(T){...}` and `((T){...})`. The caller already knows an 6801 ;; aggregate is required, so a leading parenthesis is unambiguous here. 6802 (define (%global-init-compound ps expected-ty) 6803 (expect-punct ps 'lparen) 6804 (cond 6805 ((at-punct? ps 'lparen) 6806 (let ((pieces (%global-init-compound ps expected-ty))) 6807 (expect-punct ps 'rparen) 6808 pieces)) 6809 (else 6810 (let*-values (((_sto bty) (parse-decl-spec ps)) 6811 ((_n literal-ty) (parse-declarator ps bty))) 6812 (expect-punct ps 'rparen) 6813 (cond ((not (or (ctype-compat? literal-ty expected-ty) 6814 (%ctype-same-aggregate-tag? literal-ty expected-ty))) 6815 (die (tok-loc (peek ps)) 6816 "init: incompatible aggregate compound literal"))) 6817 (expect-punct ps 'lbrace) 6818 (let ((k (ctype-kind literal-ty))) 6819 (cond 6820 ((eq? k 'arr) 6821 (let-values (((pieces _count) 6822 (%parse-init-array-list ps literal-ty))) 6823 pieces)) 6824 ((or (eq? k 'struct) (eq? k 'union)) 6825 (%parse-init-struct-list ps literal-ty)) 6826 (else (die #f "init: compound literal is not aggregate" k)))))))) 6827 6828 (define (%ctype-same-aggregate-tag? a b) 6829 ;; Qualifier propagation can clone a tagged aggregate ctype, so pointer 6830 ;; identity alone is too strict for `(T){...}` assigned to a const-qualified 6831 ;; T field. A matching non-anonymous tag is the seed compiler's nominal 6832 ;; identity for structs/unions. 6833 (let ((ka (ctype-kind a)) (kb (ctype-kind b))) 6834 (and (eq? ka kb) 6835 (or (eq? ka 'struct) (eq? ka 'union)) 6836 (let ((ta (car (ctype-ext a))) (tb (car (ctype-ext b)))) 6837 (and ta tb (bytes=? ta tb)))))) 6838 6839 (define (%init-grouped-compound-start? ps) 6840 (or (%const-paren-is-cast? ps) 6841 (pmatch (peek2 ps) 6842 (($ tok? (kind PUNCT) (value lparen)) #t) 6843 (else #f)))) 6844 6845 (define (%init-array-elem-type ty) 6846 (cond ((eq? (ctype-kind ty) 'arr) (car (ctype-ext ty))) 6847 (else (die #f "init: not an array" ty)))) 6848 6849 (define (%init-array-decl-len ty) 6850 ;; Declared array length (-1 = inferred). 6851 (cond ((eq? (ctype-kind ty) 'arr) (cdr (ctype-ext ty))) (else -1))) 6852 6853 (define (%init-fixed-arr-type ty count) 6854 ;; Construct a fresh array ctype with the inferred length resolved 6855 ;; to `count`. Pure — does not mutate `ty`. For non-inferred or 6856 ;; non-array `ty`, callers should detect this themselves and just 6857 ;; pass `ty` through. 6858 (%mk-arr (car (ctype-ext ty)) count)) 6859 6860 (define (%init-struct-fields ty) 6861 ;; Return ((name-bv ctype offset) ...) for a struct/union ctype. 6862 (let ((ext (ctype-ext ty))) 6863 (cond ((and (pair? ext) (pair? (cdr ext))) (car (cddr ext))) 6864 (else (die #f "init: not a struct" ty))))) 6865 6866 ;; After processing a designated initializer for FNAME, return the 6867 ;; field list with FNAME and all preceding (already-overwritten or 6868 ;; skipped) fields removed. Empty list if FNAME isn't found (caller 6869 ;; should already have validated the field exists). 6870 (define (%init-drop-thru-field fields fname) 6871 (cond ((null? fields) '()) 6872 ((bytes=? (car (car fields)) fname) (cdr fields)) 6873 (else (%init-drop-thru-field (cdr fields) fname)))) 6874 6875 ;; Consume a C99 field designator, including a chain such as `.v.stack`. 6876 ;; Return the top-level field name (for positional-cursor advancement), the 6877 ;; final selected type, and its aggregate-relative byte offset. 6878 (define (%init-field-designator ps fields) 6879 (expect-punct ps 'dot) 6880 (let walk ((cur-fields fields) (top-name #f) (offset 0)) 6881 (let ((nt (advance ps))) 6882 (cond ((not (eq? (tok-kind nt) 'IDENT)) 6883 (die (tok-loc nt) "init: .field expects ident"))) 6884 (let ((f (%cg-find-field cur-fields (tok-value nt)))) 6885 (cond ((not f) (die (tok-loc nt) "init: no such field" 6886 (tok-value nt)))) 6887 (let ((name (car f)) 6888 (fty (car (cdr f))) 6889 (foff (car (cddr f)))) 6890 (cond 6891 ((at-punct? ps 'dot) 6892 (advance ps) 6893 (walk (%init-struct-fields fty) 6894 (or top-name name) (+ offset foff))) 6895 (else 6896 (expect-punct ps 'assign) 6897 (list (or top-name name) fty (+ offset foff))))))))) 6898 6899 ;; #t when TY is an array of i8/u8 — a char[] a string literal may 6900 ;; initialize directly (C11 §6.7.9 ¶14). 6901 (define (%char-arr-type? t) 6902 (and (eq? (ctype-kind t) 'arr) 6903 (let ((et (car (ctype-ext t)))) 6904 (or (eq? et %t-i8) (eq? et %t-u8))))) 6905 6906 ;; A char[] field/element written as `= "..."`: consume the STR token 6907 ;; and return a byte-vector piece of the array's declared size, copying 6908 ;; the string bytes (truncating an over-long string, zero-padding a 6909 ;; short one). The nested-aggregate analogue of parse-init-global's 6910 ;; top-level STR arm; `t` always has a declared length here (only a 6911 ;; top-level array may infer its length from the string). 6912 (define (%str-arr-piece ps t) 6913 (let* ((s (tok-value (peek ps))) 6914 (slen (bytevector-length s)) 6915 (final (ctype-size t)) 6916 (bv (make-bytevector final 0))) 6917 (advance ps) 6918 (let loop ((i 0)) 6919 (cond 6920 ((or (= i slen) (>= i final)) bv) 6921 (else 6922 (bytevector-u8-set! bv i (bytevector-u8-ref s i)) 6923 (loop (+ i 1))))))) 6924 6925 ;; Element/field dispatch for global aggregate initializers. ELIDE? = #f 6926 ;; means caller has just consumed `{` for this element and we own the 6927 ;; matching `}`; ELIDE? = #t is C99 §6.7.8 ¶22 brace elision (the 6928 ;; sub-aggregate draws items from the parent stream, no inner braces). 6929 ;; Returns the piece-list contributing this element to the encoding. 6930 (define (%global-init-elem ps t elide?) 6931 (let ((k (ctype-kind t))) 6932 (cond 6933 ((and elide? 6934 (or (eq? k 'arr) (eq? k 'struct) (eq? k 'union)) 6935 (at-punct? ps 'lparen) 6936 (%init-grouped-compound-start? ps)) 6937 (let ((pieces (%global-init-compound ps t))) 6938 (cond 6939 (elide? pieces) 6940 (else 6941 (cond ((at-punct? ps 'comma) (advance ps))) 6942 (expect-punct ps 'rbrace) 6943 pieces)))) 6944 ;; char[] initialized by a string literal (`{"..."}` or, under 6945 ;; brace elision, a bare `"..."`). Without this, the `arr` arm 6946 ;; below would treat the string as an element-list and encode it 6947 ;; as a pointer to the string pool instead of the bytes. 6948 ((and (eq? k 'arr) 6949 (eq? (tok-kind (peek ps)) 'STR) 6950 (%char-arr-type? t)) 6951 (let ((p (%str-arr-piece ps t))) 6952 (cond 6953 (elide? (list p)) 6954 (else 6955 (cond ((at-punct? ps 'comma) (advance ps))) 6956 (expect-punct ps 'rbrace) 6957 (list p))))) 6958 ((eq? k 'arr) 6959 (let-values (((p _c) (cond 6960 (elide? (%parse-init-array-list/mode ps t #f)) 6961 (else (%parse-init-array-list ps t))))) 6962 p)) 6963 ((or (eq? k 'struct) (eq? k 'union)) 6964 (cond 6965 (elide? (%parse-init-struct-list/mode ps t #f)) 6966 (else (%parse-init-struct-list ps t)))) 6967 (else 6968 (let ((p (%const-init-piece ps t))) 6969 (cond 6970 (elide? (list p)) 6971 (else 6972 (cond ((at-punct? ps 'comma) (advance ps))) 6973 (expect-punct ps 'rbrace) 6974 (list p)))))))) 6975 6976 ;; Element/field dispatch for local aggregate initializers. Mirrors 6977 ;; %global-init-elem but emits per-element store ops via cg-assign for 6978 ;; scalar leaves, and recurses into the local-list walkers for 6979 ;; aggregates. Returns 0; the side effect is the emitted code. 6980 (define (%local-init-expr-tokens ps) 6981 ;; Capture one assignment-expression without consuming the comma or `}` 6982 ;; which terminates it in the surrounding initializer list. Braces must 6983 ;; be tracked separately from parentheses: a compound literal such as 6984 ;; `(struct S){ .x = 1, .y = 2 }` is one expression even though it contains 6985 ;; both commas and a closing brace. 6986 (let loop ((acc '()) (paren 0) (brack 0) (brace 0)) 6987 (let ((t (peek ps))) 6988 (cond 6989 ((eq? (tok-kind t) 'EOF) 6990 (die (tok-loc t) "EOF in local initializer expression")) 6991 ((and (= paren 0) (= brack 0) (= brace 0) 6992 (eq? (tok-kind t) 'PUNCT) 6993 (or (eq? (tok-value t) 'comma) 6994 (eq? (tok-value t) 'rbrace))) 6995 (reverse acc)) 6996 (else 6997 (let ((nt (advance ps))) 6998 (cond 6999 ((not (eq? (tok-kind nt) 'PUNCT)) 7000 (loop (cons nt acc) paren brack brace)) 7001 ((eq? (tok-value nt) 'lparen) 7002 (loop (cons nt acc) (+ paren 1) brack brace)) 7003 ((eq? (tok-value nt) 'rparen) 7004 (loop (cons nt acc) (- paren 1) brack brace)) 7005 ((eq? (tok-value nt) 'lbrack) 7006 (loop (cons nt acc) paren (+ brack 1) brace)) 7007 ((eq? (tok-value nt) 'rbrack) 7008 (loop (cons nt acc) paren (- brack 1) brace)) 7009 ((eq? (tok-value nt) 'lbrace) 7010 (loop (cons nt acc) paren brack (+ brace 1))) 7011 ((eq? (tok-value nt) 'rbrace) 7012 (loop (cons nt acc) paren brack (- brace 1))) 7013 (else 7014 (loop (cons nt acc) paren brack brace))))))))) 7015 7016 (define (%local-init-aggregate-expr? ps expected) 7017 ;; A brace-less aggregate initializer is ambiguous until its first 7018 ;; assignment-expression has been typed: it can be a whole-aggregate copy 7019 ;; (`.member = value`) or brace elision (`.member = 1, 2`). Parse that 7020 ;; expression once under a codegen snapshot, then put its tokens back. The 7021 ;; real parse below either copies the aggregate or descends into its first 7022 ;; scalar member. This is the same no-side-effect mechanism used by sizeof. 7023 (let* ((it (ps-iter ps)) 7024 (toks (%local-init-expr-tokens ps))) 7025 (for-each (lambda (t) (iter-unget! it t)) (reverse toks)) 7026 (cond 7027 ((null? toks) #f) 7028 (else 7029 (let ((tag (cg-snapshot (ps-cg ps)))) 7030 (parse-saved-expr ps toks) 7031 (let* ((top (cg-top (ps-cg ps))) 7032 (actual (opnd-type top)) 7033 (aggregate? 7034 (and (or (eq? (ctype-kind actual) 'struct) 7035 (eq? (ctype-kind actual) 'union)) 7036 (or (ctype-compat? actual expected) 7037 (%ctype-same-aggregate-tag? actual expected))))) 7038 (cg-rewind (ps-cg ps) tag) 7039 aggregate?)))))) 7040 7041 (define (%local-init-elem ps sm eoff t elide?) 7042 (let ((k (ctype-kind t))) 7043 (cond 7044 ;; char[] field/element initialized by a string literal — emit the 7045 ;; bytes into the frame slot. Mirrors %global-init-elem's STR arm; 7046 ;; without it the `arr` arm would parse the string as a scalar 7047 ;; expression and store one byte of its decayed pointer. 7048 ((and (eq? k 'arr) 7049 (eq? (tok-kind (peek ps)) 'STR) 7050 (%char-arr-type? t)) 7051 (let* ((s (tok-value (peek ps))) 7052 (slen (bytevector-length s)) 7053 (final (ctype-size t))) 7054 (advance ps) 7055 (let loop ((i 0)) 7056 (cond 7057 ((>= i final) 0) 7058 (else 7059 (let ((b (cond ((< i slen) (bytevector-u8-ref s i)) (else 0)))) 7060 (%push-frame-elem-lval ps (+ eoff i) %t-u8) 7061 (cg-push-imm (ps-cg ps) %t-u8 b) 7062 (cg-assign (ps-cg ps)) (cg-pop (ps-cg ps)) 7063 (loop (+ i 1)))))) 7064 (cond 7065 (elide? 0) 7066 (else 7067 (cond ((at-punct? ps 'comma) (advance ps))) 7068 (expect-punct ps 'rbrace))))) 7069 ((eq? k 'arr) 7070 (cond 7071 (elide? (%parse-init-local-array-list/mode ps sm eoff t #f)) 7072 (else (%parse-init-local-array-list ps sm eoff t)))) 7073 ((or (eq? k 'struct) (eq? k 'union)) 7074 (cond 7075 ((and elide? (%local-init-aggregate-expr? ps t)) 7076 (%push-frame-elem-lval ps eoff t) 7077 (parse-expr-bp ps 4) 7078 (cg-copy-struct (ps-cg ps))) 7079 (elide? (%parse-init-local-struct-list/mode ps sm eoff t #f)) 7080 (else (%parse-init-local-struct-list ps sm eoff t)))) 7081 (else 7082 (%push-frame-elem-lval ps eoff t) 7083 (parse-expr-bp ps 4) (rval! ps) 7084 (cg-cast (ps-cg ps) t) 7085 (cg-assign (ps-cg ps)) (cg-pop (ps-cg ps)) 7086 (cond 7087 (elide? 0) 7088 (else 7089 (cond ((at-punct? ps 'comma) (advance ps))) 7090 (expect-punct ps 'rbrace))))))) 7091 7092 (define (%pad-piece nbytes) 7093 (make-bytevector nbytes 0)) 7094 7095 ;; Prepend XS in reverse order without allocating an intermediate list. 7096 (define (%init-prepend-reversed xs acc) 7097 (let loop ((ys xs) (out acc)) 7098 (cond 7099 ((null? ys) out) 7100 (else 7101 (loop (cdr ys) (cons (car ys) out)))))) 7102 7103 ;; ----- Global initializers --------------------------------------------- 7104 ;; Returns (values pieces final-ty). For inferred-length array `ty`, 7105 ;; final-ty is a freshly-built array ctype with the resolved length; 7106 ;; otherwise final-ty is `ty` unchanged. 7107 (define (parse-init-global ps ty) 7108 (pmatch (peek ps) 7109 ;; String literal initializer for char[] 7110 (($ tok? (kind STR) (value ,s)) 7111 (guard (and (eq? (ctype-kind ty) 'arr) 7112 (let ((et (car (ctype-ext ty)))) 7113 (or (eq? et %t-i8) (eq? et %t-u8))))) 7114 (advance ps) 7115 (let* ((slen (bytevector-length s)) 7116 (decl (cdr (ctype-ext ty))) 7117 (final (cond ((< decl 0) (+ slen 1)) (else decl))) 7118 (final-ty (cond ((< decl 0) (%init-fixed-arr-type ty final)) 7119 (else ty)))) 7120 (let ((bv (make-bytevector final 0))) 7121 (let loop ((i 0)) 7122 (cond 7123 ((or (= i slen) (>= i final)) 7124 (values (list bv) final-ty)) 7125 (else 7126 (bytevector-u8-set! bv i (bytevector-u8-ref s i)) 7127 (loop (+ i 1)))))))) 7128 ;; Brace-form 7129 (($ tok? (kind PUNCT) (value lbrace)) 7130 (advance ps) 7131 (cond 7132 ((eq? (ctype-kind ty) 'arr) 7133 (let-values (((pieces count) (%parse-init-array-list ps ty))) 7134 (let* ((decl (%init-array-decl-len ty)) 7135 (final-ty (cond ((< decl 0) 7136 (%init-fixed-arr-type ty count)) 7137 (else ty)))) 7138 (values pieces final-ty)))) 7139 ((or (eq? (ctype-kind ty) 'struct) (eq? (ctype-kind ty) 'union)) 7140 (values (%parse-init-struct-list ps ty) ty)) 7141 (else 7142 ;; Brace-wrapped scalar: { expr } 7143 (let ((piece (%const-init-piece ps ty))) 7144 (cond ((at-punct? ps 'comma) (advance ps))) 7145 (expect-punct ps 'rbrace) 7146 (values (list piece) ty))))) 7147 ;; Bare scalar initializer 7148 (else (values (list (%const-init-piece ps ty)) ty)))) 7149 7150 ;; Returns (values pieces count). `count` is the number of element 7151 ;; initializers actually consumed (used by parse-init-global to resolve 7152 ;; an inferred top-level length). C99 forbids inferred length in 7153 ;; nested array elements, so recursive callers ignore `count`. 7154 ;; 7155 ;; `brace?` controls termination: when #t (the normal case), the loop 7156 ;; consumes elements until `}` is seen. When #f (brace-elision recursion 7157 ;; from C99 §6.7.8 ¶22), the loop consumes exactly `decl` elements from 7158 ;; the parent stream and returns without expecting `}`. In no-brace 7159 ;; mode, a leading `.` or `[` designator targets the enclosing aggregate 7160 ;; — the recursion terminates immediately, padding the unfilled tail. 7161 (define (%parse-init-array-list ps ty) 7162 (%parse-init-array-list/mode ps ty #t)) 7163 7164 (define (%parse-init-array-list/mode ps ty brace?) 7165 ;; Element-list array initializer; assumes `{` already consumed when 7166 ;; brace? is #t. 7167 (let* ((elem (%init-array-elem-type ty)) 7168 (esize (ctype-size elem)) 7169 (decl (%init-array-decl-len ty))) 7170 (let lp ((entries '()) (cursor 0) (count 0)) 7171 (cond 7172 ((cond (brace? (at-punct? ps 'rbrace)) 7173 (else (or (>= cursor (cond ((< decl 0) 0) (else decl))) 7174 (at-punct? ps 'rbrace) 7175 (at-punct? ps 'dot) 7176 (at-punct? ps 'lbrack)))) 7177 (cond (brace? (advance ps))) 7178 (let ((final (cond ((< decl 0) count) (else decl)))) 7179 (values (%merge-init-entries (reverse entries) (* final esize)) 7180 count))) 7181 (else 7182 (let* ((designated? (and brace? (at-punct? ps 'lbrack))) 7183 (index 7184 (cond 7185 (designated? 7186 (advance ps) 7187 (let ((n (parse-const-int ps))) 7188 (expect-punct ps 'rbrack) 7189 (expect-punct ps 'assign) 7190 n)) 7191 (else cursor))) 7192 (_range 7193 (cond 7194 ((or (< index 0) (and (>= decl 0) (>= index decl))) 7195 (die (tok-loc (peek ps)) 7196 "init: array designator out of range" index)) 7197 (else 0))) 7198 (piece 7199 (let ((p 7200 (cond 7201 ((at-punct? ps 'lbrace) 7202 (advance ps) 7203 (%global-init-elem ps elem #f)) 7204 (else 7205 (%global-init-elem ps elem #t))))) 7206 ;; Inter-item comma: consume except for the comma 7207 ;; following our LAST item in no-brace mode — that 7208 ;; one belongs to the enclosing parent. 7209 (cond 7210 (brace? 7211 (cond ((at-punct? ps 'comma) (advance ps)))) 7212 (else 7213 ;; no-brace: consume comma only if more items 7214 ;; remain in our quota. 7215 (cond 7216 ((and (< (+ index 1) 7217 (cond ((< decl 0) 0) (else decl))) 7218 (at-punct? ps 'comma)) 7219 (advance ps))))) 7220 p)) 7221 (next (+ index 1))) 7222 (lp (cons (cons (* index esize) piece) entries) 7223 next (max count next)))))))) 7224 7225 (define (%piece-bytesize p) 7226 ;; Output width of one piece (cf. %cg-init-piece->bv): a bv emits 7227 ;; one byte per element; a (label-ref . _) emits a target-word slot. 7228 (cond 7229 ((bytes? p) (bytevector-length p)) 7230 ((and (pair? p) (eq? (car p) 'label-ref)) %CC-WORD-BYTES) 7231 (else (die #f "init: unknown piece" p)))) 7232 7233 (define (%pieces-bytesize ps-list) 7234 (let loop ((xs ps-list) (n 0)) 7235 (cond ((null? xs) n) 7236 (else (loop (cdr xs) (+ n (%piece-bytesize (car xs)))))))) 7237 7238 (define (%merge-init-entries entries total-size) 7239 ;; entries: list of (abs-offset . piece-list), in source order. 7240 ;; Sort stably by offset (later writes to the same offset win, per C 7241 ;; designated-init semantics) and emit pad pieces in any gaps and at 7242 ;; the tail. Preserves label-ref pieces — we never merge them into a 7243 ;; flat bv. 7244 (let* ((sorted (%init-stable-sort-by-offset entries)) 7245 (out 7246 (let walk ((xs sorted) (cursor 0) (acc '())) 7247 (cond 7248 ((null? xs) 7249 (cond 7250 ((< cursor total-size) 7251 (reverse (cons (%pad-piece (- total-size cursor)) acc))) 7252 (else (reverse acc)))) 7253 (else 7254 (let* ((e (car xs)) 7255 (eoff (car e)) 7256 (epieces (cdr e)) 7257 (esize (%pieces-bytesize epieces)) 7258 (acc1 (cond 7259 ((> eoff cursor) 7260 (cons (%pad-piece (- eoff cursor)) acc)) 7261 (else acc))) 7262 (acc2 (append (reverse epieces) acc1))) 7263 (walk (cdr xs) (+ eoff esize) acc2))))))) 7264 out)) 7265 7266 (define (%init-stable-sort-by-offset entries) 7267 ;; Insertion sort, stable by source order for ties. n is small (one 7268 ;; entry per initialized field) so O(n^2) is fine. 7269 (let lp ((xs entries) (acc '())) 7270 (cond 7271 ((null? xs) acc) 7272 (else 7273 (let ((e (car xs))) 7274 (lp (cdr xs) 7275 (let ins ((ys acc) (head '())) 7276 (cond 7277 ((null? ys) 7278 (append (reverse head) (list e))) 7279 ((<= (car e) (car (car ys))) 7280 (append (reverse head) (cons e ys))) 7281 (else 7282 (ins (cdr ys) (cons (car ys) head))))))))))) 7283 7284 (define (%parse-init-struct-list ps ty) 7285 (%parse-init-struct-list/mode ps ty #t)) 7286 7287 (define (%parse-init-struct-list/mode ps ty brace?) 7288 ;; Struct/union initializer; assumes `{` already consumed when brace?. 7289 ;; In no-brace mode (brace elision, C99 §6.7.8 ¶22), terminate when 7290 ;; positional fields are exhausted, when a `}` is seen (belongs to 7291 ;; the enclosing aggregate), or when a designator (`.`) appears (it 7292 ;; targets the enclosing aggregate). Doesn't consume the trailing 7293 ;; comma after the last field — that belongs to the parent list. 7294 (let* ((fields (%init-struct-fields ty)) 7295 (size (ctype-size ty)) 7296 (union? (eq? (ctype-kind ty) 'union))) 7297 (let lp ((entries '()) (rest fields)) 7298 (cond 7299 ((cond (brace? (at-punct? ps 'rbrace)) 7300 (else (or (null? rest) 7301 (at-punct? ps 'rbrace) 7302 (at-punct? ps 'dot) 7303 ;; Union in brace-elision mode: take one 7304 ;; member then return — the next sibling 7305 ;; initializer belongs to the parent 7306 ;; (C99 §6.7.8 ¶22 + union has one active 7307 ;; member at a time). 7308 (and union? (pair? entries))))) 7309 (cond (brace? (advance ps))) 7310 (%merge-init-entries (reverse entries) size)) 7311 (else 7312 (let* ((designated? (at-punct? ps 'dot)) 7313 (target 7314 (cond 7315 (designated? 7316 (%init-field-designator ps fields)) 7317 ((null? rest) 7318 (die (tok-loc (peek ps)) "init: too many fields")) 7319 (else (car rest)))) 7320 (fname (car target)) 7321 (fty (car (cdr target))) 7322 (foff (car (cddr target))) 7323 (piece-list 7324 (cond 7325 ((at-punct? ps 'lbrace) 7326 (advance ps) 7327 (%global-init-elem ps fty #f)) 7328 (else 7329 (%global-init-elem ps fty #t)))) 7330 (rest1 7331 (cond 7332 ;; designated init: drop fields up to and including target 7333 (designated? (%init-drop-thru-field fields fname)) 7334 (else (cdr rest))))) 7335 ;; Inter-item comma: consume except for the comma after our 7336 ;; LAST field in no-brace mode (belongs to enclosing list). 7337 (cond 7338 (brace? 7339 (cond ((at-punct? ps 'comma) (advance ps)))) 7340 (else 7341 (cond ((and (not (null? rest1)) 7342 ;; Union in brace-elision mode terminates 7343 ;; after one element regardless of rest1; 7344 ;; that means the comma belongs to the parent. 7345 (not union?) 7346 (at-punct? ps 'comma)) 7347 (advance ps))))) 7348 (lp (cons (cons foff piece-list) entries) rest1))))))) 7349 7350 ;; ----- Local aggregate initializers ------------------------------------ 7351 ;; Emits per-element store sequences via cg ops into the slot of `sm` 7352 ;; (a 'var sym whose slot is the frame offset). Assumes the assignment 7353 ;; `=` has already been consumed. 7354 (define (parse-init-local-aggregate ps sm ty) 7355 (pmatch (peek ps) 7356 ;; Local char[] = "string" — fill from string bytes. 7357 (($ tok? (kind STR) (value ,s)) 7358 (guard (and (eq? (ctype-kind ty) 'arr) 7359 (let ((et (car (ctype-ext ty)))) 7360 (or (eq? et %t-i8) (eq? et %t-u8))))) 7361 (advance ps) 7362 ;; Note: for inferred-length (`int x[] = "..."`) auto arrays the 7363 ;; sm-type still records the original (size=-1) ctype — `sizeof(x)` 7364 ;; in the body would not see the resolved length. The slot is also 7365 ;; sized off the original (= 1 byte), so the path is pre-existing 7366 ;; broken; we don't paper over it here. Real C bootstrap code uses 7367 ;; statics/globals for inferred-length arrays. 7368 (let* ((slen (bytevector-length s)) 7369 (decl (cdr (ctype-ext ty))) 7370 (final (cond ((< decl 0) (+ slen 1)) (else decl)))) 7371 ;; Emit byte stores for each char in s, plus NUL for the 7372 ;; trailing slot if final > slen. 7373 (let loop ((i 0)) 7374 (cond 7375 ((>= i final) #t) 7376 (else 7377 (let ((b (cond ((< i slen) (bytevector-u8-ref s i)) 7378 (else 0))) 7379 (off (+ (sym-slot sm) i))) 7380 (%push-frame-elem-lval ps off %t-u8) 7381 (cg-push-imm (ps-cg ps) %t-u8 b) 7382 (cg-assign (ps-cg ps)) 7383 (cg-pop (ps-cg ps)) 7384 (loop (+ i 1)))))))) 7385 (($ tok? (kind PUNCT) (value lbrace)) 7386 (advance ps) 7387 (cond 7388 ((eq? (ctype-kind ty) 'arr) 7389 (%parse-init-local-array-list ps sm (sym-slot sm) ty)) 7390 ((or (eq? (ctype-kind ty) 'struct) (eq? (ctype-kind ty) 'union)) 7391 (%parse-init-local-struct-list ps sm (sym-slot sm) ty)) 7392 (else (die #f "init local: brace on scalar?")))) 7393 (else (die (tok-loc (peek ps)) "init local aggregate?")))) 7394 7395 (define (%push-frame-elem-lval ps base-off ty) 7396 (cg-push (ps-cg ps) (%opnd 'frame ty base-off #t))) 7397 7398 (define (%parse-init-local-array-list ps sm base-off ty) 7399 (%parse-init-local-array-list/mode ps sm base-off ty #t)) 7400 7401 (define (%parse-init-local-array-list/mode ps sm base-off ty brace?) 7402 (let* ((elem (%init-array-elem-type ty)) 7403 (esize (ctype-size elem)) 7404 (decl (%init-array-decl-len ty))) 7405 (let lp ((i 0)) 7406 (cond 7407 ((cond (brace? (at-punct? ps 'rbrace)) 7408 (else (or (>= i (cond ((< decl 0) 0) (else decl))) 7409 (at-punct? ps 'rbrace) 7410 (at-punct? ps 'dot) 7411 (at-punct? ps 'lbrack)))) 7412 (cond (brace? (advance ps))) 7413 ;; Inferred-length auto path is pre-existing broken (slot 7414 ;; allocated off size=-1, sm-type unfixed). See note in 7415 ;; parse-init-local-aggregate STR branch. 7416 ;; Zero out remaining slots if any (declared length > i). 7417 (let ((final (cond ((< decl 0) i) (else decl)))) 7418 (let zlp ((k i)) 7419 (cond 7420 ((>= k final) #t) 7421 (else 7422 (let ((off (+ base-off (* k esize)))) 7423 (cond 7424 ((or (eq? (ctype-kind elem) 'arr) 7425 (eq? (ctype-kind elem) 'struct) 7426 (eq? (ctype-kind elem) 'union)) 7427 ;; Zero each byte in this aggregate slot. 7428 (let zb ((j 0)) 7429 (cond 7430 ((>= j esize) #t) 7431 (else 7432 (%push-frame-elem-lval ps (+ off j) %t-u8) 7433 (cg-push-imm (ps-cg ps) %t-u8 0) 7434 (cg-assign (ps-cg ps)) 7435 (cg-pop (ps-cg ps)) 7436 (zb (+ j 1)))))) 7437 (else 7438 (%push-frame-elem-lval ps off elem) 7439 (cg-push-imm (ps-cg ps) elem 0) 7440 (cg-assign (ps-cg ps)) 7441 (cg-pop (ps-cg ps))))) 7442 (zlp (+ k 1))))))) 7443 (else 7444 (let ((eoff (+ base-off (* i esize)))) 7445 (cond 7446 ((at-punct? ps 'lbrace) 7447 (advance ps) 7448 (%local-init-elem ps sm eoff elem #f)) 7449 (else 7450 (%local-init-elem ps sm eoff elem #t))) 7451 ;; Inter-item comma: in no-brace mode, don't eat the comma 7452 ;; that follows our LAST item (it belongs to the parent). 7453 (cond 7454 (brace? 7455 (cond ((at-punct? ps 'comma) (advance ps)))) 7456 (else 7457 (cond ((and (< (+ i 1) 7458 (cond ((< decl 0) 0) (else decl))) 7459 (at-punct? ps 'comma)) 7460 (advance ps))))) 7461 (lp (+ i 1)))))))) 7462 7463 (define (%bv-in-list? bv xs) 7464 (cond ((null? xs) #f) 7465 ((bytes=? bv (car xs)) #t) 7466 (else (%bv-in-list? bv (cdr xs))))) 7467 7468 ;; Does any leaf-name of `f` (a struct/union field tuple, possibly with 7469 ;; a nameless anon-aggregate type) appear in `seen`? Used by the 7470 ;; local-struct zero-pass to skip an anonymous member whose sub-field 7471 ;; was already written through a designator like `.a` (C11 §6.7.2.1). 7472 (define (%anon-touched? f seen) 7473 (let ((fn (car f))) 7474 (cond 7475 (fn (%bv-in-list? fn seen)) 7476 (else 7477 (let ((k (ctype-kind (cadr f)))) 7478 (cond 7479 ((or (eq? k 'struct) (eq? k 'union)) 7480 (let lp ((xs (car (cddr (ctype-ext (cadr f)))))) 7481 (cond 7482 ((null? xs) #f) 7483 ((%anon-touched? (car xs) seen) #t) 7484 (else (lp (cdr xs)))))) 7485 (else #f))))))) 7486 7487 (define (%emit-zero-field ps base-off f) 7488 ;; Keep the absolute byte-offset expression explicit: base + field offset, 7489 ;; then add the byte index used by the zero-fill loop. 7490 (let* ((fty (car (cdr f))) 7491 (foff (car (cddr f))) 7492 (fsize (ctype-size fty)) 7493 (start-off (+ base-off foff))) 7494 (let zb ((j 0)) 7495 (cond 7496 ((>= j fsize) #t) 7497 (else 7498 (%push-frame-elem-lval ps (+ start-off j) %t-u8) 7499 (cg-push-imm (ps-cg ps) %t-u8 0) 7500 (cg-assign (ps-cg ps)) 7501 (cg-pop (ps-cg ps)) 7502 (zb (+ j 1))))))) 7503 7504 (define (%parse-init-local-struct-list ps sm base-off ty) 7505 (%parse-init-local-struct-list/mode ps sm base-off ty #t)) 7506 7507 (define (%parse-init-local-struct-list/mode ps sm base-off ty brace?) 7508 ;; Track each initialized field by name in `seen`; at the closing brace 7509 ;; zero every field NOT in `seen`. Tracking by name (rather than 7510 ;; positional "remaining" fields) handles a designator jumping 7511 ;; backwards correctly — e.g. `{.y = 5}` must still zero `x`. 7512 ;; C requires every unmentioned member of an aggregate with at least 7513 ;; one designator/initializer to be zeroed (C11 §6.7.9 ¶21). 7514 ;; 7515 ;; In no-brace mode (brace elision, C99 §6.7.8 ¶22): terminate when 7516 ;; positional fields exhausted, on `}` (parent's), or on `.` designator 7517 ;; (targets parent). Don't consume trailing comma after our last field. 7518 (let ((fields (%init-struct-fields ty))) 7519 (let lp ((rest fields) (seen '())) 7520 (cond 7521 ((cond (brace? (at-punct? ps 'rbrace)) 7522 (else (or (null? rest) 7523 (at-punct? ps 'rbrace) 7524 (at-punct? ps 'dot)))) 7525 (cond (brace? (advance ps))) 7526 (for-each 7527 (lambda (f) 7528 (cond ((not (%anon-touched? f seen)) 7529 (%emit-zero-field ps base-off f)))) 7530 fields)) 7531 (else 7532 (let* ((designated? (at-punct? ps 'dot)) 7533 (target 7534 (cond 7535 (designated? 7536 (%init-field-designator ps fields)) 7537 ((null? rest) 7538 (die (tok-loc (peek ps)) "init: too many fields")) 7539 (else (car rest)))) 7540 (fname (car target)) 7541 (fty (car (cdr target))) 7542 (foff (car (cddr target))) 7543 (eoff (+ base-off foff))) 7544 (cond 7545 ((at-punct? ps 'lbrace) 7546 (advance ps) 7547 (%local-init-elem ps sm eoff fty #f)) 7548 (else 7549 (%local-init-elem ps sm eoff fty #t))) 7550 (let ((rest1 7551 (cond 7552 (designated? (%init-drop-thru-field fields fname)) 7553 (else (cdr rest))))) 7554 ;; Inter-item comma: in no-brace mode, don't eat the comma 7555 ;; that follows our LAST field (belongs to enclosing list). 7556 (cond 7557 (brace? 7558 (cond ((at-punct? ps 'comma) (advance ps)))) 7559 (else 7560 (cond ((and (not (null? rest1)) 7561 (at-punct? ps 'comma)) 7562 (advance ps))))) 7563 (lp rest1 (cons fname seen))))))))) 7564 7565 7566 ;; parse-fn-body: bind the fn-sym for recursive lookup, then parse the 7567 ;; body. Persistent entries remain reachable through the world graph; 7568 ;; function-local parser state becomes collectible after the body. 7569 (define (parse-fn-body ps sto name dt) 7570 (scope-bind! ps name (%sym name 'fn (or sto 'extern) dt #f #t)) 7571 (%parse-fn-body-inner ps name dt)) 7572 7573 (define (%parse-fn-body-inner ps name dt) 7574 (let* ((e (ctype-ext dt)) (ret (car e)) 7575 (par (cadr e)) (var (car (cddr e)))) 7576 (let ((psyms (cg-fn-begin/v (ps-cg ps) name par ret var))) 7577 (ps-fn-ctx-set! ps 7578 (%fn-ctx name ret (map cdr psyms) var '() 0)) 7579 (scope-enter! ps) 7580 (for-each (lambda (p) (scope-bind! ps (car p) (cdr p))) 7581 psyms) 7582 (expect-punct ps 'lbrace) 7583 (parse-cstmt-body ps) 7584 (expect-punct ps 'rbrace) 7585 (scope-leave! ps) 7586 (ps-fn-ctx-set! ps #f) 7587 (cg-fn-end (ps-cg ps))))) 7588 7589 (define (parse-stmt ps) 7590 (pmatch (peek ps) 7591 (($ tok? (kind PUNCT) (value lbrace)) (parse-cstmt ps)) 7592 (($ tok? (kind KW) (value if)) (parse-if-stmt ps)) 7593 (($ tok? (kind KW) (value while)) (parse-while-stmt ps)) 7594 (($ tok? (kind KW) (value do)) (parse-do-stmt ps)) 7595 (($ tok? (kind KW) (value for)) (parse-for-stmt ps)) 7596 (($ tok? (kind KW) (value switch)) (parse-switch-stmt ps)) 7597 (($ tok? (kind KW) (value return)) (parse-return-stmt ps)) 7598 (($ tok? (kind KW) (value goto)) (parse-goto-stmt ps)) 7599 (($ tok? (kind KW) (value break)) 7600 (advance ps) (expect-punct ps 'semi) (do-break ps)) 7601 (($ tok? (kind KW) (value continue)) 7602 (advance ps) (expect-punct ps 'semi) (do-continue ps)) 7603 (($ tok? (kind KW) (value case)) (parse-case-stmt ps)) 7604 (($ tok? (kind KW) (value default)) (parse-default-stmt ps)) 7605 (($ tok? (kind KW) (value _Static_assert)) (parse-static-assert! ps)) 7606 (($ tok? (kind IDENT)) 7607 (guard (and (eq? (tok-kind (peek2 ps)) 'PUNCT) 7608 (eq? (tok-value (peek2 ps)) 'colon))) 7609 (parse-labelled-stmt ps)) 7610 (else 7611 (cond ((stmt-starts-decl? ps) (parse-local-decl ps)) 7612 (else (parse-expr-stmt ps)))))) 7613 7614 (define (stmt-starts-decl? ps) 7615 (let ((t (peek ps))) 7616 (or (%tok-decl-start? ps t) 7617 ;; Storage classes only appear at declaration position; check here 7618 ;; rather than fold them into %tok-decl-start? (which is also 7619 ;; used for cast typenames where storage classes are illegal). 7620 (pmatch t 7621 (($ tok? (kind KW) (value ,v)) 7622 (or (eq? v 'auto) (eq? v 'register) (eq? v 'static) 7623 (eq? v 'extern) (eq? v 'typedef))) 7624 (else #f))))) 7625 7626 (define (parse-local-decl ps) 7627 (let-values (((sto b) (parse-decl-spec ps))) 7628 (cond 7629 ((at-punct? ps 'semi) (advance ps) #t) 7630 (else 7631 (let lp () 7632 (let-values (((n t) (parse-declarator ps b))) 7633 (handle-decl ps sto n t) 7634 (cond ((at-punct? ps 'comma) (advance ps) (lp)) 7635 (else (expect-punct ps 'semi) #t)))))))) 7636 7637 (define (parse-cstmt ps) 7638 (expect-punct ps 'lbrace) 7639 (scope-enter! ps) 7640 (parse-cstmt-body ps) 7641 (scope-leave! ps) 7642 (expect-punct ps 'rbrace) #t) 7643 7644 (define (parse-cstmt-body ps) 7645 (cond 7646 ((at-punct? ps 'rbrace) #t) 7647 ((eq? (tok-kind (peek ps)) 'EOF) 7648 (die (tok-loc (peek ps)) "EOF in cstmt")) 7649 (else (parse-stmt ps) (parse-cstmt-body ps)))) 7650 7651 (define (parse-compound-stmt ps) (parse-cstmt ps)) 7652 7653 (define (parse-if-stmt ps) 7654 (expect-kw ps 'if) 7655 (expect-punct ps 'lparen) 7656 (parse-expr ps) (rval! ps) 7657 (expect-punct ps 'rparen) 7658 (cg-ifelse (ps-cg ps) 7659 (lambda () (parse-stmt ps)) 7660 (lambda () 7661 (cond ((at-kw? ps 'else) 7662 (advance ps) (parse-stmt ps)) 7663 (else #t))))) 7664 7665 ;; cg-loop's body-thunk receives the tag from cg; the parser threads 7666 ;; it into break/continue via loop-ctx. 7667 7668 (define (parse-while-stmt ps) 7669 (expect-kw ps 'while) 7670 (expect-punct ps 'lparen) 7671 (cg-loop (ps-cg ps) 7672 (lambda () (parse-expr ps) (rval! ps)) 7673 (lambda (tag) 7674 (expect-punct ps 'rparen) 7675 (push-loop-ctx! ps 'while tag #t) 7676 (parse-stmt ps) 7677 (pop-loop-ctx! ps))) #t) 7678 7679 (define (parse-do-stmt ps) 7680 (expect-kw ps 'do) 7681 ;; `continue` in a do-while must jump to the *cond test* (C11 7682 ;; §6.8.6.2 ¶2), not to the top of the body. The scoped loop labels 7683 ;; `.top` at the condition test and `.end` after the loop, so bare 7684 ;; %continue / %break bind through hex2++ local lookup. 7685 ;; 7686 ;; Layout: 7687 ;; .scope 7688 ;; :.body 7689 ;; <body> 7690 ;; :.top ; %continue jumps here 7691 ;; <cond> 7692 ;; %if_eqz(c, %break) 7693 ;; %b(&.body) 7694 ;; :.end 7695 ;; .endscope 7696 (let* ((cg (ps-cg ps)) 7697 (tag (%cg-fresh-loop-tag cg))) 7698 (%cg-emit-many cg (list ".scope\n" 7699 ":.body\n")) 7700 (push-loop-ctx! ps 'do tag #t) 7701 (parse-stmt ps) 7702 (pop-loop-ctx! ps) 7703 (expect-kw ps 'while) (expect-punct ps 'lparen) 7704 (%cg-emit-many cg (list ":.top\n")) 7705 (parse-expr ps) (rval! ps) 7706 (expect-punct ps 'rparen) (expect-punct ps 'semi) 7707 (let ((c (cg-pop cg))) 7708 (%cg-load-truth-into cg c 't0) 7709 (%cg-emit-many cg (list "%if_eqz(t0, { %break })\n"))) 7710 (%cg-emit-many cg (list "%b(&.body)\n" 7711 ":.end\n" 7712 ".endscope\n"))) 7713 #t) 7714 7715 (define (parse-for-stmt ps) 7716 (expect-kw ps 'for) (expect-punct ps 'lparen) 7717 (scope-enter! ps) 7718 (cond 7719 ((at-punct? ps 'semi) (advance ps)) 7720 ((stmt-starts-decl? ps) (parse-local-decl ps)) 7721 (else (parse-expr ps) (cg-pop (ps-cg ps)) 7722 (expect-punct ps 'semi))) 7723 (let* ((cg (ps-cg ps)) 7724 (cond-toks (cond 7725 ((at-punct? ps 'semi) '()) 7726 (else (collect-til-top-punct ps 'semi "EOF in for-cond")))) 7727 (_ (expect-punct ps 'semi)) 7728 (step-toks (collect-til-rparen ps)) 7729 (_ (expect-punct ps 'rparen)) 7730 (tag (%cg-fresh-loop-tag cg))) 7731 ;; A C `continue` in a for-loop must run the step expression before 7732 ;; retesting the condition. Arrange the loop as: 7733 ;; jump test; top: step; test: condition; body; jump top 7734 (%cg-emit-many cg (list ".scope\n" 7735 "%b(&.test)\n" 7736 ":.top\n")) 7737 (parse-saved-expr-stmt ps step-toks) 7738 (%cg-emit-many cg (list ":.test\n")) 7739 (cond 7740 ((null? cond-toks) (cg-push-imm cg %t-i32 1)) 7741 (else (parse-saved-expr ps cond-toks) (rval! ps))) 7742 (let ((c (cg-pop cg))) 7743 (%cg-load-truth-into cg c 't0) 7744 (%cg-emit-many cg (list "%if_eqz(t0, { %break })\n"))) 7745 (push-loop-ctx! ps 'for tag #t) 7746 (parse-stmt ps) 7747 (pop-loop-ctx! ps) 7748 (%cg-emit-many cg (list "%b(&.top)\n" 7749 ":.end\n" 7750 ".endscope\n"))) 7751 (scope-leave! ps) #t) 7752 7753 (define (parse-saved-expr ps toks) 7754 (let ((sv (ps-iter ps))) 7755 (ps-iter-set! ps (make-list-iter (append toks (list (make-tok 'EOF #f #f))))) 7756 (parse-expr ps) 7757 (ps-iter-set! ps sv))) 7758 7759 (define (parse-saved-expr-stmt ps toks) 7760 (cond 7761 ((null? toks) #t) 7762 (else (parse-saved-expr ps toks) (cg-pop (ps-cg ps))))) 7763 7764 (define (collect-til-top-punct ps punct err) 7765 (let loop ((acc '()) (d 0)) 7766 (let ((t (peek ps))) 7767 (cond 7768 ((eq? (tok-kind t) 'EOF) 7769 (die (tok-loc t) err)) 7770 ((and (zero? d) (eq? (tok-kind t) 'PUNCT) 7771 (eq? (tok-value t) punct)) (reverse acc)) 7772 (else 7773 (let ((nt (advance ps))) 7774 (loop (cons nt acc) 7775 (cond ((not (eq? (tok-kind nt) 'PUNCT)) d) 7776 ((or (eq? (tok-value nt) 'lparen) 7777 (eq? (tok-value nt) 'lbrack)) (+ d 1)) 7778 ((or (eq? (tok-value nt) 'rparen) 7779 (eq? (tok-value nt) 'rbrack)) (- d 1)) 7780 (else d))))))))) 7781 7782 (define (collect-til-rparen ps) 7783 (collect-til-top-punct ps 'rparen "EOF in for-step")) 7784 7785 (define (parse-switch-stmt ps) 7786 (expect-kw ps 'switch) (expect-punct ps 'lparen) 7787 (parse-expr ps) (rval! ps) 7788 (expect-punct ps 'rparen) 7789 ;; Switch's break-target tag is the swctx's end-tag — cg owns it, 7790 ;; and we read it back so cg-break inside the switch body emits a 7791 ;; tag cg actually labels. 7792 (let* ((sw (cg-switch-begin (ps-cg ps))) 7793 (tg (swctx-end-tag sw))) 7794 (push-loop-ctx-sw! ps 'switch tg sw) 7795 (parse-stmt ps) 7796 (pop-loop-ctx! ps) 7797 (cg-switch-end (ps-cg ps) sw))) 7798 7799 (define (parse-case-stmt ps) 7800 (expect-kw ps 'case) 7801 (let ((v (parse-const-int ps))) 7802 (expect-punct ps 'colon) 7803 (cg-switch-case (ps-cg ps) (innermost-sw ps) v) 7804 (parse-stmt ps))) 7805 7806 (define (parse-default-stmt ps) 7807 (expect-kw ps 'default) (expect-punct ps 'colon) 7808 (cg-switch-default (ps-cg ps) (innermost-sw ps)) 7809 (parse-stmt ps)) 7810 7811 (define (parse-return-stmt ps) 7812 (expect-kw ps 'return) 7813 (cond 7814 ((at-punct? ps 'semi) (advance ps) (cg-return (ps-cg ps))) 7815 (else 7816 (let* ((fc (ps-fn-ctx ps)) 7817 (rty (and fc (fn-ctx-return-type fc))) 7818 (rk (and rty (ctype-kind rty)))) 7819 (cond 7820 ;; Struct/union return — leave the source as a struct lval; 7821 ;; cg-return copies bytes into the function's return slot. 7822 ;; (P1.md §Arguments and return values.) 7823 ((or (eq? rk 'struct) (eq? rk 'union)) 7824 (parse-expr ps) 7825 (cg-return (ps-cg ps))) 7826 (else 7827 (parse-expr ps) (rval! ps) 7828 (cond 7829 ((and fc (not (eq? rk 'void))) 7830 (cg-cast (ps-cg ps) rty)) 7831 (else #t)) 7832 (cg-return (ps-cg ps))))) 7833 (expect-punct ps 'semi)))) 7834 7835 (define (parse-goto-stmt ps) 7836 (expect-kw ps 'goto) 7837 (let ((t (advance ps))) 7838 (cond ((eq? (tok-kind t) 'IDENT) 7839 (cg-goto (ps-cg ps) (tok-value t))) 7840 (else (die (tok-loc t) "label?")))) 7841 (expect-punct ps 'semi)) 7842 7843 (define (parse-labelled-stmt ps) 7844 (let ((t (advance ps))) 7845 (expect-punct ps 'colon) 7846 (cg-emit-label (ps-cg ps) (tok-value t)) 7847 (parse-stmt ps))) 7848 7849 (define (parse-expr-stmt ps) 7850 (cond 7851 ((at-punct? ps 'semi) (advance ps) #t) 7852 (else (parse-expr ps) (cg-pop (ps-cg ps)) 7853 (expect-punct ps 'semi)))) 7854 7855 (define (push-loop-ctx! ps k tg hc) 7856 (ps-loops-set! ps (cons (%loop-ctx k tg hc) (ps-loops ps)))) 7857 (define (push-loop-ctx-sw! ps k tg sw) 7858 (ps-loops-set! ps 7859 (cons (%loop-ctx k (cons tg sw) #f) (ps-loops ps)))) 7860 (define (pop-loop-ctx! ps) 7861 (ps-loops-set! ps (cdr (ps-loops ps)))) 7862 (define (do-break ps) 7863 (let ((c (innermost-loop ps))) 7864 (cond 7865 ((not c) (die #f "break outside")) 7866 ((eq? (loop-ctx-kind c) 'switch) 7867 (cg-break (ps-cg ps) (car (loop-ctx-tag c)))) 7868 (else (cg-break (ps-cg ps) (loop-ctx-tag c)))))) 7869 (define (do-continue ps) 7870 (let ((c (innermost-cont ps))) 7871 (cond ((not c) (die #f "cont outside")) 7872 (else (cg-continue (ps-cg ps) (loop-ctx-tag c)))))) 7873 (define (innermost-loop ps) 7874 (cond ((null? (ps-loops ps)) #f) (else (car (ps-loops ps))))) 7875 (define (innermost-cont ps) 7876 (let lp ((xs (ps-loops ps))) 7877 (cond ((null? xs) #f) 7878 ((eq? (loop-ctx-kind (car xs)) 'switch) (lp (cdr xs))) 7879 (else (car xs))))) 7880 (define (innermost-sw ps) 7881 (let lp ((xs (ps-loops ps))) 7882 (cond ((null? xs) (die #f "case outside switch")) 7883 ((eq? (loop-ctx-kind (car xs)) 'switch) 7884 (cdr (loop-ctx-tag (car xs)))) 7885 (else (lp (cdr xs)))))) 7886 7887 (define %binop-bp 7888 (list 7889 (cons 'comma (cons 1 2)) 7890 (cons 'assign (cons 4 3)) (cons 'plus-eq (cons 4 3)) 7891 (cons 'minus-eq (cons 4 3)) (cons 'star-eq (cons 4 3)) 7892 (cons 'slash-eq (cons 4 3)) (cons 'pct-eq (cons 4 3)) 7893 (cons 'shl-eq (cons 4 3)) (cons 'shr-eq (cons 4 3)) 7894 (cons 'amp-eq (cons 4 3)) (cons 'caret-eq (cons 4 3)) 7895 (cons 'bar-eq (cons 4 3)) (cons 'qmark (cons 6 5)) 7896 (cons 'lor (cons 10 11)) (cons 'land (cons 20 21)) 7897 (cons 'bar (cons 30 31)) (cons 'caret (cons 40 41)) 7898 (cons 'amp (cons 50 51)) 7899 (cons 'eq2 (cons 60 61)) (cons 'ne (cons 60 61)) 7900 (cons 'lt (cons 70 71)) (cons 'le (cons 70 71)) 7901 (cons 'gt (cons 70 71)) (cons 'ge (cons 70 71)) 7902 (cons 'shl (cons 80 81)) (cons 'shr (cons 80 81)) 7903 (cons 'plus (cons 90 91)) (cons 'minus (cons 90 91)) 7904 (cons 'star (cons 100 101)) (cons 'slash (cons 100 101)) 7905 (cons 'pct (cons 100 101)))) 7906 7907 (define (binop-bp-of s) (alist-ref/eq s %binop-bp)) 7908 7909 (define (punct-to-cgop s) 7910 (cond ((eq? s 'plus) 'add) ((eq? s 'minus) 'sub) 7911 ((eq? s 'star) 'mul) ((eq? s 'slash) 'div) 7912 ((eq? s 'pct) 'rem) ((eq? s 'amp) 'and) 7913 ((eq? s 'bar) 'or) ((eq? s 'caret) 'xor) 7914 ((eq? s 'shl) 'shl) ((eq? s 'shr) 'shr) 7915 ((eq? s 'eq2) 'eq) ((eq? s 'ne) 'ne) 7916 ((eq? s 'lt) 'lt) ((eq? s 'le) 'le) 7917 ((eq? s 'gt) 'gt) ((eq? s 'ge) 'ge) 7918 (else (die #f "binop" s)))) 7919 7920 (define (compound-op s) 7921 (cond ((eq? s 'plus-eq) 'add) ((eq? s 'minus-eq) 'sub) 7922 ((eq? s 'star-eq) 'mul) ((eq? s 'slash-eq) 'div) 7923 ((eq? s 'pct-eq) 'rem) ((eq? s 'shl-eq) 'shl) 7924 ((eq? s 'shr-eq) 'shr) ((eq? s 'amp-eq) 'and) 7925 ((eq? s 'caret-eq) 'xor) ((eq? s 'bar-eq) 'or) 7926 (else #f))) 7927 7928 (define (parse-expr ps) (parse-expr-bp ps 0)) 7929 7930 (define (parse-expr-bp ps mn) 7931 (parse-unary ps) (parse-binary-rhs ps mn)) 7932 7933 (define (parse-binary-rhs ps mn) 7934 (let ((t (peek ps))) 7935 (cond 7936 ((not (eq? (tok-kind t) 'PUNCT)) #t) 7937 (else 7938 (let ((bp (binop-bp-of (tok-value t)))) 7939 (cond 7940 ((not bp) #t) 7941 ((< (car bp) mn) #t) 7942 (else 7943 (let ((op (tok-value t)) (rb (cdr bp))) 7944 (advance ps) 7945 (cond 7946 ((eq? op 'comma) 7947 ;; lhs has been parsed; discard it and evaluate rhs. 7948 ;; Result of the comma expr is the rhs's rval. 7949 (cg-pop (ps-cg ps)) 7950 (parse-expr-bp ps rb) (rval! ps)) 7951 ((eq? op 'assign) 7952 ;; Struct/union assignment must memcpy the whole 7953 ;; aggregate. The scalar cg-assign path loads/stores 7954 ;; via a single 8-byte register, dropping any field at 7955 ;; offset >= 8. Detect via the lhs (already on the 7956 ;; vstack) and route to cg-assign-struct, which keeps 7957 ;; rhs as an lvalue and emits a memcpy. 7958 (let* ((lhs-top (cg-top (ps-cg ps))) 7959 (lk (cond ((and (opnd? lhs-top) (opnd-lval? lhs-top)) 7960 (ctype-kind (opnd-type lhs-top))) 7961 (else #f)))) 7962 (cond 7963 ((or (eq? lk 'struct) (eq? lk 'union)) 7964 (parse-expr-bp ps rb) 7965 (cg-assign-struct (ps-cg ps))) 7966 (else 7967 (parse-expr-bp ps rb) (rval! ps) 7968 (cg-assign (ps-cg ps)))))) 7969 ((compound-op op) 7970 (let ((b (compound-op op))) 7971 (cg-dup (ps-cg ps)) 7972 (cg-load (ps-cg ps)) 7973 (parse-expr-bp ps rb) (rval! ps) 7974 ;; Skip the usual arithmetic conversion for shift 7975 ;; compounds (`<<=` / `>>=`) so the lhs's signedness 7976 ;; survives; cg-binop's shr branch then picks the 7977 ;; right arithmetic-vs-logical opcode. 7978 (cond ((or (eq? b 'shl) (eq? b 'shr)) #t) 7979 (else (cg-arith-conv (ps-cg ps)))) 7980 (cg-binop (ps-cg ps) b) 7981 (cg-assign (ps-cg ps)))) 7982 ((eq? op 'qmark) 7983 (rval! ps) 7984 (cg-ifelse-merge (ps-cg ps) 7985 (lambda () 7986 (parse-expr-bp ps 0) (rval! ps)) 7987 (lambda () 7988 (expect-punct ps 'colon) 7989 (parse-expr-bp ps rb) (rval! ps)))) 7990 ((eq? op 'land) 7991 (rval! ps) 7992 ;; Both branches must push i32 0/1. Right side is 7993 ;; coerced via `cg-cast bool` so the merge slot 7994 ;; carries i32 (per §H.2). 7995 (cg-ifelse-merge (ps-cg ps) 7996 (lambda () 7997 (parse-expr-bp ps rb) (rval! ps) 7998 (cg-cast (ps-cg ps) %t-bool) 7999 (cg-cast (ps-cg ps) %t-i32)) 8000 (lambda () 8001 (cg-push-imm (ps-cg ps) %t-i32 0)))) 8002 ((eq? op 'lor) 8003 (rval! ps) 8004 (cg-ifelse-merge (ps-cg ps) 8005 (lambda () 8006 (cg-push-imm (ps-cg ps) %t-i32 1)) 8007 (lambda () 8008 (parse-expr-bp ps rb) (rval! ps) 8009 (cg-cast (ps-cg ps) %t-bool) 8010 (cg-cast (ps-cg ps) %t-i32)))) 8011 (else 8012 (rval! ps) (cg-promote (ps-cg ps)) 8013 (parse-expr-bp ps rb) (rval! ps) 8014 (cg-promote (ps-cg ps)) 8015 ;; Shifts (C 6.5.7) only require integer promotion of 8016 ;; each operand individually; the usual arithmetic 8017 ;; conversion would force the lhs into an unsigned 8018 ;; common type when the rhs is unsigned, breaking 8019 ;; arithmetic-shift semantics for `signed >> unsigned`. 8020 (cond ((or (eq? op 'shl) (eq? op 'shr)) #t) 8021 (else (cg-arith-conv (ps-cg ps)))) 8022 (cg-binop (ps-cg ps) (punct-to-cgop op)))) 8023 (parse-binary-rhs ps mn))))))))) 8024 8025 (define (parse-unary ps) 8026 (pmatch (peek ps) 8027 (($ tok? (kind PUNCT) (value amp)) 8028 (advance ps) (parse-unary ps) 8029 (cg-take-addr (ps-cg ps))) 8030 (($ tok? (kind PUNCT) (value star)) 8031 (advance ps) (parse-unary ps) (rval! ps) 8032 (cg-push-deref (ps-cg ps))) 8033 (($ tok? (kind PUNCT) (value plus)) 8034 (advance ps) (parse-unary ps) 8035 (rval! ps) (cg-promote (ps-cg ps))) 8036 (($ tok? (kind PUNCT) (value minus)) 8037 (advance ps) (parse-unary ps) 8038 (rval! ps) (cg-promote (ps-cg ps)) 8039 (cg-unop (ps-cg ps) 'neg)) 8040 (($ tok? (kind PUNCT) (value tilde)) 8041 (advance ps) (parse-unary ps) 8042 (rval! ps) (cg-promote (ps-cg ps)) 8043 (cg-unop (ps-cg ps) 'bnot)) 8044 (($ tok? (kind PUNCT) (value bang)) 8045 (advance ps) (parse-unary ps) (rval! ps) 8046 (cg-unop (ps-cg ps) 'lnot)) 8047 (($ tok? (kind PUNCT) (value inc)) 8048 (advance ps) (parse-unary ps) 8049 (cg-dup (ps-cg ps)) 8050 (cg-load (ps-cg ps)) 8051 (cg-push-imm (ps-cg ps) %t-i32 1) 8052 (cg-binop (ps-cg ps) 'add) (cg-assign (ps-cg ps))) 8053 (($ tok? (kind PUNCT) (value dec)) 8054 (advance ps) (parse-unary ps) 8055 (cg-dup (ps-cg ps)) 8056 (cg-load (ps-cg ps)) 8057 (cg-push-imm (ps-cg ps) %t-i32 1) 8058 (cg-binop (ps-cg ps) 'sub) (cg-assign (ps-cg ps))) 8059 (($ tok? (kind PUNCT) (value lparen)) (parse-cast-or-unary ps)) 8060 (($ tok? (kind KW) (value sizeof)) 8061 (advance ps) 8062 (cond 8063 ((at-punct? ps 'lparen) 8064 (advance ps) 8065 (cond 8066 ((token-is-decl? ps) 8067 (let*-values (((_sto bty) (parse-decl-spec ps)) 8068 ((_n ty) (parse-declarator ps bty))) 8069 (expect-punct ps 'rparen) 8070 (cg-push-imm (ps-cg ps) %t-word-u 8071 (max (ctype-size ty) 0)))) 8072 ;; A string literal has type char[N] until ordinary expression 8073 ;; conversion. parse-primary deliberately lowers strings as 8074 ;; pointers for runtime expressions, so preserve the array bound 8075 ;; here before entering that path. Kit's KIT_SLICE_LIT relies on 8076 ;; exactly this `sizeof("...") - 1` shape during parser setup. 8077 ((and (eq? (tok-kind (peek ps)) 'STR) 8078 (eq? (tok-kind (peek2 ps)) 'PUNCT) 8079 (eq? (tok-value (peek2 ps)) 'rparen)) 8080 (let ((n (+ (bytevector-length (tok-value (peek ps))) 1))) 8081 (advance ps) 8082 (expect-punct ps 'rparen) 8083 (cg-push-imm (ps-cg ps) %t-word-u n))) 8084 (else 8085 ;; sizeof(EXPR): C semantics — operand is NOT evaluated. 8086 ;; Snapshot cg state, parse the expr to learn its type, 8087 ;; then rewind to discard any code emission and vstack 8088 ;; pushes the parse incurred (e.g. `sizeof(x++)` must not 8089 ;; increment x). cf. CC.md §Expressions. 8090 (let ((tag (cg-snapshot (ps-cg ps)))) 8091 (parse-expr ps) (expect-punct ps 'rparen) 8092 (let* ((tp (cg-top (ps-cg ps))) 8093 (sz (max (ctype-size (opnd-type tp)) 0))) 8094 (cg-rewind (ps-cg ps) tag) 8095 (cg-push-imm (ps-cg ps) %t-word-u sz)))))) 8096 (else 8097 ;; sizeof EXPR (no parens) — same no-eval rule. 8098 (cond 8099 ((eq? (tok-kind (peek ps)) 'STR) 8100 (let ((n (+ (bytevector-length (tok-value (peek ps))) 1))) 8101 (advance ps) 8102 (cg-push-imm (ps-cg ps) %t-word-u n))) 8103 (else 8104 (let ((tag (cg-snapshot (ps-cg ps)))) 8105 (parse-unary ps) 8106 (let* ((tp (cg-top (ps-cg ps))) 8107 (sz (max (ctype-size (opnd-type tp)) 0))) 8108 (cg-rewind (ps-cg ps) tag) 8109 (cg-push-imm (ps-cg ps) %t-word-u sz)))))))) 8110 (($ tok? (kind KW) (value _Alignof)) 8111 (advance ps) 8112 (expect-punct ps 'lparen) 8113 (cond 8114 ((token-is-decl? ps) 8115 (let*-values (((_sto bty) (parse-decl-spec ps)) 8116 ((_n ty) (parse-declarator ps bty))) 8117 (expect-punct ps 'rparen) 8118 (cg-push-imm (ps-cg ps) %t-word-u 8119 (max (ctype-align ty) 1)))) 8120 (else 8121 (let ((tag (cg-snapshot (ps-cg ps)))) 8122 (parse-expr ps) 8123 (expect-punct ps 'rparen) 8124 (let* ((tp (cg-top (ps-cg ps))) 8125 (al (max (ctype-align (opnd-type tp)) 1))) 8126 (cg-rewind (ps-cg ps) tag) 8127 (cg-push-imm (ps-cg ps) %t-word-u al)))))) 8128 (else (parse-postfix ps)))) 8129 8130 (define (token-is-decl? ps) (%tok-decl-start? ps (peek ps))) 8131 8132 (define (parse-cast-or-unary ps) 8133 (cond 8134 ((or (%tok-decl-start? ps (peek2 ps)) 8135 ;; A leading GNU attribute on the cast typename 8136 ;; (e.g. `((__attribute__((...)) int(*)(void))ptr)()`) — eaten 8137 ;; by parse-decl-spec along with the rest of the decl-spec. 8138 (let ((t (peek2 ps))) 8139 (and (eq? (tok-kind t) 'KW) (eq? (tok-value t) '__attribute__)))) 8140 (advance ps) 8141 (let*-values (((_sto bty) (parse-decl-spec ps)) 8142 ((_n ty) (parse-declarator ps bty))) 8143 (expect-punct ps 'rparen) 8144 (cond 8145 ;; (T){ ... } — compound literal (C99 §6.5.2.5). Looks like a 8146 ;; cast at the typename level but disambiguates on the 8147 ;; following `{` and is a postfix lvalue, not a cast operator. 8148 ((at-punct? ps 'lbrace) (parse-compound-literal ps ty)) 8149 (else 8150 (parse-unary ps) 8151 ;; Cast operand undergoes lvalue conversion first (C semantics): 8152 ;; arrays decay to pointers, lvals become rvals. cg-cast then 8153 ;; bit-casts the resulting rval to the target type. 8154 (rval! ps) 8155 (cg-cast (ps-cg ps) ty))))) 8156 (else (advance ps) (parse-expr ps) 8157 (expect-punct ps 'rparen) 8158 (parse-postfix-rest ps)))) 8159 8160 ;; -------------------------------------------------------------------- 8161 ;; Compound literals (C99 §6.5.2.5): (T){ init-list } 8162 ;; 8163 ;; Block scope — allocate a fresh frame slot sized for T, drive the 8164 ;; existing local-aggregate initializer path against it, then push a 8165 ;; frame lval typed as T. The literal is an lvalue with automatic 8166 ;; storage tied to the enclosing block, so &literal, literal.field, 8167 ;; literal[i], byval pass, and array decay all chain through the 8168 ;; existing primitives (cg-take-addr / cg-push-field / cg-decay-array 8169 ;; via rval!). 8170 ;; 8171 ;; File scope — handled out-of-band in %const-init-piece (incl. its `&` 8172 ;; arm) via %emit-fs-compound-literal: pieces go to .data under a fresh 8173 ;; cc__cl_N label and the enclosing initializer takes a (label-ref . LBL) 8174 ;; piece. Reaching parse-compound-literal at file scope would mean an 8175 ;; expression context outside an initializer (which file scope doesn't 8176 ;; have), so this entry point still rejects it. 8177 ;; -------------------------------------------------------------------- 8178 (define (parse-compound-literal ps ty) 8179 (cond 8180 ((not (ps-fn-ctx ps)) 8181 (die (tok-loc (peek ps)) "compound literal at file scope: unsupported"))) 8182 (let* ((sz (max (ctype-size ty) 1)) 8183 (al (max (ctype-align ty) 1)) 8184 (sl (cg-alloc-slot (ps-cg ps) sz al)) 8185 ;; Synthetic sym: parse-init-local-aggregate only reads 8186 ;; sym-slot at its top-level entry to seed base-off; the 8187 ;; recursive helpers thread `sm` along but never read other 8188 ;; fields. The name is unbound and never enters scope. 8189 (sm (%sym "__cl" 'var 'auto ty sl #t))) 8190 (cond 8191 ((or (eq? (ctype-kind ty) 'arr) 8192 (eq? (ctype-kind ty) 'struct) 8193 (eq? (ctype-kind ty) 'union)) 8194 (parse-init-local-aggregate ps sm ty)) 8195 (else 8196 ;; Scalar (T){expr [,]} — parse-init-local-aggregate's brace arm 8197 ;; only handles aggregates, so emit the single-element store 8198 ;; here directly. 8199 (expect-punct ps 'lbrace) 8200 (cg-push (ps-cg ps) (%opnd 'frame ty sl #t)) 8201 (parse-expr-bp ps 4) (rval! ps) 8202 (cg-cast (ps-cg ps) ty) 8203 (cg-assign (ps-cg ps)) (cg-pop (ps-cg ps)) 8204 (cond ((at-punct? ps 'comma) (advance ps))) 8205 (expect-punct ps 'rbrace))) 8206 ;; The literal is an lvalue with automatic storage. ctype-size may 8207 ;; have been resolved by parse-init-local-aggregate (e.g. (int[]) 8208 ;; gets its bound fixed in-place); we re-fetch via the slot's type 8209 ;; pointer (ty) which the init code mutated. 8210 (cg-push (ps-cg ps) (%opnd 'frame ty sl #t)) 8211 (parse-postfix-rest ps))) 8212 8213 (define (parse-postfix ps) 8214 (parse-primary ps) (parse-postfix-rest ps)) 8215 8216 (define (parse-postfix-rest ps) 8217 (let lp () 8218 (pmatch (peek ps) 8219 (($ tok? (kind PUNCT) (value lbrack)) 8220 (advance ps) (rval! ps) 8221 (parse-expr ps) (rval! ps) 8222 (expect-punct ps 'rbrack) 8223 (cg-binop (ps-cg ps) 'add) 8224 (cg-push-deref (ps-cg ps)) (lp)) 8225 (($ tok? (kind PUNCT) (value lparen)) 8226 (advance ps) (rval-not-fn! ps) 8227 (let* ((fn-ty (call-fn-type (ps-cg ps))) 8228 (n (parse-call-args ps fn-ty)) 8229 ;; has-result? = #f for known void returns. Skips the 8230 ;; wasted ST a0 → frame-slot spill that cg-call would 8231 ;; otherwise emit for void calls. 8232 (has-result? 8233 (cond 8234 ((not fn-ty) #t) 8235 ((eq? (ctype-kind (car (ctype-ext fn-ty))) 'void) #f) 8236 (else #t)))) 8237 (expect-punct ps 'rparen) 8238 (cg-call (ps-cg ps) n has-result?) 8239 ;; Maintain parse's "one rval per expression" invariant so 8240 ;; comma / parse-expr-stmt / for-init/step pop sites stay 8241 ;; simple. The placeholder is vstack-only and never 8242 ;; materialized (cg-pop is a vstack op, no emit). 8243 (cond ((not has-result?) 8244 (cg-push-imm (ps-cg ps) %t-i32 0))) 8245 (lp))) 8246 (($ tok? (kind PUNCT) (value dot)) 8247 (advance ps) 8248 (pmatch (advance ps) 8249 (($ tok? (kind IDENT) (value ,n)) 8250 (cg-push-field (ps-cg ps) n) (lp)) 8251 (($ tok? (loc ,l)) (die l "expected field name")))) 8252 (($ tok? (kind PUNCT) (value arrow)) 8253 (advance ps) 8254 (pmatch (advance ps) 8255 (($ tok? (kind IDENT) (value ,n)) 8256 ;; ptr -> field: load the pointer to rval, deref to reach 8257 ;; the struct lval, then push the field. 8258 (rval! ps) 8259 (cg-push-deref (ps-cg ps)) 8260 (cg-push-field (ps-cg ps) n) (lp)) 8261 (($ tok? (loc ,l)) (die l "expected field name")))) 8262 (($ tok? (kind PUNCT) (value inc)) 8263 (advance ps) 8264 (cg-postinc (ps-cg ps)) (lp)) 8265 (($ tok? (kind PUNCT) (value dec)) 8266 (advance ps) 8267 (cg-postdec (ps-cg ps)) (lp)) 8268 (else #t)))) 8269 8270 ;; call-fn-type cg -> ctype-or-#f 8271 ;; The function operand sits at the top of the vstack when 8272 ;; parse-call-args runs (just after rval-not-fn!). Its type may be 8273 ;; `fn` directly (named callee) or `ptr -> fn` (function pointer). 8274 ;; Returns the underlying `fn` ctype, or #f if the operand isn't 8275 ;; recognizably callable (callsite still works — no per-arg cast). 8276 (define (call-fn-type cg) 8277 (let* ((tp (cg-top cg))) 8278 (cond 8279 ((not tp) #f) 8280 (else 8281 (let* ((ty (opnd-type tp)) 8282 (k (ctype-kind ty))) 8283 (cond 8284 ((eq? k 'fn) ty) 8285 ((eq? k 'ptr) 8286 (let ((pe (ctype-ext ty))) 8287 (cond ((and pe (eq? (ctype-kind pe) 'fn)) pe) 8288 (else #f)))) 8289 (else #f))))))) 8290 8291 ;; param-types-of fn-ty -> (params variadic?) with a #f fallback. 8292 (define (call-fn-param-info fn-ty) 8293 (cond 8294 ((not fn-ty) (cons '() #f)) 8295 (else 8296 (let ((ext (ctype-ext fn-ty))) 8297 (cons (cadr ext) (car (cddr ext))))))) 8298 8299 ;; parse-call-args ps fn-ty -> arg-count 8300 ;; Casts each fixed arg to the declared param type (CC.md §K.5). 8301 ;; For variadic args (index >= named-arg count, when variadic? = #t) 8302 ;; applies cg-promote (CC.md §G.1). 8303 (define (parse-call-args ps fn-ty) 8304 (cond 8305 ((at-punct? ps 'rparen) 0) 8306 (else 8307 (let* ((info (call-fn-param-info fn-ty)) 8308 (params (car info)) 8309 (var? (cdr info)) 8310 (nfix (length params))) 8311 (let lp ((n 0) (rem params)) 8312 (parse-expr-bp ps 4) (rval! ps) 8313 (cond 8314 ;; Fixed-arg: cast to declared param type. param entry shape 8315 ;; is (name . ctype) per cg-fn-begin's contract. 8316 ((not (null? rem)) 8317 (cg-cast (ps-cg ps) (cdr (car rem)))) 8318 ;; Variadic position (n >= nfix and var? is true): promote. 8319 (var? 8320 (cg-promote (ps-cg ps)))) 8321 (let ((m (+ n 1)) 8322 (rest (if (null? rem) '() (cdr rem)))) 8323 (cond ((at-punct? ps 'comma) (advance ps) (lp m rest)) 8324 (else m)))))))) 8325 8326 ;; -------------------------------------------------------------------- 8327 ;; __builtin_va_* (§G.2). va_list / va_start / va_arg / va_end in 8328 ;; <stdarg.h> alias these. Each is parsed as: name '(' args ')'. 8329 ;; va_start(ap, last) — last is parsed and discarded; cg only needs 8330 ;; the variadic-first-slot offset, which it already tracks. 8331 ;; va_arg(ap, T) — T is a type-name; result rval has that type. 8332 ;; va_end(ap) — no-op codegen; just consumes ap. 8333 ;; 8334 ;; Pushes a single imm 0 for va_start / va_end so they fit as 8335 ;; expression statements; va_arg pushes the rval. 8336 ;; -------------------------------------------------------------------- 8337 (define (parse-builtin-va-start ps) 8338 (advance ps) ; IDENT 8339 (expect-punct ps 'lparen) 8340 (parse-expr-bp ps 4) ; ap (must be lval) 8341 (expect-punct ps 'comma) 8342 ;; "last" is parsed for syntactic completeness then dropped — cg 8343 ;; doesn't need it; the variadic-first-slot was determined at 8344 ;; cg-fn-begin/v time. 8345 (parse-expr-bp ps 4) (cg-pop (ps-cg ps)) 8346 (expect-punct ps 'rparen) 8347 (cg-va-start (ps-cg ps)) 8348 ;; Push a placeholder rval so the call expression has a value 8349 ;; (matches va_start's "void" but our parser expects all 8350 ;; expressions to leave one rval). 8351 (cg-push-imm (ps-cg ps) %t-i32 0)) 8352 8353 (define (parse-builtin-va-arg ps) 8354 (advance ps) ; IDENT 8355 (expect-punct ps 'lparen) 8356 (parse-expr-bp ps 4) ; ap (lval) 8357 (expect-punct ps 'comma) 8358 (let*-values (((_sto bty) (parse-decl-spec ps)) 8359 ((_n ty) (parse-declarator ps bty))) 8360 (expect-punct ps 'rparen) 8361 (cg-va-arg (ps-cg ps) ty))) 8362 8363 (define (parse-builtin-expect ps) 8364 ;; GCC `__builtin_expect(EXPR, EXPECTED)` — branch-prediction hint. 8365 ;; We ignore the hint and emit just the value of EXPR. 8366 (advance ps) ; IDENT 8367 (expect-punct ps 'lparen) 8368 (parse-expr-bp ps 4) (rval! ps) ; result 8369 (expect-punct ps 'comma) 8370 (parse-expr-bp ps 4) (cg-pop (ps-cg ps)) ; expected (drop) 8371 (expect-punct ps 'rparen)) 8372 8373 (define (parse-builtin-va-end ps) 8374 (advance ps) ; IDENT 8375 (expect-punct ps 'lparen) 8376 (parse-expr-bp ps 4) ; ap 8377 (expect-punct ps 'rparen) 8378 (cg-va-end (ps-cg ps)) 8379 (cg-push-imm (ps-cg ps) %t-i32 0)) 8380 8381 (define (parse-builtin-offsetof ps) 8382 ;; Reuse the constant aggregate-designator parser in ordinary expression 8383 ;; context; offsetof emits no code beyond materializing its integer value. 8384 (advance ps) ; IDENT 8385 (let ((v (%const-builtin-offsetof ps))) 8386 (cg-push-imm (ps-cg ps) (cdr v) (car v)))) 8387 8388 (define (parse-primary ps) 8389 (let ((t (peek ps))) 8390 (pmatch t 8391 (($ tok? (kind INT) (value ,n)) 8392 (advance ps) 8393 (cg-push-imm (ps-cg ps) (%c-int-type n) (%c-int-raw n))) 8394 (($ tok? (kind CHAR) (value ,c)) 8395 (advance ps) 8396 ;; C99 §6.4.4.4: an integer character constant has type int. 8397 (cg-push-imm (ps-cg ps) %t-i32 c)) 8398 (($ tok? (kind STR) (value ,s)) 8399 (advance ps) 8400 (cg-push-string (ps-cg ps) s)) 8401 (($ tok? (kind IDENT) (value ,n)) 8402 (cond 8403 ((bv= n "__builtin_va_start") (parse-builtin-va-start ps)) 8404 ((bv= n "__builtin_va_arg") (parse-builtin-va-arg ps)) 8405 ((bv= n "__builtin_va_end") (parse-builtin-va-end ps)) 8406 ((bv= n "__builtin_expect") (parse-builtin-expect ps)) 8407 ((bv= n "__builtin_offsetof") (parse-builtin-offsetof ps)) 8408 (else 8409 (let ((sm (scope-lookup ps n))) 8410 (advance ps) 8411 (cond 8412 ((not sm) (die (tok-loc t) "undecl" n)) 8413 ((eq? (sym-kind sm) 'enum-const) 8414 (cg-push-imm (ps-cg ps) %t-i32 (sym-slot sm))) 8415 (else (cg-push-sym (ps-cg ps) sm))))))) 8416 (($ tok? (kind PUNCT) (value lparen)) 8417 (advance ps) (parse-expr ps) (expect-punct ps 'rparen)) 8418 (else (die (tok-loc t) "unexp" (tok-value t)))))) 8419 8420 (define (rval! ps) 8421 (let ((tp (cg-top (ps-cg ps)))) 8422 (cond ((and tp (opnd? tp) (opnd-lval? tp)) 8423 (cg-load (ps-cg ps))) 8424 (else #t)))) 8425 8426 (define (rval-not-fn! ps) 8427 (let ((tp (cg-top (ps-cg ps)))) 8428 (cond ((and tp (opnd? tp) (opnd-lval? tp) 8429 (not (ctype-is-fn? (opnd-type tp)))) 8430 (cg-load (ps-cg ps))) 8431 (else #t)))) 8432 ;; cc/main.scm — driver. Argv, file I/O, ties phases together. 8433 8434 ;; -------------------------------------------------------------------- 8435 ;; CLI: cc [--cc-debug] [--cc-trace-emit] [--lib=PFX] 8436 ;; <input.c> <output.P1pp> 8437 ;; 8438 ;; scheme1 passes (argv) as a list of bvs; argv[0] is "scheme1", argv[1] 8439 ;; is the catm'd compiler source path, argv[2..] are the user-facing 8440 ;; positional args. cc-main strips the first two. 8441 ;; -------------------------------------------------------------------- 8442 8443 (define (%cc-slurp path) 8444 (let ((r (open-input path))) 8445 (cond ((not (car r)) 8446 (die #f "cannot open input" path))) 8447 (let* ((p (cdr r)) 8448 (rd (read-all p))) 8449 (close p) 8450 (cond ((not (car rd)) (die #f "read failed" path))) 8451 (cdr rd)))) 8452 8453 (define (%cc-write path bv) 8454 (let ((r (open-output path))) 8455 (cond ((not (car r)) 8456 (die #f "cannot open output" path))) 8457 (let ((p (cdr r))) 8458 (write-bv-fd (port-fd p) bv) 8459 (close p) 8460 0))) 8461 8462 (define (%cc-write-cg path cg) 8463 (let ((r (open-output path))) 8464 (cond ((not (car r)) 8465 (die #f "cannot open output" path))) 8466 (let ((p (cdr r))) 8467 (%cg-write-finalized-fd cg (port-fd p)) 8468 (close p) 8469 0))) 8470 8471 ;; CC_DEBUG=1 in the env doesn't fly here (no getenv); instead, scan 8472 ;; argv for a sentinel "--cc-debug" flag. When present, debug-log 8473 ;; prints heap usage between phases to fd 2. 8474 (define (%cc-flag? args flag) 8475 (cond ((null? args) #f) 8476 ((bv= (car args) flag) #t) 8477 (else (%cc-flag? (cdr args) flag)))) 8478 8479 (define (%cc-strip-flag args flag) 8480 (cond ((null? args) '()) 8481 ((bv= (car args) flag) (cdr args)) 8482 (else (cons (car args) (%cc-strip-flag (cdr args) flag))))) 8483 8484 ;; --lib=PFX selects library-mode codegen: cc.scm skips the p1_main 8485 ;; entry stub and trailing :ELF_end (the catm chain supplies them 8486 ;; from P1/entry-*.P1pp + P1/elf-end.P1pp once), and uses PFX as the 8487 ;; translation-unit namespace for internal-linkage symbols, strings, 8488 ;; compound literals, frame macros, and generated labels. Returns 8489 ;; (values prefix-bv rest-args). PREFIX = "" means exec mode (flag 8490 ;; absent). PREFIX = "" with the flag present is rejected — silently 8491 ;; falling back to exec mode would mask a typo'd Makefile rule. 8492 (define (%cc-take-lib args) 8493 (let loop ((acc '()) (rest args) (pfx #f)) 8494 (cond 8495 ((null? rest) 8496 (values (cond (pfx pfx) (else "")) (reverse acc))) 8497 ((bv-prefix? "--lib=" (car rest)) 8498 (cond (pfx (die #f "cc: --lib= specified twice"))) 8499 (let* ((arg (car rest)) 8500 (p (bv-slice arg 6 (bytevector-length arg)))) 8501 (cond ((= 0 (bytevector-length p)) 8502 (die #f "cc: --lib= requires a non-empty PREFIX"))) 8503 (loop acc (cdr rest) p))) 8504 (else 8505 (loop (cons (car rest) acc) (cdr rest) pfx))))) 8506 8507 ;; Predefined macros visible to every translation unit. CCSCM lets 8508 ;; tests/headers branch on "compiled by cc.scm" — e.g. skip <stdarg.h> 8509 ;; and use the __builtin_va_* primitives directly. 8510 (define %cc-initial-defines 8511 (list (cons "CCSCM" (%macro 'obj '() '())))) 8512 8513 (define (cc-main av) 8514 (let* ((raw (cdr (cdr av))) 8515 (dbg (%cc-flag? raw "--cc-debug")) 8516 (a1 (%cc-strip-flag raw "--cc-debug")) 8517 (tr (%cc-flag? a1 "--cc-trace-emit")) 8518 (a2 (%cc-strip-flag a1 "--cc-trace-emit"))) 8519 (cond (dbg (debug-log-on!))) 8520 (cond (tr (trace-emit-on!))) 8521 (let-values (((lib-prefix args) (%cc-take-lib a2))) 8522 (cond 8523 ((or (null? args) (null? (cdr args))) 8524 (die #f "usage: cc [--cc-debug] [--cc-trace-emit] [--lib=PFX] <input.c> <output.P1pp>"))) 8525 (let* ((in-path (car args)) 8526 (out-path (car (cdr args))) 8527 (lib? (cond ((= 0 (bytevector-length lib-prefix)) #f) 8528 (else #t)))) 8529 (debug-log "phase=start" "heap" (heap-usage)) 8530 ;; Streaming pipeline: lex → pp → parser → cg, all concurrent. 8531 ;; Each stage pulls one tok at a time from upstream. Steady-state 8532 ;; live data is bounded by parser/pp state, not source length. 8533 (let* ((src (%cc-slurp in-path)) 8534 (_1 (debug-log "phase=slurp" "heap" (heap-usage) 8535 "src-bytes" (bytevector-length src))) 8536 (lex-iter (make-lex-iter src in-path)) 8537 (pp-iter (make-pp-iter lex-iter %cc-initial-defines)) 8538 (cg (cg-init/v lib? lib-prefix)) 8539 (ps (make-pstate pp-iter cg))) 8540 (parse-translation-unit ps) 8541 (debug-log "phase=parse" "heap" (heap-usage)) 8542 (cg-finalize! cg) 8543 (debug-log "phase=cg-finish" "heap" (heap-usage) 8544 "out-bytes" (cg-output-size cg)) 8545 (%cc-write-cg out-path cg) 8546 0)))))