Search code examples
cassemblyx86-64stack-memorystack-frame

Is there a way to calculate the bytes allocated to the stack frame of a function?


I have been given this code in C and I need to calculate the bytes allocated to the stack frame of function arith. I looked everywhere for a way to do it but everyone has a different answer.

long arith(long x, long y, long z){
    long t1 = x + y;
    long t2 = z + t1;
    long t3 = x + 4;
    long t4 = y * 48;
    long t5 = t3 + t4;
    long rval = t2 * t5;
    return rval;
}

    arith:
    .LFB0:
        .cfi_startproc
        endbr64
        pushq    %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movq    %rdi, -56(%rbp)
        movq    %rsi, -64(%rbp)
        movq    %rdx, -72(%rbp)
        movq    -56(%rbp), %rdx
        movq    -64(%rbp), %rax
        addq    %rdx, %rax
        movq    %rax, -48(%rbp)
        movq    -72(%rbp), %rdx
        movq    -48(%rbp), %rax
        addq    %rdx, %rax
        movq    %rax, -40(%rbp)
        movq    -56(%rbp), %rax
        addq    $4, %rax
        movq    %rax, -32(%rbp)
        movq    -64(%rbp), %rdx
        movq    %rdx, %rax
        addq    %rax, %rax
        addq    %rdx, %rax
        salq    $4, %rax
        movq    %rax, -24(%rbp)
        movq    -32(%rbp), %rdx
        movq    -24(%rbp), %rax
        addq    %rdx, %rax
        movq    %rax, -16(%rbp)
        movq    -40(%rbp), %rax
        imulq    -16(%rbp), %rax
        movq    %rax, -8(%rbp)
        movq    -8(%rbp), %rax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc

I tried to add up the bytes every time we call the datatype long so 6*8, which is 64 bytes but im not very sure about this.


Solution

  • TL:DR: no, you can't tell just from looking at the C.
    You need to look at the asm for a particular build and count pushes as well as sub $imm, %rsp.

    In this case, the variables aren't volatile so a normal build (with optimization enabled) would just use registers, not touching the stack pointer at all. (You could say that the variables had their addresses optimized away, since they didn't escape the function, and nothing even took their addresses in the first place.)
    See it yourself on Godbolt with GCC13 -O3 -fverbose-asm

     # first set of comments is compiler-generated, second are hand-written
    arith:
            lea     rax, [rsi+rsi*2]  # t4,    # RAX = y*3
            sal     rax, 4    # tmp96,         # t4 = RAX = y*3*16 = y*48
            lea     rax, [rdi+4+rax]  # t5,    # t5 = RAX += x+4
            add     rdi, rsi  # t1, y          # t1 =  x + y  (replacing x in RDI)
            add     rdi, rdx  # t2, tmp102     # t2 =  x+y + z
            imul    rax, rdi        # rval, t2  # rval = t5 * t2
            ret     
    

    If optimization is disabled (Why does clang produce inefficient asm with -O0 (for this simple floating point sum)?), you can calculate a minimum from the sizeof() and alignof() for the named C variables (assuming optimal packing), but compilers might reserve more for various reasons, like to align RSP, or

    In a leaf function in the x86-64 System V ABI, the compiler can just use the space already reserved (the red zone) for some/all of the space it needs, so it might appear that less is needed: