为什么GCC在printf调用之前插入看似不重要的指令?
我试图自己学习x86,我决定剖析一个简单的c程序,看看GCC输出什么。计划如下:为什么GCC在printf调用之前插入看似不重要的指令?,gcc,assembly,x86,Gcc,Assembly,X86,我试图自己学习x86,我决定剖析一个简单的c程序,看看GCC输出什么。计划如下: #include <stdio.h> int main() { printf("%s","Hello World"); return 0; } 现在,除了main下面的前两个之外,上面代码中的几乎所有内容对我来说都是有意义的。虽然这是我不用剥掉东西就能得到的 .LC0: .string "%s" .LC1: .string "Hello World" .text .
#include <stdio.h>
int main() {
printf("%s","Hello World");
return 0;
}
现在,除了main下面的前两个之外,上面代码中的几乎所有内容对我来说都是有意义的。虽然这是我不用剥掉东西就能得到的
.LC0:
.string "%s"
.LC1:
.string "Hello World"
.text
.globl main
.type main, @function
main:
.LFB0:
pushq %rbp # push what was in base pointer onto stack
movq %rsp, %rbp # move stack pointer to base pointer
# prepare arguments for printf
movl $.LC0, %eax # put arg into %eax
movl $.LC1, %esi # put second arg into %esi
movq %rax, %rdi # move value in %rax to %rdi ???? ( why not just put $.LCO into %rax directly )
movl $0, %eax # clear out %eax ???? ( why do we need to clear it out )
call printf
movl $0, %eax # return 0
leave
ret
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
.section .note.GNU-stack,"",@progbits
我已经标记了两个说明????我不明白
第一条指令是将%rax中的内容移动到%rdi中,以准备printf调用。这一切都很好,只是我们刚刚将$.LC0(字符串“%s”)移动到了%eax中。这似乎没有必要,为什么我们不先将$.LC0移动到%rdi,而不是将其移动到%eax,然后再移动到%rdi
第二条指令是清除%eax,我理解它是函数的返回值。但是,如果函数将以任何方式对其进行重击,那么GCC为什么要清除它呢?您是在查看优化的输出,还是未优化的输出(这基本上是将C代码转换为汇编程序的简单过程)?这会产生巨大的差异,因为优化器通常很擅长应用与您描述的相同类型的规则。您是在查看优化的输出,还是未优化的输出(这基本上是将C代码转换为汇编代码的简单过程)?这会产生巨大的差异,因为优化器通常非常擅长应用与您描述的相同类型的规则。因为GCC是一个编译器,而编译器是哑的
您可以使用-O2使GCC更智能。它开始使用优化技巧并减少冗余指令。因为GCC是一个编译器,而编译器是哑的
您可以使用-O2使GCC更智能。它开始使用优化技巧并减少冗余指令。一些经验法则:
如果您真的想挤出性能的最后一点,请在代码块前后使用rdtsc指令来测量花费的时钟数。要小心一点,因为rdtsc并没有严格按照周围的指令进行订购,但实际上测量它对于1000时钟范围内的任何东西都非常准确。一些经验法则:
%rax
中的内容移动到%rdi
中,以准备printf
调用。除了我们刚刚将$.LC0
(即字符串%s“
)移动到%eax
)之外,一切都很好。这似乎没有必要,为什么我们不先将$.LC0
移动到%rdi
中,而不是将其移动到%eax
中,然后再移动到%rdi
这可能是因为编译时没有优化。当我在Mac OS X v10.6.8上使用GCC 4.2.1编译您的示例时,我得到以下输出:
.globl _main
_main:
LFB3:
pushq %rbp
LCFI0:
movq %rsp, %rbp
LCFI1:
leaq LC0(%rip), %rsi
leaq LC1(%rip), %rdi
movl $0, %eax
call _printf
movl $0, %eax
leave
ret
如您所见,参数直接存储在%rsi
和%rdi
中
第二条指令是清除%eax
,我理解它是函数的返回值。但是,如果函数将以任何方式对其进行重击,那么GCC为什么要清除它呢
因为x86_64 ABI指定,如果函数采用变量参数,则AL
(属于%eax
)将保存用于该函数调用参数的向量寄存器的数量。因为调用printf()
时没有指定浮点参数,所以没有使用向量寄存器,所以.globl _main
_main:
LFB3:
pushq %rbp
LCFI0:
movq %rsp, %rbp
LCFI1:
leaq LC0(%rip), %rsi
leaq LC1(%rip), %rdi
movl $0, %eax
call _printf
movl $0, %eax
leave
ret