https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61781

--- Comment #2 from Geir Johansen <geir at cray dot com> ---
Longer version of the test case, along with notes from the original user.  T


I believe you have an overaggressive optimizer. Which I have turned into the
following somewhat  block of code (simple-instrumented.c):


========================================================================
/**

   Demo for a gcc 4.8.1 bug

   Built with:  gcc -O3 -std=c99 simple-instrumented.c
*/

#include <stdio.h>
#include <stdlib.h>

#ifdef myrand_unsigned
unsigned long myrand (unsigned long x)
#else
long myrand( long x )
#endif
{
#ifdef myrand_constants_ul
   unsigned long c = 6824061905351802757UL;  // prime
   unsigned long d = 5001297291839117257UL;  // prime, p - 1 divisible by 4
#else
   long c = 6824061905351802757L;  // prime
   long d = 5001297291839117257L;  // prime, p - 1 divisible by 4
#endif

#ifdef printf_sub_start
   printf(" x-ss: 0x%016ld\n", x);
#endif

#ifdef simpler
   x = d*x;
#else
   x = d*x + c;
#endif

#ifdef printf_sub_end
   printf(" x-se: 0x%016ld\n", x);
#endif

   return( x );
}


int main( int argc, char *argv[] ) {
#ifdef unsigned_loop
   unsigned long x;
#else
   long x;
#endif
   long len;
   long count = 0;

   x = 1;
   len = 1e2;

   if( argc > 1 ) {
     len = atof( argv[1] ) + 0.5;
   }
   if( argc > 2 ) {
     x = strtol( argv[2], 0, 0 );
   }

   printf("len = %ld, seed = 0x%016lx\n", len, x);

#ifdef unsigned_loop
   for(unsigned long i=0; i<len; i++ ) {
#else
   for( long i=0; i<len; i++ ) {
#endif

#ifdef myrand_x
     x = myrand( x );           // this version seems okay
#else
     x = myrand( i );           // this version breaks gcc
#endif
     printf("%4ld:  0x%016lx\n", i, x);
     count++;
   }
   printf("Count is %ld\n", count);

   return( 0 );
}

========================================================================

And I've been using the following block of code to exercise the
instrumented version of this code (do-it.bash):

========================================================================
#!/bin/bash
gcc -v
opt_gcc=("-fwrapv" "-fstrict-overflow" "-Wstrict-overflow=5" "-Wall")
opt_code=("simpler" "myrand_unsigned" "myrand_constants_ul"
"printf_sub_start" "printf_sub_end" "unsigned_loop" "myrand_x")

echo gcc options "-fwrapv" "-fstrict-overflow" "-Wstrict-overflow=5" "-Wall"
echo code options "simpler" "myrand_unsigned" "myrand_constants_ul"
"printf_sub_start" "printf_sub_end" "unsigned_loop" "myrand_x"

for ((j=0; j<16; j++))
   do
   optionsx=""
   if ((j&1)); then optionsx="$optionsx ${opt_gcc[0]}"; fi
   if ((j&2)); then optionsx="$optionsx ${opt_gcc[1]}"; fi
   if ((j&4)); then optionsx="$optionsx ${opt_gcc[2]}"; fi
   if ((j&8)); then optionsx="$optionsx ${opt_gcc[3]}"; fi
   for ((i=0; i<128; i++))
     do
     options=$optionsx
     if ((i& 1)); then options="$options -D ${opt_code[0]}"; fi
     if ((i& 2)); then options="$options -D ${opt_code[1]}"; fi
     if ((i& 4)); then options="$options -D ${opt_code[2]}"; fi
     if ((i& 8)); then options="$options -D ${opt_code[3]}"; fi
     if ((i&16)); then options="$options -D ${opt_code[4]}"; fi
     if ((i&32)); then options="$options -D ${opt_code[5]}"; fi
     if ((i&64)); then options="$options -D ${opt_code[6]}"; fi

     printf -v outfile "s-i-cc-%02d-%03d-d" $j $i
     time gcc  -O3 -std=c99 $options simple-instrumented.c -o ${outfile}

     echo 
     echo $outfile $options
     ./$outfile 3 | head -40
   done
done

========================================================================


I believe the 'bug' in the code is that the multiply in myrand uses a
signed integer, and when it overflows, it causes all sorts of fun things
to happen in the backend optimizer.


How odd that I can 'fix' that problem, occasionally, by adding a printf
between the entry to the subroutine and multiply.  (It does not seem to
matter when it's a 'multiply add'. Apparently, the more complex
operation keeps the optimizer confused, and one printf anywhere causes
it to fall out of that optimization pass.)

I similarly seem to be able to 'defeat' the bug by adding two printf
statements


s-i-cc-00-000
s-i-cc-00-016 -D printf_sub_end
s-i-cc-00-048 -D printf_sub_end -D unsigned_loop
s-i-cc-00-032                   -D unsigned_loop
s-i-cc-00-008 -D printf_sub_start
s-i-cc-00-024 -D printf_sub_start -D printf_sub_end

s-i-cc-00-001 -D simpler
s-i-cc-00-017 -D simpler -D printf_sub_end



(It doesn't seem to matter which options are set for flags --- if
-fwrapv is set, then the code runs correctly; if it is not, then the
rest of the options don't seem to do anything different in terms of
making it run or fail.)

... some observations:

     ... if an unsigned int is passed in, and the variable is printed
before it is used, the bug is averted.
     ... just passing in an unsigned int value does not 'fix' the
iteration counter.
     ... the code likes to run more cleanly if I ask to see the variable
with printf before it is used.

and I guess I'm not clear on what the type rules are if I pass in an
unsigned int to a signed int value --- shouldn't that force all of the
computations done with an unsigned int to be unsigned int computations?

And if the optimizer is going to take a hit when I overflow the
register, shouldn't -Wstrict-overflow=5 (or more) tell me about that?

Reply via email to