Is the compiler allowed to optimize stack memory usage by reordering local variables?

Consider the following program:

#include <stdio.h> void some_func(char*, int*, char*); void stack_alignment(void) { char a = '-'; int i = 1337; char b = '+'; some_func(&a, &i, &b); // to prevent the compiler from removing the local variables printf("%c|%i|%c", a, i, b); } 

It generates the following assembly (the comments added by me, I'm a complete newbie for the assembly):

 $ vim stack-alignment.c $ gcc -c -S -O3 stack-alignment.c $ cat stack-alignment.s .file "stack-alignment.c" .section .rdata,"dr" LC0: .ascii "%c|%i|%c\0" .text .p2align 2,,3 .globl _stack_alignment .def _stack_alignment; .scl 2; .type 32; .endef _stack_alignment: LFB7: .cfi_startproc subl $44, %esp .cfi_def_cfa_offset 48 movb $45, 26(%esp) // local variable 'a' movl $1337, 28(%esp) // local variable 'i' movb $43, 27(%esp) // local variable 'b' leal 27(%esp), %eax movl %eax, 8(%esp) leal 28(%esp), %eax movl %eax, 4(%esp) leal 26(%esp), %eax movl %eax, (%esp) call _some_func movsbl 27(%esp), %eax movl %eax, 12(%esp) movl 28(%esp), %eax movl %eax, 8(%esp) movsbl 26(%esp), %eax movl %eax, 4(%esp) movl $LC0, (%esp) call _printf addl $44, %esp .cfi_def_cfa_offset 4 ret .cfi_endproc LFE7: .def _some_func; .scl 2; .type 32; .endef .def _printf; .scl 2; .type 32; .endef 

As you can see, there are 3 local variables ( a , i and b ) with sizes of 1 byte, 4 bytes and 1 byte. Including filling, it will be 12 bytes (provided that the compiler is aligned with 4 bytes).

Wouldn't it be more efficient to use memory if the compiler changed the order of the variables to ( a , b , i )? Then only 8 bytes are needed.

Here is a graphical representation:

  3 bytes unused 3 bytes unused vvvvvvvvvvv vvvvvvvvvvv +---+---+---+---+---+---+---+---+---+---+---+---+ | a | | | | i | b | | | | +---+---+---+---+---+---+---+---+---+---+---+---+ | v +---+---+---+---+---+---+---+---+ | a | b | | | i | +---+---+---+---+---+---+---+---+ ^^^^^^^ 2 bytes unused 

Is the compiler allowed to perform this optimization (according to the C standard, etc.)?

  • If not (it seems to me that the assembly is being displayed), why?
  • If so, why doesn't this happen above?
+7
source share
5 answers

Is the compiler allowed to perform this optimization (according to the C standard, etc.)?

Yes.

If so, why is this not so?

It happened.

Read the assembler output carefully.

  movb $45, 26(%esp) // local variable 'a' movl $1337, 28(%esp) // local variable 'i' movb $43, 27(%esp) // local variable 'b' 

Variable a is at offset 26. Variable b is equal to offset 27. Variable i is equal to offset 28.

Using the images you made, the layout is now:

 +---+---+---+---+---+---+---+---+ | | | a | b | i | +---+---+---+---+---+---+---+---+ ^^^^^^^ 2 bytes unused 
+4
source

The compiler is free to compose local variables as it sees fit. He should not even use the stack.

It can store local variables in an order unrelated to the order of declaration on the stack if it uses the stack.

Is the compiler allowed to perform this optimization (according to the C standard, etc.)?

  • If so, why doesn't this happen above?

Well, is that optimization at all?

This is not clear. It uses a couple of bytes less, but that rarely matters. But on some architectures, it may be faster to read char if it is stored with word-alignment. Thus, adding char next to each other will cause one of them to at least not align with words and make reading slower.

+7
source

Wouldn't it be more memory efficient if the compiler reordered the variables

It is impossible to say without speaking about a specific processor, a specific OS and a specific compiler. In general, the compiler works optimally. To optimize your code in a meaningful way, you need in-depth knowledge about a particular system.

In your case, in this case, the compiler will most likely be optimized for speed. It seems that the compiler has decided that aligned addresses for each variable give the most efficient code. In some systems, this happens not only faster, but also necessarily allocated to even addresses, because some processors can only handle consistent access.

Is the compiler allowed to perform this optimization (according to the C standard, etc.)?

Yes, standard C does not even require the allocation of variables. The compiler is completely free to handle this the way he wants, and he does not need to document how and why. It can allocate variables anywhere, it can fully optimize them or place them inside processor registers or on the stack or in a small wooden box under your desk.

+2
source

Typically, on normal systems where speed matters, reading the word wise is faster than reading characters. Memory loss compared to speed gain is ignored. But in the case of a system in which memory is important, as in different cross-compilers that generate an executable file (in a very general sense) for a specific target platform, the picture may be completely different. The compiler can assemble them together, even check their service life and use, depending on what reduces the bit rate, etc. Etc. Therefore, basically it depends a lot on necessity. But in general, every compiler gives you flexibility if you want to "pack" them tightly. You can study the manual for this.

0
source

Compilers with stack buffer overflow protection ( /GS for the Microsoft compiler) can reorder variables as a security function. For example, if your local variables are an array (w980) with a constant size (w980) and a pointer to a function, an attacker who can overflow the buffer can also overwrite the pointer to the function. Thus, local variables are reordered so that the buffer is next to the canary. Thus, the attacker cannot (directly) compromise the function pointer and buffer overflow (hopefully) is detected by the destroyed canary.

A warning. Such features do not prevent compromise; they simply raise barriers for the attacker, but an experienced attacker usually finds its way.

0
source

All Articles