boot2

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

335-ternary-merge-arith-conv.c (2118B)


      1 /* tests/cc/335-ternary-merge-arith-conv.c — C11 §6.5.15 ¶5: ternary
      2  * applies the usual arithmetic conversions to its second and third
      3  * operands and the result type is the resulting common type.
      4  *
      5  * cc.scm originally took arm 1's type as the merge slot's result type
      6  * unchanged. When arm 1 was narrower than arm 2, the surrounding
      7  * expression saw a too-narrow rvalue and cg-arith-conv truncated to
      8  * that width on the next op. Concretely, in
      9  *
     10  *     (uint32_t)x | (cond ? 0 : -(x & 0x80000000))
     11  *
     12  * the ternary's int arm-1 dominated the merge type, so the `|` was
     13  * lowered as 32-bit and the upper sign-extension bits in arm-2 were
     14  * dropped. tcc.flat.c's gen_opic uses exactly this idiom for
     15  * sign-extension of narrow constants, so the cc.scm-built tcc-boot2
     16  * miscompiled int < int as unsigned and 220-const-promote.c failed
     17  * under tcc-cc.
     18  *
     19  * Keep this fixture as a runtime check on cc.scm directly so the
     20  * regression surfaces without dragging tcc into the loop. */
     21 typedef unsigned long long u64;
     22 typedef unsigned int u32;
     23 
     24 int main(void) {
     25     u64 l = (u64)-1;
     26     int t = 0;
     27 
     28     /* The exact pattern from tcc.flat.c gen_opic line 5471-5475. */
     29     u64 sext = ((u32)l |
     30                 (t & 0x10 ? 0 : -(l & 0x80000000)));
     31     if (sext != (u64)-1) return 1;
     32 
     33     /* Same shape with the ternary's arms in the other order — the
     34      * picked common type must not depend on which arm parses first. */
     35     u64 sext2 = ((u32)l |
     36                  (t & 0x10 ? -(l & 0x80000000) : 0));
     37     if (sext2 != 0xFFFFFFFFULL) return 2;
     38 
     39     /* Asymmetric arm types (int vs u64) at top level. With arm-1's
     40      * type leaking through, the result is read as a 32-bit value and
     41      * the high bits vanish. */
     42     u64 a = (1 ? 0 : (u64)0x100000001ULL);
     43     if (a != 0) return 3;
     44     u64 b = (0 ? 0 : (u64)0x100000001ULL);
     45     if (b != 0x100000001ULL) return 4;
     46 
     47     /* Mixed signed/unsigned ternary: result type should follow the
     48      * usual arithmetic conversions (i32 + u32 -> u32). */
     49     int s = -1;
     50     u32 u = 1u;
     51     u32 r = (1 ? s : u);
     52     if (r != 0xFFFFFFFFu) return 5;
     53 
     54     return 0;
     55 }