[En-Nut-Discussion] Probable GCC Compiler error, was: Re: Problem with ARM floating point, again

Paweł apcom at tlen.pl
Tue Sep 17 08:37:53 CEST 2013

    As I wrote some time ago - up to date I'm using Yagarto 4.3.2 (with
Ethernut 4.10.2). With this compiler printf works without any problem.
Maybe this helps to better investigate problem.
I tested almost all later version of Yagarto on different Ethernut versions
and everytime was some problems with floating point.

best regards

> Will try replying again; looks like I've got issues with my email...
> Yes, this is a compiler issue. Again, I'm using the Codesourcery compiler for an AT91SAM7X512 project.
> The "nut" of it is that the va_arg() macro fails for 8-byte values if the va_list pointer ends in '0'.
> Example va_arg() math from line 449 of putf.c:
>     449                    _double = va_arg(ap, double);
> -    0xa104    <_putf+2412>:        ldr    r3, [r11, #-124]    ; 0x7c
> -    0xa108    <_putf+2416>:        add    r3, r3, #7
> -    0xa10c    <_putf+2420>:        bic    r3, r3, #7
> -    0xa110    <_putf+2424>:        add    r2, r3, #8
> -    0xa114    <_putf+2428>:        str    r2, [r11, #-124]    ; 0x7c
> -    0xa118    <_putf+2432>:        ldm    r3, {r3, r4}
> -    0xa11c    <_putf+2436>:        str    r3, [r11, #-76]    ; 0x4c
> -    0xa120    <_putf+2440>:        str    r4, [r11, #-72]    ; 0x48
> In this case, r11 holds a pointer to the va_list (ap). If that value ends 
> in '0', the "add 7, and ~7" operation leaves the pointer ending with 0 
> instead of the intended "pointing to the next 8 byte boundary" 
> operation. So, for this example, the double value was taken from r1-r2 
> instead of r2-r3 (on the stack).
> The code was "printf("%f" , fval)". The compiler loads r0 with a pointer to the format, then promotes the floating point value to a double and 
> loads it into r2-r3 before calling printf(). R1 IS NOT USED, as 8 byte 
> values MUST be passed in r2-r3 per the ARM standard.
> Printf() then pushes r0-r3. It passes r0 and a pointer to r1 on the stack to 
> vfprintf(). That pointer to r1 on the stack is the va_list, and if it 
> happens that the pointer ends in '0', the va_arg() call mucks up the 
> arithmetic.
> This error can surface in a long printf with lots of parameters if the 
> va_list pointer ends up ending in '0' and va_arg() wants a double or 
> long long.
> So, one way to fix this is to modify _putf() so it doesn't use va_arg(). Ugly, I know.
> This can be done, however, and we don't need to worry about freeing memory 
> or any unintended consequences (I think), as the va_end() macro in 
> printf does nothing. So the va_list pointer passed to _putb() can be 
> manipulated to provide correct values. I'll see if it works.
> I wonder if anyone at GCC is listening?
> Best,
> Bob Wirka
> Realtime Control Works
> Janesville, WI
> ________________________________
>  From: "bon at elektron.ikp.physik.tu-darmstadt.de" <bon at elektron.ikp.physik.tu-darmstadt.de>
> To: Bob Wirka <bobwirka at yahoo.com>; Ethernut User Chat (English) <en-nut-discussion at egnite.de> 
> Sent: Sunday, September 15, 2013 4:02 PM
> Subject: Probable GCC Compiler error, was: Re: [En-Nut-Discussion] Problem with ARM floating point, again
>>>>>> "Bob" == Bob Wirka <bobwirka at yahoo.com> writes:
>     Bob> Hello, I'm having issues with printf("%f") on my AT91SAM7X512
>     Bob> platform (using ethernut-4.10.3). We're using the Codesourcery
>     Bob> compiler (gcc version 4.5.1 (Sourcery G++ Lite 2010.09-51)).
> Sending a working, stripped down example may lower the barrier for others
> to enter that problem...
> Anyway, I tried on STM32F4 with Launchpad gcc and I can see the problem
> too. Debugging and stepping a lot through the code, I am quite sure that it
> is a compiler problem. I can get things to print right with substituting
>                 _double = va_arg(ap, double);
> by
>                 uint32_t *l = (uint32_t *)&_double;
>                 if (*(uint32_t*)&ap & 4) {
>                     l[0]= va_arg(ap, uint32_t);
>                     l[1]= va_arg(ap, uint32_t);
>                 }
>                 else {
>                     l[0]= va_arg(ap, uint32_t);
>                     l[0]= va_arg(ap, uint32_t);
>                     l[1]= va_arg(ap, uint32_t);
>                 }
> When pushing the arguments on the stack, the arm calling convention tells to
> put 64-bit units in R0/1 or R2/3 or stack aligned. I see this happening. It
> seems as va_start doesn't care for that alignment. The 64-bit data has also
> either its 32-bit words swapped in the va_list, or va_arg(ap, double) uses the
> wrong order.
> Can anybody confirm that this may not me caused by some strange compiler
> option we give?
> Bye

More information about the En-Nut-Discussion mailing list