Bug 41072

Summary: Alias stacked type cast interpretation regression
Product: gcc Reporter: Sergei Larin <sergei_lus>
Component: middle-endAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED DUPLICATE    
Severity: normal CC: alexcher, andrew, attardi, baisjing, cdfrey, chiabaut, cuerob, daniel, Dries.Decock, dvt, gael.guennebaud, gcc-bugs, gcc2eran, gray, hans.buchmann.wantuch, hazeman, honza, jacob, jsworley, lucifer_ww, mueller, myan, ricardo, rosenfeld, sb, schendel, sergei_lus, songyulu, sorenj, stillzhang, takahisa.yokota, tompa-l, vkolluri
Priority: P3    
Version: 4.4.0   
Target Milestone: ---   
Host: x86_64-unknown-linux-gnu Target: x86_64-unknown-linux-gnu
Build: Known to work:
Known to fail: Last reconfirmed:

Description Sergei Larin 2009-08-14 18:38:36 UTC
The following code:

#include <stdio.h> 

float floatsisf(int x) {
  float ans = 0.5F;
  long lexp = 24;
  unsigned long frac = x;
  unsigned long norm = 0x800000;
  unsigned long xint;
  
   for (; frac < norm; frac <<= 1)
    --lexp;
  
 
  xint = (unsigned long)frac;
  (((unsigned short *)(char *)&(ans)))[1] = (((unsigned short *)(char *)&(ans)))[1] & 0xff80 | ((xint) >> 16 ) & 0x007f; 
  (((unsigned short *)(char *)&(ans)))[0] = (xint) & 0xffff; 
  
  fprintf(stderr,"scale got (%f)\n",ans);
  
  return ans;
 }

 main(){
 int i; 
 for(i=1;i<10;i++) floatsisf(i); 
 }

uses an obscure way to communicate with alias analyzer. The way I understand the original intent, once you cast &ans to (char *), alias analyzer should treat it as "char" == "alias everything" case. With GCC 3.3.3 and 3.4.6 that was the case. Starting with GCC 4.4.0 it does not - the leftmost (unsigned short *) cast seems to permit speculation about the two references, resulting in a wrong code generated. 

More details:
>/xxx/x86_gcc_4.4/bin/bin/gcc -v
Using built-in specs.
Target: x86_64-unknown-linux-gnu
Configured with: ../configure --enable-threads=posix --prefix=/prj/dsp/qdsp6_aus/users/slarin/x86_gcc_4.4/bin --enable-languages=c,c++ --disable-checking
Thread model: posix
gcc version 4.4.0 (GCC)
>/xxx/x86_gcc_4.4/bin/bin/gcc -O1  test.c
>./a.out
scale got (0.500000)
scale got (0.500000)
scale got (0.750000)
scale got (0.500000)
scale got (0.625000)
scale got (0.750000)
scale got (0.875000)
scale got (0.500000)
scale got (0.562500)
>/xxx/x86_gcc_4.4/bin/bin/gcc -O1 -fstrict-aliasing test.c
slarin@l1:~/test_gcc/qcc/bad> ./a.out
scale got (0.500000)
scale got (0.500000)
scale got (0.500000)
scale got (0.500000)
scale got (0.500000)
scale got (0.500000)
scale got (0.500000)
scale got (0.500000)
scale got (0.500000)

Same exercise with:
> gcc -v
Reading specs from /usr/lib64/gcc-lib/x86_64-suse-linux/3.3.3/specs
Configured with: ../configure --enable-threads=posix --prefix=/usr --with-local-prefix=/usr/local --infodir=/usr/share/info --mandir=/usr/share/man --enable-languages=c,c++,f77,objc,java,ada --disable-checking --libdir=/usr/lib64 --enable-libgcj --with-gxx-include-dir=/usr/include/g++ --with-slibdir=/lib64 --with-system-zlib --enable-shared --enable-__cxa_atexit x86_64-suse-linux
Thread model: posix
gcc version 3.3.3 (SuSE Linux)

... yields correct results either way.
Comment 1 Andrew Pinski 2009-08-14 19:10:30 UTC
You are wrong to assume that the cast to char* changes the aliasing violation.  In fact it is an access via char which is able to be done to any other type.
So this:
(((unsigned short *)(char *)&(ans)))[1]
is an access of an float variable as an unsigned short and there is an aliasing violation there.

*** This bug has been marked as a duplicate of 21920 ***
Comment 2 Sergei Larin 2009-08-17 16:35:39 UTC
First of all, I agree that a union would work better for type-punning, but... 

With all the respect I am not convinced that this is the case here. Even if I do something like this: 

tmp_ch = (char *)&ans; 
tmp_sh = (unsigned short *)tmp_ch; 

I would expect tmp_ch to be "aliased to everything", and it should in turn also hold for tmp_sh... unless you can say which standard (C99) rule says otherwise. 

... but this argument aside, maybe you can explain why up to 4.3.2 GCC was "ok" with this code, and 4.4.0 explicitly changed its behavior - I could not readily track it down (could be my inability to trace it back). In a similar fashion I would appreciate a justification (C99 reference). 

Also, in 4.4.0 forwprop1 turns this: 
tmp_ch_10 = (char *) &ans;
tmp_sh_11 = (short unsigned int *) tmp_ch_10;
into this: 
tmp_sh_11 = (short unsigned int *) &ans;

...so alias analyzer does not even "see" the (char *) cast... 4.3.2 does not do this.


The actual issue is that this breaks a long standing legacy code (a math library) and I see no provision to disable this new "feature" short of turning off strict aliasing all together. 

Thank you for your response - and information is very much appreciated.
Comment 3 Richard Biener 2009-08-18 09:18:32 UTC
6.5/6 and 6.5/7 specify your code invokes undefined behavior.  Use -fno-strict-aliasing for programs you don't want to fix.

*** This bug has been marked as a duplicate of 21920 ***