Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is the compiler allowed to optimize the 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 (comments added by myself, I'm a complete newbie to 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 the sizes of 1 byte, 4 byte and 1 byte. Including the padding this would be 12 byte (assuming the compiler aligns to 4 bytes).

Wouldn't it be more memory efficient if the compiler would change the order of the variables to (a, b, i)? Then only 8 bytes would be necessary.

Here a "graphic" representation:

    3 bytes unused                  3 bytes unused
     vvvvvvvvvvv                     vvvvvvvvvvv
+---+---+---+---+---+---+---+---+---+---+---+---+
| a |   |   |   | i             | b |   |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+

                |
                v

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

Is the compiler allowed to do this optimization (by the C standard etc.)?

  • If no (as I think the assembly output shows), why?
  • If yes, why doesn't that happen above?
like image 426
MarcDefiant Avatar asked Apr 09 '13 11:04

MarcDefiant


People also ask

How are local variables allocated on the stack?

The stack is used for dynamic memory allocation, and local variables are stored at the top of the stack in a stack frame. A frame pointer is used to refer to local variables in the stack frame.

Does a local variable allocate space on the stack?

When a new local variables is declared, more stack memory is allocated for that function to store the variable. Such allocations make the stack grow downwards. After the function returns, the stack memory of this function is deallocated, which means all local variables become invalid.

Why are local variables stored in stack?

When a function is called the local variables are stored in a stack, and it is automatically destroyed once returned. A stack is used when a variable is not used outside that function. It allows you to control how memory is allocated and deallocated. Stack automatically cleans up the object.

In what order are variables pushed on the stack?

The C calling convention states that the parameters are pushed onto the stack in reverse order. That means that the last parameter is pushed first and the first parametersis pushed last. This means that in the procedure, the first parameter will be the parameter closest to the top of the stack.


2 Answers

The compiler is free to layout the local variables as it wants. It need not even use the stack.

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

Is the compiler allowed to do this optimization (by the C standard etc.)?

  • If yes, why doesn't that happen above?

Well, is it an optimisation at all?

That's not clear. It uses a couple of bytes less, but that rarely matters. But on some architectures, it may be faster to read a char if it is stored word-aligned. So then putting the chars next to each other would force one of them at least to not be word-aligned and make reading it slower.

like image 164
Daniel Fischer Avatar answered Oct 28 '22 02:10

Daniel Fischer


Is the compiler allowed to do this optimization (by the C standard etc.)?

Yes.

If yes, why doesn't that happen above?

It did happen.

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 at offset 27. Variable i is at offset 28.

Using the images you made the layout is now:

+---+---+---+---+---+---+---+---+
|   |   | a | b | i             |
+---+---+---+---+---+---+---+---+
 ^^^^^^^
 2 bytes unused
like image 4
Art Avatar answered Oct 28 '22 04:10

Art