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