Mail Archives: djgpp-workers/1998/01/13/12:24:58
I was asked to sent an explanation of the options pgcc (and egcs sometime
in the future) uses to ensure an 8 byte alignment of doubles, how it is
achieved and what breaks.
pgcc has three switches:
-malign-double this is old, even gcc-2.7.2 has it
-mstack-align-double this aligns the stack to an 8 byte boundary
-marg-align-double this aligns function arguments to an 8 byte boundary
-malign-double is used to align doubles in structs to an 8 byte boundary, i.e.
struct offset with /without switch
int i 0 0
double j 4 8
int k 12 16
this (obiously) breaks the published sysv x86 ABI, since code compiled with
and without this option is incompatible when doubles in struct's are used
without proper alignment.
-mstack-align-double assumes that the stack is suitably aligned
i.e. on an 8 byte boundary before the function call, and will
ensure that spill/frame slots containing doubles will be correctly
aligned (i.e. auto variables).
This does NOT break the ABI, since auto variables are function-private, the
call protocol is not being changed. This means that arguments to function
might still be misaligned (and are copied into aligned stack slots upon
function entry).
gcc assumes that the stack is aligned AT the function call, i.e. BEFORE the
call instruction is executed, or AFTER the function prologue has executed,
NOT upon function entry, since gcc generally doesn't know about prologue
code:
code %esp (hex) _before_ insn.
movl $1,%eax 1f00c
pushl %eax 1f00c
call fun 1f008
fun: 1f004
pushl %ebx 1f004
nop 1f000
...
the stack itself must already be aligned by the startup code upon entry to
main(). linux uses a code equivalent to this (taken from glibc2.0.6):
/* Before pushing the arguments align the stack to a double word
boundary to avoid penalties from misaligned accesses. Thanks
to Edward Seidl <seidl AT janed DOT com> for pointing this out. */
andl $0xfffffff8, %esp
pushl %eax /* Push garbage because we allocate
twelve more bytes. */
pushl %eax /* Push third argument: envp. */
pushl %edx /* Push second argument: argv. */
pushl %esi /* Push first argument: argc. */
/* Call the user's main function, and exit with its value. */
call main
pushl %eax
call exit
hlt /* Crash if somehow `exit' does return. */
to be effective, all code execution passes thru must be compiled with
-mstack-align-double, to the minimum all code that's executed before entry
to main(), i.e. the startup code. -mstack-align-double also imposes a small
integer penalty because the alignment has to be enforced with additional
push insns. also, function like qsort() possibly destroys the alignment
(although these libc functions are rare).
it's IMHO best only to align the actual call to main and not compile anything
with this switch (even more since this switch isn't available in every
gcc version).
-marg-align-double will align doubles in argument slots (parameters
to function calls) to an 8 byte boundary. This DOES break the ABI and makes
the same assumptions about alignment as -mstack-align-double.
Due to a bug in gcc, this currently only works with -mamdk6 (the amd
doesn't use pushes) anyway.
I hope this clarifies the issue a bit... If I forgot sth. or was unclear,
don't hesitate to ask me!
-----==- |
----==-- _ |
---==---(_)__ __ ____ __ Marc Lehmann +--
--==---/ / _ \/ // /\ \/ / pcg AT goof DOT com |e|
-=====/_/_//_/\_,_/ /_/\_\ --+
The choice of a GNU generation |
|
- Raw text -