https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80944

            Bug ID: 80944
           Summary: redundant memcpy/memset with non-constant size not
                    eliminated
           Product: gcc
           Version: 7.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: msebor at gcc dot gnu.org
  Target Milestone: ---

GCC eliminates redundant calls to memset (and memcpy) with the same destination
and known size, but it fails to eliminate such calls when the size is not
known.  This is a missed optimization opportunity (I haven't found a compiler
that does eliminate the first memset.)

As an example, consider the following function that tries to zero out the
memory occupied by the object just before releasing it (in C++, S could be a
std::string or similar class).  GCC retains the first memset call but
successfully eliminates the second, even though both could be eliminated.

$ cat t.c && gcc -O2 -S -Wall -Wextra -fdump-tree-optimized=/dev/stdout t.c
struct S { char *s; unsigned n; };

void f (struct S *s)
{
  __builtin_memset (s->s, 0, s->n);
  __builtin_free (s->s);
  __builtin_memset (s, 0, sizeof *s);
  __builtin_free (s);
}

;; Function f (f, funcdef_no=0, decl_uid=1796, cgraph_uid=0, symbol_order=0)

f (struct S * s)
{
  unsigned int _1;
  long unsigned int _2;
  char * _3;
  char * _4;

  <bb 2> [100.00%]:
  _1 = s_6(D)->n;
  _2 = (long unsigned int) _1;
  _3 = s_6(D)->s;
  __builtin_memset (_3, 0, _2);
  _4 = s_6(D)->s;
  __builtin_free (_4);
  __builtin_free (s_6(D)); [tail call]
  return;

}

The following is a more comprehensive test case:

$ cat t.c && gcc -O2 -S -Wall -Wextra -fdump-tree-optimized=/dev/stdout t.c

struct S { unsigned n; char a[32], b[]; };

void clear_cst (struct S *s)
{
  __builtin_memset (s->a, 0, sizeof s->a);
  __builtin_memset (s->a, 0, sizeof s->a);   // eliminated
}

void copy_cst (struct S *s)
{
  __builtin_memcpy (s->a, s->b, sizeof s->a);
  __builtin_memcpy (s->a, s->b, sizeof s->a);   // eliminated
}

void clear_var (struct S *s)
{
  __builtin_memset (s->b, 0, s->n);
  __builtin_memset (s->b, 0, s->n);   // not eliminated
}

void copy_var (struct S *s)
{
  __builtin_memcpy (s->b, s->a, s->n);
  __builtin_memcpy (s->b, s->a, s->n);   // not eliminated
}


;; Function clear_cst (clear_cst, funcdef_no=0, decl_uid=1797, cgraph_uid=0,
symbol_order=0)

clear_cst (struct S * s)
{
  char[32] * _1;

  <bb 2> [100.00%]:
  _1 = &s_2(D)->a;
  __builtin_memset (_1, 0, 32); [tail call]
  return;

}



;; Function copy_cst (copy_cst, funcdef_no=1, decl_uid=1800, cgraph_uid=1,
symbol_order=1)

copy_cst (struct S * s)
{
  char[0:] * _1;
  char[32] * _2;

  <bb 2> [100.00%]:
  _1 = &s_3(D)->b;
  _2 = &s_3(D)->a;
  __builtin_memcpy (_2, _1, 32); [tail call]
  return;

}



;; Function clear_var (clear_var, funcdef_no=2, decl_uid=1803, cgraph_uid=2,
symbol_order=2)

clear_var (struct S * s)
{
  unsigned int _1;
  long unsigned int _2;
  char[0:] * _3;

  <bb 2> [100.00%]:
  _1 = s_5(D)->n;
  _2 = (long unsigned int) _1;
  _3 = &s_5(D)->b;
  __builtin_memset (_3, 0, _2);
  __builtin_memset (_3, 0, _2); [tail call]
  return;

}



;; Function copy_var (copy_var, funcdef_no=3, decl_uid=1806, cgraph_uid=3,
symbol_order=3)

copy_var (struct S * s)
{
  unsigned int _1;
  long unsigned int _2;
  char[32] * _3;
  char[0:] * _4;

  <bb 2> [100.00%]:
  _1 = s_6(D)->n;
  _2 = (long unsigned int) _1;
  _3 = &s_6(D)->a;
  _4 = &s_6(D)->b;
  __builtin_memcpy (_4, _3, _2);
  __builtin_memcpy (_4, _3, _2); [tail call]
  return;

}

Reply via email to