Bug 36296 - bogus uninitialized warning (loop representation, VRP missed-optimization)
Summary: bogus uninitialized warning (loop representation, VRP missed-optimization)
Status: RESOLVED WORKSFORME
Alias: None
Product: gcc
Classification: Unclassified
Component: middle-end (show other bugs)
Version: 4.3.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic
: 37361 (view as bug list)
Depends on:
Blocks: Wuninitialized 21733
  Show dependency treegraph
 
Reported: 2008-05-22 07:27 UTC by Paul Zimmermann
Modified: 2014-02-16 13:13 UTC (History)
6 users (show)

See Also:
Host: x86_64-unknown-linux-gnu
Target: x86_64-unknown-linux-gnu
Build: x86_64-unknown-linux-gnu
Known to work:
Known to fail:
Last reconfirmed: 2008-08-18 23:38:16


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Paul Zimmermann 2008-05-22 07:27:28 UTC
When compiling mpfr-2.3.1 with gcc-4.3, one gets the following warning
(among others):
{{{
lngamma.c: In function 'mpfr_lngamma_aux':
lngamma.c:160: warning: 'B' may be used uninitialized in this function
}}}
However, looking at the code shows that this variable cannot be used uninitialized:
* a variable Bm is initialized to zero
* if Bm=0, B is initialized using an auxiliary function, and Bm is set to 2
  (this code is in a do { ... } while loop, thus is always executed)
Comment 1 Andrew Pinski 2008-05-22 08:22:04 UTC
It is most likely the case, that we have to use predicated PHI nodes to detect that the variable is no unitialized.
Comment 2 Vincent Lefèvre 2008-05-22 08:34:03 UTC
The severity should probably be changed to enhancement because gcc behaves as documented (well, almost).

What can be done IMHO is:
1. Split the -Wuninitialized into two different warnings: one for which gcc knows that the variable is uninitialized and one for which it cannot decide. -Wuninitialized currently does both.
2. Provide an extension so that the user can tell gcc not to emit a warning for some particular variable. This would sometimes be better than adding a dummy initialization (which has its own drawbacks).

In the mean time, make the documentation better concerning -Wuninitialized: change the first sentence "Warn if an automatic variable is used without first being initialized [...]" to "Warn if an automatic variable *may be* used without first being initialized" (though the behavior is detailed later).
Comment 3 Richard Biener 2008-05-22 10:21:07 UTC
A way to tell gcc a variable is not uninitialized is to perform self-initialization like

 int i = i;

