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

            Bug ID: 71690
           Summary: integer conversion defeats memcpy optimizaton
           Product: gcc
           Version: 7.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: middle-end
          Assignee: unassigned at gcc dot gnu.org
          Reporter: msebor at gcc dot gnu.org
  Target Milestone: ---

Both functions in the following program are expected to result in comparably
efficient code.  In expand_builtin_memcpy, GCC decides whether an invocation of
__builtin_memcpy will be expanded inline or result in a library call.  Among
the factors it uses to make that decision is the range of sizes of the copy. 
The range is obtained from the result of the Value Range Propagation
optimization for the size argument, provided the argument is constrained to a
subrange of its type.  When the argument's type is other than size_t, VRP makes
the range available, and the call to memcpy may be expanded inline (this is the
case with the function g below).  But when the argument's type is size_t, VRP
does not make its range available, and the expansion results in a library call,
thus defeating the optimization.  This seems to be a general problem with VRP,
not one limited to memcpy (I just used memcpy as an example).  Any built-in
whose expansion depends on the result of VRP of one of its arguments may be
affected.

char d [10];
char s [10];

void f (int);

void g (unsigned n)
{
  if (n >= sizeof d) return;

  __builtin_memcpy (d, s, n);
}

void h (unsigned long n)
{
  if (n >= sizeof d) return;

  __builtin_memcpy (d, s, n);
}



The problem can be seen in the generated assembly and also in the vrp dump for
the program (see the <<< annotations):

Value ranges after VRP:

...
_1: [0, 9]
.MEM_2: VARYING
n_3(D): VARYING
n_6: [0, 9]  EQUIVALENCES: { n_3(D) } (1 elements)


g (unsigned int n)
{
  long unsigned int _1;

  <bb 2>:
  if (n_3(D) > 9)
    goto <bb 4>;
  else
    goto <bb 3>;

  <bb 3>:
  _1 = (long unsigned int) n_3(D);   <<< constrained to [0, 9]
  __builtin_memcpy (&d, &s, _1);

  <bb 4>:
  return;
}
...
Value ranges after VRP:

.MEM_1: VARYING
n_2(D): VARYING
n_5: [0, 9]  EQUIVALENCES: { n_2(D) } (1 elements)


h (long unsigned int n)
{
  <bb 2>:
  if (n_2(D) > 9)
    goto <bb 4>;
  else
    goto <bb 3>;

  <bb 3>:
  __builtin_memcpy (&d, &s, n_2(D));   <<< n_2(D) is VARYING

  <bb 4>:
  return;

}

Reply via email to