0

I'm trying to improve my understanding of assembly, just so I can at least have a rough idea of what a given compiled code is doing. And for fun.

I've choosen an example which is relatively simple, even if not the simplest:

int boo(int);
int bar(int, int, int);

int foo(int i) {
    int a = bar(boo(i), boo(i), boo(i));
    int b = bar(boo(i), boo(i), boo(i));
    return a + b;
}

The assembly corresponding to the C++ translation unit, is the following (complete example here):

foo(int):
        pushq   %r13
        pushq   %r12
        pushq   %rbp
        pushq   %rbx
        movl    %edi, %ebx
        subq    $8, %rsp
        call    boo(int)
        movl    %ebx, %edi
        movl    %eax, %r12d
        call    boo(int)
        movl    %ebx, %edi
        movl    %eax, %ebp
        call    boo(int)
        movl    %r12d, %edx
        movl    %ebp, %esi
        movl    %eax, %edi
        call    bar(int, int, int)
        movl    %ebx, %edi
        movl    %eax, %ebp
        call    boo(int)
        movl    %ebx, %edi
        movl    %eax, %r13d
        call    boo(int)
        movl    %ebx, %edi
        movl    %eax, %r12d
        call    boo(int)
        movl    %r13d, %edx
        movl    %r12d, %esi
        movl    %eax, %edi
        call    bar(int, int, int)
        addq    $8, %rsp
        popq    %rbx
        addl    %ebp, %eax
        popq    %rbp
        popq    %r12
        popq    %r13
        ret

Why is the stack pointer, %rsp altered at all?

It looks to me that all computation is accomplished by using registers, and no local variable, so why there's any need to make space in the stack via subq $8, %rsp?


This question was originally less focused (see the edit history), but I've entirely reworded it as I got a better understanding of the matter thanks to the links that were provided in the comments:

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • re: your update: Where is the compiler going to keep `i` such that it still has access to the value after the first call to `boo(i)`? It was in EDI on function entry, and it needs the value again to pass it in EDI to another `boo(i)` call. And it needs to save the three return values of `boo` calls somewhere. It can't use the red-zone since this is a non-leaf function, so its options are reserving stack space or saving/restoring the caller's values of call-preserved regs so it can use them. [What are callee and caller saved registers?](https://stackoverflow.com/a/56178078) – Peter Cordes Aug 28 '23 at 18:14
  • @PeterCordes, my understanding is that `i` is saved into EBX via `movl %edi, %ebx`. After all, after making the space on the stack, there's no push operation that makes use of it, no? – Enlico Aug 28 '23 at 18:18
  • 1
    That's correct about `mov %edi, %ebx` saving `i`. (But `push` doesn't use already-reserved space, it reserves new space). Oh, now I see from the question text you were just asking about the stack-pointer movement from `sub`, not from the `push` instructions. 16-byte RSP alignment is required before a call, so the total RSP adjustment between function entry and the first `call` must be an odd multiple of 8, but it only needed four call-preserved regs. ([Why does the x86-64 / AMD64 System V ABI mandate a 16 byte stack alignment?](https://stackoverflow.com/q/49391001)) – Peter Cordes Aug 28 '23 at 18:19
  • [glibc scanf Segmentation faults when called from a function that doesn't align RSP](https://stackoverflow.com/q/51070716) explains in more detail what asm has to do to maintain stack alignment – Peter Cordes Aug 28 '23 at 18:22

0 Answers0