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

Bob Wirka bobwirka at yahoo.com
Mon Sep 16 16:47:47 CEST 2013


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

-- 
Uwe Bonnes                bon at elektron.ikp.physik.tu-darmstadt.de

Institut fuer Kernphysik  Schlossgartenstrasse 9  64289 Darmstadt
--------- Tel. 06151 162516 -------- Fax. 06151 164321 ----------


More information about the En-Nut-Discussion mailing list