https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92245
Bug ID: 92245 Summary: strncpy followed by nul store not folded into memcpy Product: gcc Version: 9.0 Status: UNCONFIRMED Severity: enhancement Priority: P3 Component: tree-optimization Assignee: unassigned at gcc dot gnu.org Reporter: msebor at gcc dot gnu.org Target Milestone: --- Because the strlen pass has no support for strncpy beyond warnings, GCC emits suboptimal code for all functions below except f0. In f1(), the strncpy call and nul store can be merged into a single memcpy. The same can happen in f2(), f3(), and f4(). In addition, the strlen calls can then be folded into constants. Since strncpy followed by a nul store to terminate the string is a common idiom this could would not only lead to better code but also help expose buffer overflows when inappropriately using the result. With the strlen calls removed, Clang emits the same optimal code for f0() and f1(), but does just as poorly on the rest of the functions. $ cat z.c && gcc -O2 -S -Wall -fdump-tree-optimized=/dev/stdout z.c extern char a[8]; void f0 (void) // optimal { __builtin_memcpy (a, "123", 4); if (__builtin_strlen (a) != 3) __builtin_abort (); } void f1 (void) { __builtin_strncpy (a, "123", 3); a[3] = 0; // can be merged with the above if (__builtin_strlen (a) != 3) __builtin_abort (); } void f2 (void) { const char s[] = "123"; __builtin_strncpy (a, s, 3); a[3] = 0; // can be merged with the above if (__builtin_strlen (a) != 3) // can be folded to false __builtin_abort (); } void f3 (const char *s) { if (__builtin_strlen (s) != 3) return; __builtin_strncpy (a, s, 3); a[3] = 0; // can be merged with the above if (__builtin_strlen (a) != 3) // can be folded to false __builtin_abort (); } void f4 (const char *s) { if (__builtin_strlen (s) < 3) return; __builtin_strncpy (a, s, 3); a[3] = 0; // can be merged with the above if (__builtin_strlen (a) != 3) // can be folded to false __builtin_abort (); } ;; Function f0 (f0, funcdef_no=0, decl_uid=1931, cgraph_uid=1, symbol_order=0) f0 () { <bb 2> [local count: 1073741824]: __builtin_memcpy (&a, "123", 4); [tail call] return; } ;; Function f1 (f1, funcdef_no=1, decl_uid=1934, cgraph_uid=2, symbol_order=1) f1 () { <bb 2> [local count: 1073741824]: __builtin_memcpy (&a, "123", 3); a[3] = 0; return; } ;; Function f2 (f2, funcdef_no=2, decl_uid=1937, cgraph_uid=3, symbol_order=2) f2 () { const char s[4]; long unsigned int _1; <bb 2> [local count: 1073741824]: s = "123"; __builtin_strncpy (&a, &s, 3); a[3] = 0; _1 = __builtin_strlen (&a); if (_1 != 3) goto <bb 3>; [0.00%] else goto <bb 4>; [100.00%] <bb 3> [count: 0]: __builtin_abort (); <bb 4> [local count: 1073741824]: s ={v} {CLOBBER}; return; } ;; Function f3 (f3, funcdef_no=3, decl_uid=1941, cgraph_uid=4, symbol_order=3) Removing basic block 6 Removing basic block 7 f3 (const char * s) { long unsigned int _1; long unsigned int _2; <bb 2> [local count: 1073741824]: _1 = __builtin_strlen (s_5(D)); if (_1 != 3) goto <bb 5>; [67.00%] else goto <bb 3>; [33.00%] <bb 3> [local count: 354334802]: __builtin_strncpy (&a, s_5(D), 3); a[3] = 0; _2 = __builtin_strlen (&a); if (_2 != 3) goto <bb 4>; [0.00%] else goto <bb 5>; [100.00%] <bb 4> [count: 0]: __builtin_abort (); <bb 5> [local count: 1073741826]: return; } ;; Function f4 (f4, funcdef_no=4, decl_uid=1944, cgraph_uid=5, symbol_order=4) Removing basic block 6 Removing basic block 7 f4 (const char * s) { long unsigned int _1; long unsigned int _2; <bb 2> [local count: 1073741824]: _1 = __builtin_strlen (s_5(D)); if (_1 <= 2) goto <bb 5>; [51.12%] else goto <bb 3>; [48.88%] <bb 3> [local count: 524845004]: __builtin_strncpy (&a, s_5(D), 3); a[3] = 0; _2 = __builtin_strlen (&a); if (_2 != 3) goto <bb 4>; [0.00%] else goto <bb 5>; [100.00%] <bb 4> [count: 0]: __builtin_abort (); <bb 5> [local count: 1073741828]: return; }