__attribute__((naked) on x86 would be useful

Mat Hostetter mat@curl.com
Mon Feb 16 16:30:00 GMT 2004


>>>>> "jakub" == Jakub Jelinek <jakub@redhat.com> writes:

 jakub> -O2 -fomit-frame-pointer compiles this into
 jakub>         movl    4(%esp), %eax
 jakub>         addl    $16, %eax
 jakub>         movl    %eax, 4(%esp)
 jakub>         jmp     bar
 jakub> on both gcc-3_3-rhl-branch and gcc-3_4-branch.

Impressive!  I should try upgrading to a newer gcc.  This moots the
example I gave, and lets me write it in C, although I still have a
few other cases where an all-assembly proc is useful.

 >> Interestingly, that movl/addl/movl in the middle there could just
 >> as well be one instruction.  Perhaps there is a missing peephole
 >> optimization, or something is missed in the interaction between
 >> tail calls and peephole optimizations.

 jakub> Why?  You are not optimizing for space, and the 3 insns are
 jakub> faster I think (contemporary IA-32 CPUs prefer simple
 jakub> instructions).

If you increment a global variable ("global += 3"), gcc (correctly)
generates "addl $3,global", rather than a load/add/store sequence.
Indeed, gcc even recombines "int tmp = global; tmp += 3; global = tmp;"
into "addl $3,global", so I suspect the three instruction sequence
in your assembly snippet is unintentional.

As you probably know, contemporary IA-32 CPUs internally break complex
instructions into simpler internal "micro-ops" and run those.
Micro-ops get thrown on a big pile and the out-of-order engine deals
with executing them.  Consequently, x86 integer instruction scheduling
is less important now than it used to be (although there is still room
for it).

Post-Pentium 1, if you have a read-modify-write sequence as
back-to-back instructions, you're generally better off writing it as
one CISCy instruction, saving some icache space, and letting the
hardware deal with splitting it into smaller pieces at runtime.  There
can be exceptions because of the "4-1-1" rule for which combinations
of instructions can be launched in a cycle on some Intel CPUs, but
this is a good rule of thumb.

To test my claim I timed 100 instances of this sequence on a Pentium 4
using the "rdtsc" instruction:

    movl 4(%edx),%eax ; addl $3,%eax ; movl %eax,4(%edx)

against 100 of this:

    addl $3,4(%edx)

and the latter is actually substantially faster.  I admit this test
isn't generally applicable because the "simple" instructions aren't
being scheduled around other work, but this is also the case in the
simple trampoline under discussion, where the read-modify-write
instructions are all back to back.

 >> Also, is there any chance gcc could "forget" the current asm
 >> section when it emits user assembly code, since that assembly may
 >> change the section behind its back?  I haven't checked, but that
 >> sounds like a

 jakub> If the __asm changes the section behind GCC's back, it should
 jakub> restore it back at the end, say: asm (".section foobar;
 jakub> something; .previous");

Thanks, that wasn't in the gas docs last time I looked, but it is now. :-)
That's very useful for linux, but unfortunately it says it's
ELF-specific.  I tried it and it doesn't work on cygwin, at least with
gas 2.13.90 20030308, so I still wonder if my hypothetical one-line change
to gcc is reasonable.

-Mat



More information about the Gcc mailing list