c - 为什么 GCC 会产生以下 asm 输出?

标签 c gcc assembly compilation x86

我不明白为什么 gcc -S -m32 会产生这些特定的代码行:

movl    %eax, 28(%esp)
movl    $desc, 4(%esp)
movl    28(%esp), %eax
movl    %eax, (%esp)
call    sort_gen_asm

我的问题是为什么 %eax 被压入然后弹出?为什么分别使用 movl 而不是 pushlpopl?它更快吗?是否有一些我还不知道的编码约定?我刚刚开始仔细研究 asm-output,所以我了解的不多。

C 代码:

void print_array(int *data, size_t sz);
void sort_gen_asm(array_t*, comparer_t);

int main(int argc, char *argv[]) {
    FILE *file;
    array_t *array;

    file = fopen("test", "rb");
    if (file == NULL) { 
        err(EXIT_FAILURE, NULL);
    }   

    array = array_get(file);
    sort_gen_asm(array, desc); 
    print_array(array->data, array->sz);

    array_destroy(array);
    fclose(file); 

    return 0;
}

它给出了这个输出:

    .file   "main.c"
    .section    .rodata
.LC0:
    .string "rb"
.LC1:
    .string "test"
    .text
    .globl  main
    .type   main, @function
main:
.LFB2:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    movl    $.LC0, 4(%esp)
    movl    $.LC1, (%esp)
    call    fopen
    movl    %eax, 24(%esp)
    cmpl    $0, 24(%esp)
    jne .L2
    movl    $0, 4(%esp)
    movl    $1, (%esp)
    call    err
.L2:
    movl    24(%esp), %eax
    movl    %eax, (%esp)
    call    array_get
    movl    %eax, 28(%esp)
    movl    $desc, 4(%esp)
    movl    28(%esp), %eax
    movl    %eax, (%esp)
    call    sort_gen_asm
    movl    28(%esp), %eax
    movl    4(%eax), %edx
    movl    28(%esp), %eax
    movl    (%eax), %eax
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    print_array
    movl    28(%esp), %eax
    movl    %eax, (%esp)
    call    array_destroy
    movl    24(%esp), %eax
    movl    %eax, (%esp)
    call    fclose
    movl    $0, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE2:
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.8.1-10ubuntu8) 4.8.1"
    .section    .note.GNU-stack,"",@progbits

最佳答案

eax 的保存/加载是因为您没有进行优化编译。因此,对变量的任何读/写都会发出内存地址的读/写。

实际上,对于(几乎)任何一行代码,您都能够识别出由它产生的确切的汇编代码片段(我建议您使用 gcc -g -c -O0 进行编译然后 objdump -S file.o):

#array = array_get(file); 
call array_get
movl    %eax, 28(%esp) #write array

#sort_gen_asm(array, desc); 
movl    28(%esp), %eax  #read array
movl    %eax, (%esp)
...

关于不推送/弹出,是标准的零成本优化。每次你想调用一个函数时,你都不需要推送/弹出,你只需在函数的开头减去最大需要的空间到 esp ,然后将你的函数参数保存在空白空间的底部。有很多优点:更快的代码(无需更改 esp),不需要以任何特定顺序计算参数,并且 esp 需要无论如何减去局部变量空间。

关于c - 为什么 GCC 会产生以下 asm 输出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19835076/

相关文章:

macos - 如何在没有管理员访问权限或使用 Xcode 的情况下在 Mac 上安装 GCC 和 make?

c++ - 内联调用always_inline '__m256d _mm256_broadcast_sd(const double*)'失败

assembly - 当 x86-64 linux 内核需要运行 x86-32 程序时,它做了什么?

C: exec到我自己的exe文件

名称正确,但是 "dereferencing pointer to incomplete type"

c - 编译这个程序如何填满我的电脑?

linux - 错误:general protection fault fffc

go - 将Go汇编程序翻译为NASM

c - 在 C 中使用 va_arg() 时如何避免运行时错误

mysql - 有问题 if 和 while ((row = mysql_fetch_row(query_results)) !=0)