this will cause no code generation but inhibits the warning.  Other compilers
may warn about this construct of course.
Comment 4 Vincent Lefèvre 2008-05-22 11:01:22 UTC
(In reply to comment #3)
> A way to tell gcc a variable is not uninitialized is to perform
> self-initialization like
> 
>  int i = i;

This doesn't seem to be valid C code.

> this will cause no code generation but inhibits the warning.  Other compilers
> may warn about this construct of course.

Or worse, generate non-working code.
Comment 5 Vincent Lefèvre 2008-05-22 11:23:15 UTC
BTW, the i = i trick, which is guaranteed to be valid and no-op only *after* i has been initialized doesn't avoid the warning in such a case. I don't know if this would be a good feature (the main drawback I can see would be to miss warnings when this is a result of macro expansion). For instance:

#include <assert.h>
int foo (int x)
{
  int y;
  assert (x == 0 || x == 1);
  if (x == 0)
    y = 1;
  else if (x == 1)
    y = 2;
  y = y;  /* to tell the compiler that y has been initialized */
  return y;
}
Comment 6 Andrew Pinski 2008-05-28 07:31:03 UTC
(In reply to comment #5)
> BTW, the i = i trick

it only works in the initializer and not as a statement after the fact.

That is: 
#include <assert.h>
int foo (int x)
{
  int y = y;
  assert (x == 0 || x == 1);
  if (x == 0)
    y = 1;
  else if (x == 1)
    y = 2;
  return y;
}

Will work, also with the jump threading, GCC should be able to figure out that y is always inlined (except when -DNDEBUG is used).

-- Pinski
Comment 7 Vincent Lefèvre 2008-05-28 08:18:49 UTC
(In reply to comment #6)
> (In reply to comment #5)
> > BTW, the i = i trick
> 
> it only works in the initializer and not as a statement after the fact.

But in such a case, as i is not initialized yet, this may be undefined behavior with some C implementations.
Comment 8 Manuel López-Ibáñez 2008-08-18 17:24:29 UTC
Please provide a preprocessed reduced testcase as similar to the original as possible. 

I think this is not only predicated PHI but our representation of loops may also have something to do.
Comment 9 Vincent Lefèvre 2008-08-18 22:58:26 UTC
(In reply to comment #8)
> Please provide a preprocessed reduced testcase as similar to the original as
> possible. 

Here's a similar testcase.

$ cat tst.c
void *foo (void);
void bar (void *);

void f (void)
{
  int init = 0;
  void *p;

  while (1)
    {
      if (init == 0)
        {
          p = foo ();
          init = 2;
        }
      bar (p);
    }
}

$ gcc -Wall -O2 tst.c -c
tst.c: In function 'f':
tst.c:7: warning: 'p' may be used uninitialized in this function

This is quite strange: if I replace the value 2 by 1 or if I replace foo() by 0, the warning is no longer displayed.

Note: in the reality (in MPFR), the variable I called init here is the size of the array (0 when the array hasn't been allocated yet).
Comment 10 Manuel López-Ibáñez 2008-08-18 23:38:16 UTC
(In reply to comment #9)
> (In reply to comment #8)
> > Please provide a preprocessed reduced testcase as similar to the original as
> > possible. 
> 
> Here's a similar testcase.

Thanks.

This is the optimized SSA dump:

f ()
{
  voidD.39 * pD.1952;
  intD.0 initD.1951;

  # BLOCK 2 freq:1
  # PRED: ENTRY [100.0%]  (fallthru,exec)
  # SUCC: 3 [100.0%]  (fallthru,exec)

  # BLOCK 3 freq:10000
  # PRED: 2 [100.0%]  (fallthru,exec) 5 [100.0%]  (fallthru,dfs_back,exec)
  # initD.1951_1 = PHI <0(2), initD.1951_2(5)>
  # pD.1952_3 = PHI <pD.1952_6(D)(2), pD.1952_4(5)>
  [/home/manuel/src/pr36296.c : 11] if (initD.1951_1 == 0)
    goto <bb 4>;
  else
    goto <bb 5>;
  # SUCC: 4 [29.0%]  (true,exec) 5 [71.0%]  (false,exec)

  # BLOCK 4 freq:2900
  # PRED: 3 [29.0%]  (true,exec)
  [/home/manuel/src/pr36296.c : 13] # SMT.10D.1967_13 = VDEF <SMT.10D.1967_10> { SMT.10D.1967 }
  pD.1952_7 = fooD.1945 ();
  # SUCC: 5 [100.0%]  (fallthru,exec)

  # BLOCK 5 freq:10000
  # PRED: 3 [71.0%]  (false,exec) 4 [100.0%]  (fallthru,exec)
  # initD.1951_2 = PHI <initD.1951_1(3), 2(4)>
  # pD.1952_4 = PHI <pD.1952_3(3), pD.1952_7(4)>
  [/home/manuel/src/pr36296.c : 16] # SMT.10D.1967_14 = VDEF <SMT.10D.1967_11> { SMT.10D.1967 }
  barD.1947 (pD.1952_4);
  [/home/manuel/src/pr36296.c : 17] goto <bb 3>;
  # SUCC: 3 [100.0%]  (fallthru,dfs_back,exec)

}

Because we create a PHI node for p in BB 3, we think that p can be used uninitialized. Notice also that we are not able to move the 'if' and the call to foo() out of the infinite loop. This is perhaps a missed optimization.

 
> This is quite strange: if I replace the value 2 by 1 or if I replace foo() by
> 0, the warning is no longer displayed.

If you replace foo() by 0, then CCP just assumes the p is always 0. In fact, it will remove p altogether, even if you use 'if (init == 2)', thus missing a real uninitialized use. This behaviour is known to hide warnings, both correct and wrong warnings.

If I replace the value 2 by 1 I still get the warning in GCC 4.4, so that really sounds strange. Are you sure about that?

Anyway, this is a confirmed bug but not easy to fix.
Comment 11 Vincent Lefèvre 2008-08-19 01:31:53 UTC
(In reply to comment #10)
> If I replace the value 2 by 1 I still get the warning in GCC 4.4, so that
> really sounds strange. Are you sure about that?

Yes and here Debian's GCC 4.4 snapshot has the same behavior as GCC 4.3.1 (also from Debian). Also, the optimized trees are not the same for 1 and 2.

vin% cat tst.c
void *foo (void);
void bar (void *);

void f (void)
{
  int init = 0;
  void *p;

  while (1)
    {
      if (init == 0)
        {
          p = foo ();
          init = INIT;
        }
      bar (p);
    }
}
vin% gcc --version
gcc.real (Debian 4.3.1-9) 4.3.1
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

vin% gcc -Wall -O2 tst.c -c -fdump-tree-optimized -DINIT=1
vin% cat tst.c.126t.optimized

;; Function f (f)

Analyzing Edge Insertions.
f ()
{
  void * p;

<bb 2>:
  p = foo ();

<bb 3>:
  bar (p);
  goto <bb 3>;

}


vin% gcc -Wall -O2 tst.c -c -fdump-tree-optimized -DINIT=2
tst.c: In function 'f':
tst.c:7: warning: 'p' may be used uninitialized in this function
vin% cat tst.c.126t.optimized

;; Function f (f)

Analyzing Edge Insertions.
f ()
{
  void * p;
  int init;

<bb 2>:
  init = 0;

<bb 3>:
  if (init == 0)
    goto <bb 4>;
  else
    goto <bb 5>;

<bb 4>:
  p = foo ();
  init = 2;

<bb 5>:
  bar (p);
  goto <bb 3>;

}


vin% /usr/lib/gcc-snapshot/bin/gcc --version
gcc (Debian 20080802-1) 4.4.0 20080802 (experimental) [trunk revision 138551]
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

vin% /usr/lib/gcc-snapshot/bin/gcc -Wall -O2 tst.c -c -DINIT=1
vin% /usr/lib/gcc-snapshot/bin/gcc -Wall -O2 tst.c -c -DINIT=2
tst.c: In function 'f':
tst.c:7: warning: 'p' may be used uninitialized in this function
vin% 
Comment 12 Manuel López-Ibáñez 2008-08-19 02:33:38 UTC
The key difference is -ftree-vrp (which is enabled at -O2). With INIT=2, it is missing the obvious optimization that it detects with INIT=1. I wonder if this is expected (after all, it is value-RANGE-propagation) or there is a PR open about this.

Notice that any two consecutive integers trigger the optimization 0,1 but also 1,2 and thus remove the bogus uninitialized warning.
Comment 13 Andrew Pinski 2008-12-25 03:00:42 UTC
*** Bug 37361 has been marked as a duplicate of this bug. ***
Comment 14 av1474 2009-02-02 23:44:17 UTC
(In reply to comment #8)
> Please provide a preprocessed reduced testcase as similar to the original as
> possible. 
> 
> I think this is not only predicated PHI but our representation of loops may
> also have something to do.
> 

I'm not sure whether following warrants a new bug entry so doing it here, sorry,
if it was inappropriate.

Following code, triggers:
 "repro.c:5: warning: 'b' may be used uninitialized in this function"
with 4.3.0 and 4.3.1 on PowerPC when invoked like this:

gcc -c -O -Wuninitialized repro.c

int f (void);

int g (int a)
{
    int b;

    if (a) b = f ();
    asm volatile ("#");
    if (a) return b;
    return 1;
}

Comment 15 Manuel López-Ibáñez 2009-02-03 00:27:52 UTC
(In reply to comment #14)
> I'm not sure whether following warrants a new bug entry so doing it here,
> sorry,
> if it was inappropriate.

That is a different case. This PR is about loops. Your case is about  initialization and use guarded by the same predicate. I think there is already a bug report for this. But it is always better to open a new bug report in case of doubt.
Comment 16 Vincent Lefèvre 2013-04-17 08:40:09 UTC
(In reply to comment #3)
> A way to tell gcc a variable is not uninitialized is to perform
> self-initialization like
> 
>  int i = i;
> 
> this will cause no code generation but inhibits the warning.  Other compilers
> may warn about this construct of course.

What makes things worse about this workaround is that even protecting this by a

#if defined(__GNUC__)

may not be sufficient as other compilers may claim GNUC compatibility and behave differently. This is the case of clang (at least under Debian): http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=705583

The only good solution would be to fix the bug. I've checked that it is still there in the trunk revision 197260 (current Debian's gcc-snapshot).
Comment 17 Manuel López-Ibáñez 2013-04-17 09:19:09 UTC
(In reply to comment #16)
> (In reply to comment #3)
> > A way to tell gcc a variable is not uninitialized is to perform
> > self-initialization like
> > 
> >  int i = i;
> > 
> > this will cause no code generation but inhibits the warning.  Other compilers
> > may warn about this construct of course.

> The only good solution would be to fix the bug. I've checked that it is still
> there in the trunk revision 197260 (current Debian's gcc-snapshot).

If you mean to fix the false warning, then you are likely to wait a long long time (in order of years) because it doesn't seem a trivial thing to fix and there are very very few people with enough GCC knowledge to fix it (and they are busy with other things).

What would be trivial to fix (but require persistence, patience and time) is to implement this idea:

http://gcc.gnu.org/ml/gcc/2010-08/msg00297.html

that is, either  __attribute__ ((initialized))

or _Pragma("GCC diagnostic ignored \"-Wuninitialized\"").

(Personally, I prefer the latter, since it reuses existing code).

Add as a follow-up, get rid of the non-portable valgrind-unfriendly i=i idiom that has caused so much grief over the years.

However, we still need someone with the persistence, patience and time to implement this and get it past the powers that be.
Comment 18 Manuel López-Ibáñez 2013-04-17 09:31:59 UTC
In fact, we should have removed the i=i idiom a long time ago. The correct thing to do (as Linus says) is to initialize the variable to a sensible value to silence the warning: http://lwn.net/Articles/529954/

If GCC is smart enough to remove the initialization, then there is no harm. If GCC is not smart enough, then the code is probably complex enough that GCC cannot optimize it properly and this is why it gives a false positive, so the fake initialization is the least of your worries.
Comment 19 Manuel López-Ibáñez 2013-04-17 09:37:24 UTC
(In reply to comment #2)

> 1. Split the -Wuninitialized into two different warnings: one for which gcc
> knows that the variable is uninitialized and one for which it cannot decide.
> -Wuninitialized currently does both.

Note that -Wmaybe-uninitialized is available since at least GCC 4.8.0
Comment 20 Vincent Lefèvre 2013-04-17 11:17:14 UTC
(In reply to comment #18)
> In fact, we should have removed the i=i idiom a long time ago. The correct
> thing to do (as Linus says) is to initialize the variable to a sensible value
> to silence the warning: http://lwn.net/Articles/529954/

There is no real sensible value except some trap value. Letting the variable uninitialized at that point (the declaration) allows some tools, like the Formalin compiler described in WG14/N1637, to detect potential problems if the variable is really used uninitialized.

(In reply to comment #19)
> Note that -Wmaybe-uninitialized is available since at least GCC 4.8.0

OK, so a solution would be to add a configure test for projects that don't want such warnings (while still using -Wall) to see whether -Wno-maybe-uninitialized is supported.
Comment 21 Manuel López-Ibáñez 2013-04-17 11:26:01 UTC
(In reply to comment #20)
> OK, so a solution would be to add a configure test for projects that don't want
> such warnings (while still using -Wall) to see whether -Wno-maybe-uninitialized
> is supported.

When an unrecognized warning option is requested (e.g., -Wunknown-warning), GCC will emit a diagnostic stating that the option is not recognized.  However, if the -Wno- form is used, the behavior is slightly different: No diagnostic will be
produced for -Wno-unknown-warning unless other diagnostics are being produced.  This allows the use of new -Wno- options with old compilers, but if something goes wrong, the compiler will warn that an unrecognized option was used.
Comment 22 Manuel López-Ibáñez 2013-04-17 11:31:29 UTC
(In reply to comment #20)
> (In reply to comment #18)
> > In fact, we should have removed the i=i idiom a long time ago. The correct
> > thing to do (as Linus says) is to initialize the variable to a sensible value
> > to silence the warning: http://lwn.net/Articles/529954/
> 
> There is no real sensible value except some trap value. Letting the variable
> uninitialized at that point (the declaration) allows some tools, like the
> Formalin compiler described in WG14/N1637, to detect potential problems if the
> variable is really used uninitialized.

That doesn't contradict my assessment above that i=i idiom should die. With the Pragma one can choose to ignore GCC warnings if they don't want to initialize the value.

The trap value would be an additional improvement, but someone needs to implement it. Clang has fsanitize=undefined-trap:

http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation
Comment 23 Vincent Lefèvre 2013-04-17 12:24:56 UTC
(In reply to comment #21)
> When an unrecognized warning option is requested (e.g., -Wunknown-warning), GCC
> will emit a diagnostic stating that the option is not recognized.  However, if
> the -Wno- form is used, the behavior is slightly different: No diagnostic will
> be
> produced for -Wno-unknown-warning unless other diagnostics are being produced. 

That was mainly for pre-4.7 GCC versions, where without the i=i idiom, one would get the usual "may be used uninitialized in this function" warning because -Wno-maybe-uninitialized is not supported, but also the

  unrecognized command line option "-Wno-maybe-uninitialized"

warning because there was already a warning. However this may not really be important.
Comment 24 Vincent Lefèvre 2013-04-17 12:34:40 UTC
BTW, since with the latest GCC versions (such as Debian's GCC 4.7.2), the warning is no longer issued with -Wno-maybe-uninitialized, perhaps the bug severity could be lowered to "enhancement".
Comment 25 Jeffrey A. Law 2013-11-20 00:55:01 UTC
All tests included in this BZ were tested and work fine on the trunk.
Comment 26 Jackie Rosen 2014-02-16 13:13:08 UTC Comment hidden (spam)