boot2

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

128-cast-signedness.c (2580B)


      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     /* (unsigned long)(signed char)(-54) = 0xFFFFFFFFFFFFFFCA. */
     31     if ((unsigned long) s[1] != 0xFFFFFFFFFFFFFFCAul) return 5;
     32 
     33     /* --- unsigned → signed, same size (u8 → i8) ------------------- */
     34     {
     35         unsigned char uc = 0xCA;
     36         signed char  sc = (signed char) uc;
     37         if (sc != -54)                       return 10;
     38         /* And the reverse round-trip: */
     39         if ((unsigned char) sc != 202)       return 11;
     40     }
     41 
     42     /* --- intermediate-cast doesn't lose the value ----------------- */
     43     {
     44         unsigned char tmp = (unsigned char) s[1];
     45         if (tmp != 202)                      return 20;
     46         if ((int) tmp != 202)                return 21;
     47     }
     48 
     49     /* --- i32 → u32 same-size: the high bits matter for 64-bit ops. */
     50     {
     51         int neg = -1;                        /* slot = 0xFFFFFFFFFFFFFFFF */
     52         unsigned int u = (unsigned int) neg; /* slot must zero high 32 */
     53         /* cast result canonical: 0x00000000FFFFFFFF = 4294967295 */
     54         if (u != 4294967295u)                return 30;
     55         if ((unsigned long) u != 4294967295ul) return 31;
     56     }
     57 
     58     return 0;
     59 }