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).

Reply via email to