Builtin bzero? Please don't.

David Holland dholland@eecs.harvard.edu
Thu Jan 10 13:54:00 GMT 2002


I discovered just now that gcc 3.0.3, with -O1 or higher, and without
-ansi, converts calls to bzero() into calls to memset(), entirely on
its own, without being told to.

There are three reasons why this is incorrect:

   1. bzero() is not a standard function. It is inappropriate to
      assume that an arbitrary call to a function called bzero can be
      correctly transformed into a call to memset, even if -ansi is
      not specified. Whether it is formally incorrect is debatable,
      but it certainly violates the principle of least surprise, since
      it happens without any request to the compiler. (If I wanted
      bzero calls to be transformed into memset calls, I would put an
      inline definition into string.h.) 

   2. This transformation does not accomplish anything. There is
      nothing to be saved from converting bzero calls into memset
      calls, except perhaps a few bytes in the C library image which
      probably need to be retained for compatibility anyway. Such
      savings are not worth the potential problems.

   3. Worse, this transformation is an anti-optimization. On many
      platforms, the fact that bzero is known to be filling with zeros
      can be exploited to make it faster and simpler than memset. And
      on some platforms, there are MMU or cache features that allow
      direct zeroing of blocks of memory. These features can be used
      in a bzero implementation, but not in a memset implementation.

      It would be more useful for the compiler to make the
      transformation the other way around: detect calls to memset with
      a zero fill-byte argument, which, when present, is almost always
      constant, and convert them to calls to bzero.

In general, I think, gcc needs to offer more control over builtin
functions to C library implementors. This sort of transformation is
fine, and in fact possibly quite useful, *if* it is only performed
when explicitly requested. If it's necessary to have these
transformations work even on source that doesn't include appropriate
headers, which it might be, it seems to me that the solution is to
have a special header file that's automatically processed with cpp
-include.

The above diagnosis was made with stock gcc 3.0.3 configured for
mips-linux on i386-netbsd:

  Reading specs from ../usr/bin/../lib/gcc-lib/mips-linux/3.0.3/specs
  Configured with: ../../option2/gcc-3.0.3/configure --target=mips-linux
  --nfp --disable-shared --prefix=/usr/tmp/tools02/test2/usr
  Thread model: single
  gcc version 3.0.3

(I should note that it didn't build without --disable-shared; I didn't
take enough notes to file a proper bug report, though.)

Sample code follows:

/*
 * Demonstration code.
 * Compile this with: mips-linux-gcc -S -O2 test.c
 * Examine the resulting .s file, which calls memset instead of bzero.
 */

typedef unsigned size_t;
void bzero(void *, size_t);

void foo(void) {
   long array[12];
   bzero(array, sizeof(array));

   /* prevent array from being optimized away */
   (void)(volatile long *)array;
}

-- 
   - David A. Holland             |    VINO project home page:
     dholland@eecs.harvard.edu    | http://www.eecs.harvard.edu/vino




More information about the Gcc-bugs mailing list