最新版本的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)等于堆栈指针?不应该将所有局部变量存储在小于当前堆栈指针的值<?p>
我正在使用此版本的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(红帽4.4.6-4)(GCC)
答案 0 :(得分:12)
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
主要功能与此上下文中的任何其他功能相同。 gcc决定使用ebx
进行中间计算,因此它保留了它的值。
答案 1 :(得分:4)
默认情况下,gcc会在禁用优化的情况下进行编译,显然就是这种情况。
您需要使用其中一个优化开关启用它(例如-O2
或-O3
)。
然后你不会看到多余的,看似毫无意义的事情。
对于rbx
,必须保留它,因为这就是调用约定所需要的。您的函数会修改它(movl -32(%rbp), %ebx
),因此必须明确保存和恢复它。