boot2

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

128-cast-signedness.c (2609B)


      1 /* Same-size and widening casts that flip signedness must canonicalize
      2  * the slot for the destination type — i8 (sign-extended in the slot)
      3  * cast to u8 / u16 / u32 / u64 needs the high bits cleared so a
      4  * subsequent comparison or wider arithmetic sees the C-semantic value.
      5  *
      6  * The classic case (which broke the tcc-boot2 string-roundtrip test):
      7  *   const char *s = "...\xCA...";
      8  *   if ((unsigned char)s[1] != 0xCA) ...   // this MUST take the false branch
      9  *
     10  * On aarch64, s[1] sign-extends to int -54. Slot = 0xFFFFFFFFFFFFFFCA.
     11  * The naive "same-size cast = relabel" path leaves the high bits set,
     12  * so a 64-bit compare against 0x00000000000000CA returns "not equal".
     13  */
     14 
     15 int main(int argc, char **argv) {
     16     const signed char *s = (const signed char *)"a\312\377"; /* bytes 'a', 0xCA, 0xFF, NUL */
     17 
     18     /* --- signed → unsigned, same size (i8 → u8) ------------------- */
     19     if ((unsigned char) s[1] != 202)         return 1;
     20     if ((unsigned char) s[1] != 0xCA)        return 2;
     21     /* Must NOT equal -54 once cast to u8. */
     22     if ((unsigned char) s[1] == -54)         return 3;
     23 
     24     /* --- signed → unsigned, widening (i8 → u16/u32/u64) ----------- */
     25     /* C: (unsigned int)(signed char)(-54) = 4294967242 = 0xFFFFFFCA. */
     26     if ((unsigned int) s[1] != 0xFFFFFFCAu)  return 4;
     27     /* Comparison at u32 width: -54 (slot=…FFCA) ≠ 4294967242 only
     28      * if the wider compare reads high bits correctly. */
     29 
     30     /* The result fills the active unsigned-long width: ...FFFFFFCA on
     31      * both LP64 and ILP32. */
     32     if ((unsigned long) s[1] != (~0UL - 53UL)) return 5;
     33 
     34     /* --- unsigned → signed, same size (u8 → i8) ------------------- */
     35     {
     36         unsigned char uc = 0xCA;
     37         signed char  sc = (signed char) uc;
     38         if (sc != -54)                       return 10;
     39         /* And the reverse round-trip: */
     40         if ((unsigned char) sc != 202)       return 11;
     41     }
     42 
     43     /* --- intermediate-cast doesn't lose the value ----------------- */
     44     {
     45         unsigned char tmp = (unsigned char) s[1];
     46         if (tmp != 202)                      return 20;
     47         if ((int) tmp != 202)                return 21;
     48     }
     49 
     50     /* --- i32 → u32 same-size: the high bits matter for 64-bit ops. */
     51     {
     52         int neg = -1;                        /* slot = 0xFFFFFFFFFFFFFFFF */
     53         unsigned int u = (unsigned int) neg; /* slot must zero high 32 */
     54         /* cast result canonical: 0x00000000FFFFFFFF = 4294967295 */
     55         if (u != 4294967295u)                return 30;
     56         if ((unsigned long) u != 4294967295ul) return 31;
     57     }
     58 
     59     return 0;
     60 }