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;
}