Menu

#3172 explicitly cast varargs promoted even in --std-sdccXX mode

open
nobody
None
Front-end
5
2025-04-25
2021-01-27
Tony Pavlov
No

code:

#include <stdio.h>

void main() {
    unsigned char i = 0x5A;
    unsigned char *p1 = &i, *p2 = &i;

    printf("%hx %hx\n", *p1, *p2);
    printf("%hx %hx\n", (unsigned char)(*p1), (unsigned char)(*p2));

    printf("%hx %hx\n", i, i);
    printf("%hx %hx\n", (unsigned char)i, (unsigned char)i);

    printf("%hx %hx\n", (unsigned char)0x5a, (unsigned char)0x5a);
}

expected output should be:

0x5a 0x00
0x5a 0x5a
0x5a 0x00
0x5a 0x5a
0x5a 0x5a

but we get:

0x5a 0x00
0x5a 0x00
0x5a 0x00
0x5a 0x5a
0x5a 0x5a

which is somehow inconsistent.

it is not a GBDK-2020 printf() implementation bug:

;test1.c:8: printf("%hx %hx\n", (unsigned char)(*p1), (unsigned char)(*p2));
    ld  a, (de)
    ld  e, a
    ld  d, #0x00
    ld  a, (bc)
    ld  c, a
    ld  b, #0x00
    push    de
    push    bc
    ld  hl, #___str_0
    push    hl
    call    _printf
    add sp, #6

sdcc -v:

SDCC : mcs51/z80/z180/r2k/r2ka/r3ka/gbz80/tlcs90/ez80_z80/z80n/ds390/pic16/pic14/TININative/ds400/hc08/s08/stm8/pdk13/pdk14/pdk15 4.0.7 #12016 (MINGW64)

possibly, that affects other targets.

Discussion

  • Philipp Klaus Krause

    Please provide the command line used to compile. Also check section 3.5.10 of the manual.

     
    • Tony Pavlov

      Tony Pavlov - 2021-01-27
      sdcc -mgbz80 --fsigned-char --no-std-crt0 -I ..\..\gbdk\include -I ..\..\gbdk\include\asm -I src\include -c test1.c -o build\test1.rel 
      sdldgb -n -m -w -j -i -k ..\..\gbdk\lib\small\asxxxx\gbz80\ -l gbz80.lib -k ..\..\gbdk\lib\small\asxxxx\gb\ -l gb.lib -g _shadow_OAM=0xC000 -g .STACK=0xE000 -g .refresh_OAM=0xFF80 -b _DATA=0xc0a0 -b _CODE=0x0200 test1.ihx ..\..\gbdk\lib\small\asxxxx\gb\crt0.o build\test1.rel  
      makebin -Z -yt 2 -yo 4 -ya 4 test1.ihx test1.gb 
      

      Philipp, i don't provide any arguments, mentioned in 3.5.10, but, as you can see, we have a "blend" of those arguments, and that is the problem. It does not follow --std-sdccxx neither --std-cxx in all cases.

      with -std-cxx, i guess, output must be:

      0x5a 0x00
      0x5a 0x00
      0x5a 0x00
      0x5a 0x00
      0x5a 0x00
      

      (because gbdk-2020 printf implementation expects byte on stack for %hx)

       

      Last edit: Tony Pavlov 2021-01-27
    • Tony Pavlov

      Tony Pavlov - 2021-01-27

      should not

      printf("%hx %hx\n", (unsigned char)(*p1), (unsigned char)(*p2));
      

      with --std-sdcc2x (isn't that a default?) push two bytes onto stack instead of two words?

      ps: manual is unclear, though:

      Arguments to vararg functions are not promoted when explicitly cast. This feature is only enabled when the compiler
      is invoked using --std-sdccxx. This breaks compability with the C standards, so linking code compiled
      with --std-sdccxx with code compiled using --std-cxx can result in failing programs when arguments to
      vararg functions are explicitly cast.
      

      it does not say anything about default behaviour, and from that quote one might think that --std-cxx is a default.

       
      • Philipp Klaus Krause

        Indeed your example should push two bytes for the non-Padauk ports. And in the test I just added, we see that doesn't work.

         
        • bbbbbr

          bbbbbr - 2025-04-25

          I stumbled across this issue recently, though in a slightly different form. It was with passing members of a struct that was a pointer.

          The things below make me wonder if this issue is specific to passing dereferenced pointer values with varargs.

          Context:

          typedef struct rtc_data_t {
              uint16_t  day;
              uint8_t   hour;
              uint8_t   min;
              uint8_t   sec;
          } rtc_data_t;
          
          rtc_data_t rtc;
          

          This produces output as if it is ignoring the casting to (uint8_t) and so every other %hd is zero instead of expected value

          printf("Day: %u  \n"
                  "Time:  %hd:%hd:%hd   \n",
                   (uint16_t)(p_rtc->day),
                   (uint8_t)(p_rtc->hour),
                   (uint8_t)(p_rtc->min),
                   (uint8_t)(p_rtc->sec));
          

          Interestingly the output works as expected if the data is copied into stack local vars and those are passed instead.

          // Copy rtc data from struct pointer
          rtc_data_t rtc;
          rtc = *p_rtc;
          
          printf("Day: %u  \n"
                  "Time:  %hd:%hd:%hd   \n",
                   (uint16_t)rtc.day,
                   (uint8_t)rtc.hour,
                   (uint8_t)rtc.min,
                   (uint8_t)rtc.sec);
          

          Similarly, changing toxa's example to use stack local or global vars instead of pointers results in the correct output.

           
  • Philipp Klaus Krause

    • Category: other --> Front-end
     
  • Philipp Klaus Krause

    Thanks. I can reproduce the issue. Looks like a front-end bug as apparently all non-Padauk ports are affected.
    I have created a regression test, which as of [r12023] is in the test suite, but disabled.

     
  • Philipp Klaus Krause

    The missing casts are already missing in the AST (see -dump-ast output), so this problem apparently happens during a very early stage in the front-end.

     
  • Philipp Klaus Krause

    • summary: GBZ80 wrong types when passing varargs --> explicitly cast varargs promoted even in --std-sdccXX mode
     

Log in to post a comment.

MongoDB Logo MongoDB