clarifications on assembler code when declaring an array with a size decided at runtime

69 Views Asked by At

I'm trying to understand how sizeof() works, so I made 2 arrays, and see what is assembled, I've not used -O3 option because I thought the code to be clearer and the code is not deleted by optimization due to unused stuff. why assembly code to declare arr[n] is so long and what does it mean all that rows of code?

#include<stdio.h>
int square() {
    int n;
    scanf("%d",&n);

    int arr[n];
    int k = sizeof(arr);

    int arr2[4];  
    int k2 = sizeof(arr2);
}

.LC0:
        .string "%d"
square():
        push    rbp
        mov     rbp, rsp
        sub     rsp, 48
        lea     rax, [rbp-28]
        mov     rsi, rax
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    __isoc99_scanf
        mov     ecx, DWORD PTR [rbp-28]
        movsx   rax, ecx
        sub     rax, 1
        mov     QWORD PTR [rbp-8], rax
        movsx   rax, ecx
        lea     rdx, [0+rax*4]
        mov     eax, 16
        sub     rax, 1
        add     rax, rdx
        mov     esi, 16
        mov     edx, 0
        div     rsi
        imul    rax, rax, 16
        sub     rsp, rax
        mov     rax, rsp
        add     rax, 3
        shr     rax, 2
        sal     rax, 2
        mov     QWORD PTR [rbp-16], rax
        movsx   rax, ecx
        sal     eax, 2
        mov     DWORD PTR [rbp-20], eax
        mov     DWORD PTR [rbp-24], 16
        ud2

image with parallel source and asm

1

There are 1 best solutions below

5
On

When you do a simple test code, you have to ensure the both the usefulness of the code & its variables, and then compile with optimization to see what is actually being done without the noise that is naturally generated to speed compilation and to help the debugger be able to locate variables and such.

Turning off optimization is not the right way to ensure the usefulness of code & variables in your test code.

Since you're not using the memory for arr or arr2, nor the value of k, that violates these principles.

A better test code would be:

int something(int *arr);

int square(int n) {
    int arr[n];
    return something(arr);
}

compiled with -O3

    push    rbp
    movsx   rdi, edi
    lea     rax, [15+rdi*4]
    and     rax, -16
    mov     rbp, rsp
    sub     rsp, rax
    mov     rdi, rsp
    call    something(int*)
    leave
    ret

Here we see both the size computation and the dynamic adjustment to the stack to accommodate.  The compiler also uses leave (that relies on the frame pointer) to restore the stack, which it wouldn't necessarily use for a local array whose size was a compile time constant.

In part getting good/useful output is enabled by hiding stuff from the compiler, so it has to assume that the various parameters and variables are required, here by declaring but not defining something(), while also using n, and arr.