Forwarding this message to the linaro toolchain list instead. I am not the person who should be supporting the Linaro ODP project with GCC questiosn; the toolchain team inside Linaro should be instead.
Thanks, Andrew Pinski
________________________________________ From: Ola Liljedahl ola.liljedahl@linaro.org Sent: Monday, November 24, 2014 2:31 PM To: lng-odp@lists.linaro.org; Pinski, Andrew Subject: strange behavior in GCC for use of uninitialized variables
Consider the following code fragment (from real life):
#include <stdint.h>
typedef volatile uint32_t odp_atomic_u32_t;
static inline uint32_t odp_atomic_fetch_inc_u32(odp_atomic_u32_t *ptr) { return __sync_fetch_and_add(ptr, 1); }
static inline void odp_spin(void) { #ifdef __SSE2__ __asm__ __volatile__ ("pause"); #else __asm__ __volatile__ ("rep; nop"); #endif }
typedef struct { int count; odp_atomic_u32_t bar; } odp_barrier_t;
void odp_barrier_wait(odp_barrier_t *barrier) { uint32_t count; int wasless;
// wasless = barrier->bar < barrier->count; <<<lost on git add -p __atomic_thread_fence(__ATOMIC_SEQ_CST); count = odp_atomic_fetch_inc_u32(&barrier->bar);
if (count == 2*barrier->count-1) { barrier->bar = 0; } else { while ((barrier->bar < barrier->count) == wasless) odp_spin(); }
__atomic_thread_fence(__ATOMIC_SEQ_CST); }
While fixing and cleaning up this code, the indicated line that initializes 'wasless' was dropped (because it reappears in a later patch in the patch set after the odp_atomic_fetch_inc call). To my surprise, GCC did not complain when compiling this file (using -O2 -Wall). But it does complain when compiling with -O0 -Wall. With some investigation, it seems like GCC understands that if a statement does not have any side effects so it can optimize away everything, including the usage of the uninitialized variable and thus also the corresponding warning.
olli@macmini:~/hacking/gcc-wunit$ gcc -O2 -Wall -c odp_barrier.c olli@macmini:~/hacking/gcc-wunit$ gcc -O0 -Wall -c odp_barrier.c odp_barrier.c: In function ‘odp_barrier_wait’: odp_barrier.c:42:9: warning: ‘wasless’ may be used uninitialized in this function [-Wmaybe-uninitialized] while ((barrier->bar < barrier->count) == wasless) ^
However the proper code seems to be generated in both cases (there is a "pause" instruction inlined or a call to odp_spin). So odp_spin() is not without side effects and is not optimized away. This contradicts my hypothesis.
Consider this minimalistic example: olli@macmini:~/hacking/gcc-wunit$ cat wunit.c #include <stdlib.h>
void test(void) { int wasless; int wasmore;
if (wasless) (void)0; if (wasmore) abort(); }
olli@macmini:~/hacking/gcc-wunit$ gcc -O0 -Wall -c wunit.c wunit.c: In function ‘test’: wunit.c:9:5: warning: ‘wasmore’ is used uninitialized in this function [-Wuninitialized] if (wasmore) abort(); ^ olli@macmini:~/hacking/gcc-wunit$ gcc -O2 -Wall -c wunit.c wunit.c: In function ‘test’: wunit.c:9:5: warning: ‘wasmore’ is used uninitialized in this function [-Wuninitialized] if (wasmore) abort(); ^
Here GCC warns when used with both -O0 and -O2 but only for the usage where there is a side effect. The use of 'wasless' that does not lead to any side-effects is ignored (and possibly rightly so, I can imagine this is undefined behavior, fortunately I did not attempt to run this program or my computer could have melted).
It is a bit worrying to me that instances of use of initialized variables is sometimes missed by GCC. Both because of lack of diagnostics for what is most likely a bug but also because I don't understand why GCC does this and the implications of that (at least it is a known unknown now).
-- Ola