Assembly 为什么要分解“printf”(“f”,6.4)`这么复杂?
第一个浮点参数被赋予xmm0、xmm1。。。在x86-64 ABI中。 没有直接将立即数(6.4的浮点表示)移动到xmm寄存器的指令,因此它首先放在堆栈上,然后移动到xmm0中。当然,另一个参数是格式字符串的地址Assembly 为什么要分解“printf”(“f”,6.4)`这么复杂?,assembly,floating-point,Assembly,Floating Point,第一个浮点参数被赋予xmm0、xmm1。。。在x86-64 ABI中。 没有直接将立即数(6.4的浮点表示)移动到xmm寄存器的指令,因此它首先放在堆栈上,然后移动到xmm0中。当然,另一个参数是格式字符串的地址 $1和1之间的差值第一个用作立即常数,第二个用作地址偏移量或地址。第一个浮点参数被赋予xmm0、xmm1。。。在x86-64 ABI中。 没有直接将立即数(6.4的浮点表示)移动到xmm寄存器的指令,因此它首先放在堆栈上,然后移动到xmm0中。当然,另一个参数是格式字符串的地址 $1和
$1和1之间的差值第一个用作立即常数,第二个用作地址偏移量或地址。第一个浮点参数被赋予xmm0、xmm1。。。在x86-64 ABI中。 没有直接将立即数(6.4的浮点表示)移动到xmm寄存器的指令,因此它首先放在堆栈上,然后移动到xmm0中。当然,另一个参数是格式字符串的地址
$1和1之间的差值是第一个用作立即常数,第二个用作地址偏移量或地址。因为编译时没有进行任何优化。尝试
-Os
:
(gdb) p 0x10
$4 = 16
(gdb) p $0x10
$5 = void
这有什么复杂的呢?因为您在编译时没有进行任何优化。尝试
-Os
:
(gdb) p 0x10
$4 = 16
(gdb) p $0x10
$5 = void
这有什么复杂的呢?因为如果简单的话,你就不会问,问题也不会结束。哪里是“复杂的”?这只是一些说明。顺便说一句,0x40199999999a是6.4文本的十六进制值。@Paul R,为什么浮点首先移动到rax,然后是堆栈,最后是目标xmm0?谁知道呢?这取决于您使用的编译器、ABI和优化级别。对我来说,在MacOSX上使用
gcc-O3
我只得到4条指令:movsd-LC0(%rip),%xmm0;leaq LC1(%rip),%rdi;movl$1,%eax;调用_printf
我使用的是gcc,在编译时不使用任何优化标志(gcc-Wall-g
),也不知道它使用的是哪种ABI。因为如果它很简单,你就不会问,问题也不会结束。它“复杂”在哪里?这只是一些说明。顺便说一句,0x40199999999a是6.4文本的十六进制值。@Paul R,为什么浮点首先移动到rax,然后是堆栈,最后是目标xmm0?谁知道呢?这取决于您使用的编译器、ABI和优化级别。对我来说,在MacOSX上使用gcc-O3
我只得到4条指令:movsd-LC0(%rip),%xmm0;leaq LC1(%rip),%rdi;movl$1,%eax;调用_printf
我正在使用gcc,在编译时不使用任何优化标志(gcc-Wall-g
),也不知道它使用的是哪一个ABI。为什么必须首先将它放在堆栈上?它不能直接从寄存器rax
移动到xmm0
?因为printf
使用的参数数量可变(使用…
)语法。通过在堆栈上传递它们,printf
函数可以很容易地检索它们。有些体系结构在寄存器中传递此类参数,但这里不是这样。@Lindydancer,printf
不是最终从xmm0
获取浮点参数吗?可能,我不知道这一点r调用约定。如果是,代码序列看起来奇怪地长。可能它是保守的,并将其存储在堆栈和寄存器中,以便printf
可以在有或没有FPU支持的情况下实现(尽管有点牵强)。直接从rax移动到xmm0是可能的,但您需要一些优化标志。它需要特殊的SSE指令(由于x86_64,可能会启用)编译器需要知道应该针对哪种体系结构进行优化—例如,在P4上,对内存的读写比从通用寄存器移动到sse寄存器要快。为什么必须首先将它放在堆栈上?它不能直接从寄存器rax
移动到xmm0
?因为printf
需要变量数量的参数(使用…
)语法。通过在堆栈上传递它们,printf
函数可以很容易地检索它们。有些体系结构在寄存器中传递此类参数,但这里不是这样。@Lindydancer,printf
不是最终从xmm0
获取浮点参数吗?可能,我不知道这一点r调用约定。如果是,代码序列看起来奇怪地长。可能它是保守的,并将其存储在堆栈和寄存器中,以便printf
可以在有或没有FPU支持的情况下实现(尽管有点牵强)。直接从rax移动到xmm0是可能的,但您需要一些优化标志。它需要特殊的SSE指令(由于x86_64,可能会启用)对于编译器来说,需要知道应该针对哪种体系结构进行优化——例如,在P4上,对内存的读写比从通用寄存器到sse寄存器的移动要快
pushq %rbp // save old frame pointer
movq %rsp, %rbp // establish new frame pointer
leaq 0x0000004d(%rip), %rdi // load address of format string ("%f")
movsd 0x0000003d(%rip), %xmm0 // load 6.4
movb $0x01, %al // load number of VA_ARGS (1)
callq _printf // call printf
xorl %eax, %eax // conjure return value (0)
popq %rbp // restore frame pointer
ret // return