https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66110
Richard Biener <rguenth at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- Keywords| |alias, missed-optimization Status|RESOLVED |NEW Last reconfirmed| |2015-05-12 CC| |rguenth at gcc dot gnu.org Component|tree-optimization |middle-end Resolution|FIXED |--- Ever confirmed|0 |1 --- Comment #3 from Richard Biener <rguenth at gcc dot gnu.org> --- So what happens is that GCC sees _3 = p_2(D)->p1; _3->f2 = 9; _5 = p_2(D)->p1; _5->f2 = 10; and to remove the first store it first has to prove that _3 and _5 are equal. CSE cannot prove this because it thinks the store to _3->f2 can clobber the value at p_2(D)->p1 (if p->p1 points to p). get_alias_set (_3->f2) returns 0, the alias oracle has special code to also consider the alias set of the base objects: /* Do type-based disambiguation. */ if (base1_alias_set != base2_alias_set && !alias_sets_conflict_p (base1_alias_set, base2_alias_set)) return false; but their alias sets happen to conflict because struct s2 alias set is a subset of the struct s1 alias set (which is because alias set zero is a subset of the struct s1 alias set...): struct GTY(()) alias_set_entry_d { /* The alias set number, as stored in MEM_ALIAS_SET. */ alias_set_type alias_set; /* Nonzero if would have a child of zero: this effectively makes this alias set the same as alias set zero. */ int has_zero_child; (I've always questioned this... - the code that looks at has_zero_child in alias_set_subset_of / alias_sets_conflict_p). Index: gcc/alias.c =================================================================== --- gcc/alias.c (revision 222996) +++ gcc/alias.c (working copy) @@ -470,15 +470,13 @@ alias_sets_conflict_p (alias_set_type se /* See if the first alias set is a subset of the second. */ ase = get_alias_set_entry (set1); if (ase != 0 - && (ase->has_zero_child - || ase->children->get (set2))) + && ase->children->get (set2)) return 1; /* Now do the same, but with the alias sets reversed. */ ase = get_alias_set_entry (set2); if (ase != 0 - && (ase->has_zero_child - || ase->children->get (set1))) + && ase->children->get (set1)) return 1; /* The two alias sets are distinct and neither one is the fixes this. I don't remember what broke (but I remember trying this for a few times - maybe with also changing alias_set_subset_of which isn't that obvious).