8

最新版本的 gcc 正在生成对我来说没有意义的程序集。我没有使用优化编译代码;但是,即使没有优化,这段代码的某些部分也没有意义。

这是C源代码:

  #include <stdio.h>

   int main()
   {
     int a = 1324;
     int b = 5657;
     int difference = 9876;
     int printf_answer = 2221;

     difference = a - b;

     printf_answer = printf("%d + %d = %d\n", a, b, difference);

     return difference;
   }

它产生这个程序集:

    .file   "exampleIML-1b.c"
    .section    .rodata
.LC0:
    .string "%d + %d = %d\n"
    .text
.globl main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    pushq   %rbx
    subq    $24, %rsp
    movl    $1324, -32(%rbp)
    movl    $5657, -28(%rbp)
    movl    $9876, -24(%rbp)
    movl    $2221, -20(%rbp)
    movl    -28(%rbp), %eax
    movl    -32(%rbp), %edx
    movl    %edx, %ecx
    subl    %eax, %ecx
    movl    %ecx, %eax
    movl    %eax, -24(%rbp)
    movl    $.LC0, %eax
    movl    -24(%rbp), %ecx
    movl    -28(%rbp), %edx
    movl    -32(%rbp), %ebx
    .cfi_offset 3, -24
    movl    %ebx, %esi
    movq    %rax, %rdi
    movl    $0, %eax
    call    printf
    movl    %eax, -20(%rbp)
    movl    -24(%rbp), %eax
    addq    $24, %rsp
    popq    %rbx
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (GNU) 4.4.6 20120305 (Red Hat 4.4.6-4)"
    .section    .note.GNU-stack,"",@progbits

有几件事没有意义:

(1) 为什么要推送 %rbx?%rbx 中有什么需要保存?

(2) 为什么我们在减法之前将 %edx 移动到 %ecx ?它不只是做sub %eax, %edx什么?

(3) 同样,为什么在存储值之前从 %ecx 移回 %eax?

(4) 编译器将变量 a 放入内存位置 -32(%rbp)。除非我添加错误,否则 -32(%rbp) 不等于堆栈指针吗?不应该将所有局部变量存储在小于当前堆栈指针的值处吗?

我正在使用这个版本的 gcc:

[eos17:~/Courses/CS451/IntelMachineLanguage]$ gcc -v 使用内置规范。目标:x86_64-redhat-linux 配置:../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http:// bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --with-java- home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share /java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux 线程模型: posix gcc 版本 4.4.6 20120305 (Red Hat 4.

4

2 回答 2

18
GCC dictates how the stack is used. Contract between caller and callee on x86:

    * after call instruction:
          o %eip points at first instruction of function
          o %esp+4 points at first argument
          o %esp points at return address 
    * after ret instruction:
          o %eip contains return address
          o %esp points at arguments pushed by caller
          o called function may have trashed arguments
          o %eax contains return value (or trash if function is void)
          o %ecx, %edx may be trashed
          o %ebp, %ebx, %esi, %edi must contain contents from time of call 
    * Terminology:
          o %eax, %ecx, %edx are "caller save" registers
          o %ebp, %ebx, %esi, %edi are "callee save" registers

main 函数与此上下文中的任何其他函数一样。gcc 决定ebx用于中间计算,因此它保留了它的值。

于 2012-10-04T21:56:37.477 回答
5

默认情况下,gcc 在禁用优化的情况下编译,显然就是这种情况。

您需要使用其中一个优化开关(例如-O2-O3)来启用它。

这样你就不会看到多余的、看似毫无意义的东西了。

至于rbx,它必须被保留,因为这是调用约定所要求的。您的函数会修改它 ( movl -32(%rbp), %ebx),因此必须显式保存和恢复它。

于 2012-10-04T22:02:50.867 回答