------- Comment #13 from jakub at gcc dot gnu dot org 2007-10-15 11:20 ------- To me this looks like aliasing violation in 400.perlbench (and in perl 5.8.8 too, so I'll cite the latter).
#define IVTYPE long /**/ typedef IVTYPE IV; typedef struct xpviv XPVIV; typedef size_t STRLEN; struct xpviv { char * xpv_pv; /* pointer to malloced string */ STRLEN xpv_cur; /* length of xpv_pv as a C string */ STRLEN xpv_len; /* allocated size */ IV xiv_iv; /* integer value or pv offset */ }; # define STRUCT_OFFSET(s,m) (Size_t)(&(((s *)0)->m)) #define STATIC static # define pTHX_ # define LOCK_SV_MUTEX # define UNLOCK_SV_MUTEX extern IV * PL_xiv_root; In sv.c, we have: STATIC void S_del_xiv(pTHX_ XPVIV *p) { IV* xiv = (IV*)((char*)(p) + STRUCT_OFFSET(XPVIV, xiv_iv)); LOCK_SV_MUTEX; *(IV**)xiv = PL_xiv_root; PL_xiv_root = xiv; UNLOCK_SV_MUTEX; } The problem is when Perl_sv_upgrade function is called, with (sv->sv_flags & 0xff) == SVt_IV, mt == SVt_PVNV, ((XPVIV*) (sv)->sv_any)->xiv_iv == 0 and PL_xiv_root != NULL: IV iv; ... switch (SvTYPE(sv)) { case SVt_NULL: break; case SVt_IV: iv = SvIVX(sv); del_XIV(SvANY(sv)); if (mt == SVt_NV) mt = SVt_PVNV; else if (mt < SVt_PVIV) mt = SVt_PVIV; break; ... switch (mt) { case SVt_PVNV: SvANY(sv) = new_XPVNV(); SvPV_set(sv, pv); SvCUR_set(sv, cur); SvLEN_set(sv, len); SvIV_set(sv, iv); SvNV_set(sv, nv); break; ... or better preprocessed: switch (((sv)->sv_flags & 0xff)) { case SVt_NULL: break; case SVt_IV: iv = ((XPVIV*) (sv)->sv_any)->xiv_iv; S_del_xiv((XPVIV*) (sv)->sv_any); if (mt == SVt_NV) mt = SVt_PVNV; else if (mt < SVt_PVIV) mt = SVt_PVIV; break; ... switch (mt) { ... case SVt_PVNV: (sv)->sv_any = (void*)S_new_xpvnv(); ((XPV*) (sv)->sv_any)->xpv_pv = pv; ((XPV*) (sv)->sv_any)->xpv_cur = cur; ((XPV*) (sv)->sv_any)->xpv_len = len; ((XPVIV*) (sv)->sv_any)->xiv_iv = iv; ((XPVNV*)(sv)->sv_any)->xnv_nv = nv; break; when sv.c is compiled with -O2 -fstrict-aliasing, then the *(IV**)xiv = PL_xiv_root; write isn't considered aliased with the iv = ((XPVIV*) (sv)->sv_any)->xiv_iv;, because the former accesses the object as long *, while the latter as long. The difference between -O2 -fstrict-aliasing and -O2 -fno-strict-aliasing compiled sv.c is then that in this call ((XPVIV*) (sv)->sv_any)->xiv_iv = iv; stores (long) PL_xiv_root value before entering this function with -fstrict-aliasing and 0 (i.e. what ((XPVIV*) (sv)->sv_any)->xiv_iv contained on function entry) with -fno-strict-aliasing. It seems perl is aware of this, but doesn't care to fix it - Configure has instead: case "$gccversion" in 1*) ;; 2.[0-8]*) ;; ?*) echo " " echo "Checking if your compiler accepts -fno-strict-aliasing" 2 >&1 echo 'int main(void) { return 0; }' > gcctest.c if $cc -O2 -fno-strict-aliasing -o gcctest gcctest.c; then echo "Yes, it does." 2>&1 case "$ccflags" in *strict-aliasing*) echo "Leaving current flags $ccflags alone." 2> &1 ;; *) dflt="$dflt -fno-strict-aliasing" ;; esac else echo "Nope, it doesn't, but that's ok." 2>&1 fi ;; esac Not sure what is the best way forward with CPU2006 and GCC though (and what are other compilers doing to pass 400.perlbench). Is it acceptable to add -fno-strict-aliasing just for this test? -- http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33383