https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119006
Jakub Jelinek <jakub at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |jakub at gcc dot gnu.org --- Comment #9 from Jakub Jelinek <jakub at gcc dot gnu.org> --- struct A { bool operator== (const char *x) const { return x and not __builtin_strcmp (a, x); } char a[11]; }; struct B { bool operator== (const char *x) const { return x and not __builtin_strcmp (a, x); } bool operator!= (const char *x) const { return !(*this == x); } char a[128]; }; [[gnu::noinline,gnu::used]] int foo (const A& lhs, const char* rhs) { return lhs == rhs; } constexpr const char *t = "abcdefghijklmno"; [[gnu::noinline,gnu::used]] void bar (B x) { if (x != t) __builtin_abort (); } int main () { B b; __builtin_strcpy (b.a, t); bar (b); } Seems ICF performs only very limited type comparison for the argument types: sem_function::equals_private does if (!types_compatible_p (TREE_TYPE (arg1), TREE_TYPE (arg2))) return return_false_with_msg ("argument types are not compatible"); but that doesn't check much, in GIMPLE all pointer types are compatible except for TYPE_ADDR_SPACE differences or data vs. function pointer types. And then /* Perform additional checks for used parameters. */ if (!compatible_parm_types_p (TREE_TYPE (arg1), TREE_TYPE (arg2))) return false; but that pretty much repeats those, punts on TREE_CODE differences (pointer vs. reference), TYPE_RESTRICT changes and compatible_types_p just repets what has been checked already. Now, the question is if optimizations derived anything from the type pointer arguments to functions point to. In this testcase, I wonder if it isn't more about _1 = &this_5(D)->a; vs. _1 = &this_5(D)->a; where in one case const struct B * const this and in another case const struct A * const this isn't different, i.e. whether we shouldn't be checking type on COMPONENT_REF. Because during strlen1 it is __builtin_strcmp (&MEM[(const struct B *)&x].a, "abcdefghijklmno") vs. __builtin_strcmp (&MEM[(const struct A *)&x].a, "abcdefghijklmno") that results in the lhs have the [irange] int [-INF, -1][1, +INF] range computed just in the latter case and not the former. Or maybe even better compare it on MEM_REF? The MEM_REF handling does right now: case MEM_REF: { tree x1 = TREE_OPERAND (t1, 0); tree x2 = TREE_OPERAND (t2, 0); tree y1 = TREE_OPERAND (t1, 1); tree y2 = TREE_OPERAND (t2, 1); if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2))) return return_false (); /* Type of the offset on MEM_REF does not matter. */ return return_with_debug (sem_variable::equals (x1, x2) && known_eq (wi::to_poly_offset (y1), wi::to_poly_offset (y2))); } That is really weird and unexpected. The types of x1 and x2 should be pretty much always be compatible (exceptions are restrict/address space I think). But it doesn't compare TREE_TYPE (t1) vs. TREE_TYPE (t2), and for aliasing also the important TREE_TYPE (y1), TREE_TYPE (y2).