Message-ID: <19980112231700.01629@cerebro.laendle> Date: Mon, 12 Jan 1998 23:17:00 +0100 From: Marc Lehmann To: djgpp-workers AT delorie DOT com Cc: Andrew Crabtree Subject: stack alignment options Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Precedence: bulk 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 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 | |