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;

}

Reply via email to