Bug 44547 - -Wuninitialized reports false warning in nested switch statements (missed switch optimization)
Summary: -Wuninitialized reports false warning in nested switch statements (missed swi...
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: tree-optimization (show other bugs)
Version: 4.5.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic
Depends on:
Blocks: Wuninitialized
  Show dependency treegraph
 
Reported: 2010-06-15 18:48 UTC by Anthony P.
Modified: 2010-06-17 08:37 UTC (History)
3 users (show)

See Also:
Host: x86_64-linux-gnu
Target: x86_64-linux-gnu
Build: x86_64-linux-gnu
Known to work:
Known to fail:
Last reconfirmed: 2010-06-17 07:18:58


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Anthony P. 2010-06-15 18:48:14 UTC
GCC with -Wuninitialized enabled and -On where n>= 1 will report falsely that a variable is uninitialized despite having sufficient information to determine that the variable must necessarily be initialized.

The erroneous warning is:
In function ‘main’:
warning: ‘n’ may be used uninitialized in this function

The output from running "gcc -v -save-temps -Wuninitialized -O1 source-file" is: 

Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.3.2-1.1' --with-bugurl=file:///usr/share/doc/gcc-4.3/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/4.3 --program-suffix=-4.3 --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-mpfr --enable-cld --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.3.2 (Debian 4.3.2-1.1) 
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Wuninitialized' '-O1' '-mtune=generic'
 /usr/lib/gcc/x86_64-linux-gnu/4.3.2/cc1 -E -quiet -v test.c -mtune=generic -Wuninitialized -O1 -fpch-preprocess -o test.i
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/4.3.2/../../../../x86_64-linux-gnu/include"
ignoring nonexistent directory "/usr/include/x86_64-linux-gnu"
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/4.3.2/include
 /usr/lib/gcc/x86_64-linux-gnu/4.3.2/include-fixed
 /usr/include
End of search list.
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Wuninitialized' '-O1' '-mtune=generic'
 /usr/lib/gcc/x86_64-linux-gnu/4.3.2/cc1 -fpreprocessed test.i -quiet -dumpbase test.c -mtune=generic -auxbase test -O1 -Wuninitialized -version -o test.s
GNU C (Debian 4.3.2-1.1) version 4.3.2 (x86_64-linux-gnu)
	compiled by GNU C version 4.3.2, GMP version 4.2.2, MPFR version 2.3.2.
warning: MPFR header version 2.3.2 differs from library version 2.3.1.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 3741a6052d20b6389f93b1cc4a619780
test.c: In function &#8216;main&#8217;:
test.c:14: warning: &#8216;n&#8217; may be used uninitialized in this function
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Wuninitialized' '-O1' '-mtune=generic'
 as -V -Qy -o test.o test.s
GNU assembler version 2.18.0 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.18.0.20080103
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.3.2/:/usr/lib/gcc/x86_64-linux-gnu/4.3.2/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.3.2/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.3.2/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.3.2/:/usr/lib/gcc/x86_64-linux-gnu/4.3.2/:/usr/lib/gcc/x86_64-linux-gnu/4.3.2/../../../../lib/:/lib/../lib/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.3.2/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Wuninitialized' '-O1' '-mtune=generic'
 /usr/lib/gcc/x86_64-linux-gnu/4.3.2/collect2 --eh-frame-hdr -m elf_x86_64 --hash-style=both -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/gcc/x86_64-linux-gnu/4.3.2/../../../../lib/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.3.2/../../../../lib/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.3.2/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.3.2 -L/usr/lib/gcc/x86_64-linux-gnu/4.3.2 -L/usr/lib/gcc/x86_64-linux-gnu/4.3.2/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.3.2/../../.. test.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/4.3.2/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.3.2/../../../../lib/crtn.o


*note: the output above lists version 4.3.2; however I have tested with the same effect on gcc version 4.5.0 and have no reason to believe it doesn't exist in trunk.

The minimal source code example:
// ---------------------------
/* GCC bug: -Wuninitialized produces false warning
 * compile with gcc -Wuninitialized -On where n >= 1
 */

int main( int argc, char *argv[] )
{
    switch( argc )
    {
        // note: bug no longer occurs if case values are consective (i.e. 1,2,3)
        case 1:
        case 2:
        case 4:
        {
            int n;
            switch( argc )
            {
                case 1:
                case 2:
                case 4:
                    n = argc;
                    break;
            }

            return n;

            break;
        }
    }

    return 0;
}

// ---------------------------

From the example above, the first switch limits the value of argc to cases 1, 2, or 4, so that in the second switch statement the compiler should be able to determine that its value must necessarily be 1,2, or 4 and thus n must necessarily become initialized. This is the case when compiling with no optimizations or when using consecutive values for cases (i.e. 1,2,3 instead of 1,2,4); however if neither is the case, the compiler issues an erroneous warning that n is uninitialized.
Comment 1 Manuel López-Ibáñez 2010-06-16 11:54:19 UTC
Value range-propagation (VRP) does not work on disjoint ranges, so the compiler does not actually know that argc can only be 1, 2 or 4. I think there is already a PR about this but I cannot find it right now.
Comment 2 Anthony P. 2010-06-17 01:14:37 UTC
(In reply to comment #1)
> Value range-propagation (VRP) does not work on disjoint ranges, so the compiler
> does not actually know that argc can only be 1, 2 or 4. I think there is
> already a PR about this but I cannot find it right now.
> 

I'm not sure VRP is the issue here (the values being range-like was merely a coincidence). Consider the following two equivalent examples:

int main( int argc, char *argv[] )
{
 if( argc == 111 || argc == 999 )
 {
  int n;
  if( argc == 111 || argc == 999 )
   n = argc;
  return n;
 }
}

int main( int argc, char *argv[] )
{
 switch( argc )
 {
  case 111:
  case 999:
  {
   int n;
   switch( argc )
   {
    case 111:
    case 999:
     n = argc;
   }
   return n;
  }
 }
}

In both examples - as with the example in the original report -  the compiler is given the exact same information, namely that after the first switch/if statement, the values on argc are constrained to either 111 or 999 (and thus the following switch/if must necessarily cover all values of argc and n is always used initialized). The only difference is that, in the example using switch statements, the compiler seems to "forget" these constraints on argc.
Comment 3 Manuel López-Ibáñez 2010-06-17 07:18:57 UTC
You are right. The issue occurs in VRP but not because of the disjoint ranges. Pass vrp1 is able to optimize your first example (nested if) but not the second (nested switch). I think this is a missed optimization. Not easy to fix, though. 
Comment 4 Anthony P. 2010-06-17 08:16:34 UTC
It seems that optimizing is what's causing the problem: the example compiles fine with -O0, but not -On>=1. It also compiles fine when the case values are consecutive, which seems telling. My first guess would be to take a look at what optimizations are being done to switch statements that have non-consecutive-valued cases as opposed to those that have consecutive values.
Comment 5 Manuel López-Ibáñez 2010-06-17 08:37:22 UTC
(In reply to comment #4)
> It seems that optimizing is what's causing the problem: the example compiles
> fine with -O0, but not -On>=1. It also compiles fine when the case values are
> consecutive, which seems telling. My first guess would be to take a look at
> what optimizations are being done to switch statements that have
> non-consecutive-valued cases as opposed to those that have consecutive values.
> 

Wuninitialized does not detect as many cases with -O0 than with higher optimizations. When the cases are consecutive, VRP kicks in and simplifies the switch. You can check what is going on using -fdump-tree-all-lineno and inspecting the dumps. n(D) means the uninitialized value of n. When that is removed, then Wuninitialized will not warn.