此问题在此处已有答案:
What are the names of the new X86_64 processors registers?(4个答案)
What are the calling conventions for UNIX & Linux system calls (and user-space functions) on i386 and x86-64(4个答案)
What's the best way to remember the x86-64 System V arg register order?(1个答案)
Where is the x86-64 System V ABI documented?(3个答案)
2天前关闭。
由于对this post about UB很感兴趣,我决定开始阅读Jonathan Bartlett's Programming from the Ground Up,以便使用C++ UB,看看程序集是什么样子的。
但是在尝试的过程中,我在一个非常简单的例子中发现了一些奇怪的东西。
int foo(int * p) {
int y = 7;
if (p)
++y;
return y;
}
其装配
foo(int*):
cmpq $1, %rdi
movl $7, %eax
sbbl $-1, %eax
ret
(Compiler Explorer)
现在我明白了movl $7, %eax
将值7
放入eax
寄存器,然后ret
将其返回给调用者。所以我也明白了sbbl $-1, %eax
是负责从eax
的内容中减去-1
并将结果存储到eax
本身的指令,而且这个指令只有在p
不为空时才会发生,这就导致我假设sbbl
正在使用前面几行计算出的隐藏布尔值,唯一的候选值,即使是名字,也是cmpq $1, %rdi
。
但是 that 在做什么呢?从前面提到的书中,我了解到函数的参数是通过堆栈从调用者传递到被调用者的:调用者将参数推入堆栈,被调用者提取这些值。但这里没有这样的事情。
那么%rdi
是什么呢?函数的第一个参数的寄存器(在这个例子中也是唯一的)?为什么会这样呢?还有其他寄存器引用更多的参数吗?有多少?此外,关于这个主题,什么是好的信息来源?
1条答案
按热度按时间yjghlzjz1#
%rdi
是对寄存器rdi
的引用。在这种情况下,编译器似乎是在寄存器中而不是在堆栈上传递第一个参数。
参数传递基本上是一种约定:只要编译器在如何传递参数方面是一致的,编译器就可以在它认为合适的任何时候(编译器的新版本,或者甚至只是在编译器命令行上传递一些开关)从一种方式(例如,总是在堆栈上)传递参数切换到另一种方式(一些在寄存器中)。
取决于您查看的时间和位置,单个编译器支持多个调用约定是很常见的。例如,在相当长的一段时间内,Microsoft的32位编译器支持四个调用约定:
cdecl
、fastcall
、stdcall
和thiscall
(最后一个仅用于C++成员函数)。其中,cdecl
和stdcall
完全基于堆栈,fastcall
和thiscall
都使用寄存器来处理某些参数。