This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Major deficiency in optimising memset calls


Hija,

I'm currently trying to track down why gcc is generating
this poor code when optimising memset with constant params.
A helping hand would be greatly appreciated.

Consider the following code:
char a[8]

int main (void)
{
  __builtin_memset (a, 0, 8);
}

This will result in two simple stores of the value 0 into
two adjacent 32bit values in memory on at least darwin-powerpc
and linux-i686. Great!

Next try:
char a[8]

int main (void)
{
  __builtin_memset (a, 1, 8);
}

will result in:
        pushl   %ebp
        movl    $16843009, %eax
        movl    %esp, %ebp
        subl    $8, %esp
        movl    $16843009, %edx
        andl    $-16, %esp
        movl    %eax, a+4
        subl    $16, %esp
        movl    %edx, a
        leave
        ret

on linux-i686 compiled with gcc-3.4 -c -march=i686 -mtune=i686 -O2.
Let's call that in interesting result.

gcc-4.0 (todays CVS) on darwin completely looses track here:

_main:
        lis r3,ha16(L_a$non_lazy_ptr)
        li r4,1
        li r5,8
        lwz r3,lo16(L_a$non_lazy_ptr)(r3)
        b L_memset$stub
.comm _a,8
.data
.section __TEXT,__symbol_stub1,symbol_stubs,pure_instructions,16
        .align 4
L_memset$stub:
        .indirect_symbol _memset
        lis r11,ha16(L_memset$lazy_ptr)
        lwzu r12,lo16(L_memset$lazy_ptr)(r11)
        mtctr r12
        bctr
.data
.lazy_symbol_pointer
L_memset$lazy_ptr:
        .indirect_symbol _memset
        .long   dyld_stub_binding_helper
.data
.non_lazy_symbol_pointer
L_a$non_lazy_ptr:
        .indirect_symbol _a
        .long   0
        .subsections_via_symbols


And it keeps getting worse: char a[12]

int main (void)
{
  __builtin_memset (a, 0, 12);
}

aforementioned gcc call on i686 will result in:
main:
        pushl   %ebp
        xorl    %eax, %eax
        movl    %esp, %ebp
        pushl   %edi
        cld
        subl    $4, %esp
        movl    $a, %edi
        movl    $3, %ecx
        andl    $-16, %esp
        subl    $16, %esp
        rep
        stosl
        movl    -4(%ebp), %edi
        leave
        ret

The rep;stosl sequence seems somewhat okay to me for a simply
inline sequence to clear a larger or only at runtime known
amount of memory, however for under 16 bytes this seems like a
clear loss in performance and code size to me.

On darwin-powerpc we get the same sequence as above except for
the to-be-written value and the amount which is a clear lose
no matter what in this case.

There are several facts known in this case which should help
to generate much better code:
- The allocated memory is local and not volatile
- The destination of the store is unused
- The alignment and size of the storage
- The destination memory is a (possibly large) block instead
  of scattered locations
- The parameters to the memset call are all constant

In addition to the poor code on i686 the results on darwin-powerpc
are really abysmal because we there seems to be no way to force
the inlining of memset which always means a *very* costly rountrip
into the linkage even for obvious uses.

I'm very certain that this pessimizes quite a lot of common
applications (including gcc) where a memset is chosen in favor
of a loop to clear a block of memory.

I'm currently digging into builtins.c but a helping hand would
be more than appreciated.

Servus,
      Daniel

Attachment: PGP.sig
Description: This is a digitally signed message part


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]