我试图通过查看clang生成的程序集来学习x86程序集。例如,我想了解如何将C中的自动数组初始化为所有0。
int64_t my_array [3000] = {0};
看起来组件在堆栈上保留24000B(3000 * 64b / 8B / b),然后调用memset。从memset的手册页中,它的签名如下:
void *
memset(void *b, int c, size_t len);
所以我知道第二个参数应该在%rsi中传递0(我希望每个字节设置为的值),第三个参数(%rdx)是24000美元,但第一个参数(%rdi)怎么样)?生成的程序集中的两条相关指令似乎是:
leaq -24016(%rbp), %rax
movq %rax, %rdi
但是我不明白为什么来自基指针的负24016?为什么要存储在%rax中然后立即移动到%rdi(也许是因为我没有用优化编译)?
无论哪种方式,我都不确定如何将数组的第一个字节的地址传递给memset。我也在OSX上,所以我已经不得不将我的堆栈指针偏移到8B以进行组装。
答案 0 :(得分:2)
你已经在堆栈上分配了my_array
(自动存储),这意味着编译器必须通过本地变量的大小加上保存寄存器的空间来递减堆栈指针(堆栈向低地址增长)。这样。 %ebp
基指针被设置为指向调用者的帧指针(在通过在栈上推送它来保存调用者的基指针之后)。这是正确堆栈展开所必需的约定的一部分。请参阅Agner Fog全面的呼叫约定文档中的第9章异常处理和堆栈展开
http://www.agner.org/optimize/calling_conventions.pdf
由于%ebp
指向调用者的帧,编译器使用它的负偏移指向my_array
的开头,这是被调用函数中的局部变量。
我没有回答为什么编译器将地址存储在%rax
中并立即将其复制到%rdi
,似乎它可以一步完成
leaq -24016(%rbp), %rdi