“红色区域”并不是绝对必要的。用你的话来说,它可以被认为是“毫无意义的”。您可以使用红色区域执行的所有操作,也可以执行针对 IA-32 ABI 的传统方式。
以下是AMD64 ABI关于“红色区域”的说明:
超出指向的位置的 128 字节区域%rsp
被认为是保留的,不应被信号或中断处理程序修改。因此,函数可以将此区域用于函数调用之间不需要的临时数据。特别是,叶函数可以将这个区域用于它们的整个堆栈帧,而不是在序言和尾声中调整堆栈指针。这个区域被称为红色区域。
红色区域的真正目的是作为优化。它的存在允许代码假设下面的 128 字节rsp
不会被信号或中断处理程序异步破坏,这使得可以将其用作暂存空间。这使得无需通过将堆栈指针移入rsp
. rsp
这是一种优化,因为现在可以省略递减和恢复指令,从而节省时间和空间。
所以是的,虽然你可以用 AMD64 做到这一点(并且需要用 IA-32 做到这一点):
function:
push rbp ; standard "prologue" to save the
mov rbp, rsp ; original value of rsp
sub rsp, 32 ; reserve scratch area on stack
mov QWORD PTR [rsp], rcx ; copy rcx into our scratch area
mov QWORD PTR [rsp+8], rdx ; copy rdx into our scratch area
; ...do something that clobbers rcx and rdx...
mov rcx, [rsp] ; retrieve original value of rcx from our scratch area
mov rdx, [rsp+8] ; retrieve original value of rdx from our scratch area
add rsp, 32 ; give back the stack space we used as scratch area
pop rbp ; standard "epilogue" to restore rsp
ret
在只需要 128 字节暂存区(或更小)的情况下,我们不需要这样做,因为这样我们就可以将红色区域用作暂存区。
另外,由于我们不再需要递减堆栈指针,我们可以将rsp
其用作基指针(而不是rbp
),从而无需保存和恢复rbp
(在序言和尾声中),并且还可以腾出空间rbp
用作另一个通用指针——目的登记!
(从技术上讲,打开帧指针省略(-fomit-frame-pointer
,默认情况下启用,-O1
因为 ABI 允许它)也可以使编译器省略序言和尾声部分,具有相同的好处。但是,如果没有红色区域,需要调整堆栈指针以保留空间不会改变。)
但是请注意,ABI 仅保证诸如信号和中断处理程序之类的异步事物不会修改红色区域。对其他函数的调用可能会破坏红色区域中的值,因此它不是特别有用,除了叶函数(那些不调用任何其他函数的函数,就好像它们位于函数调用树的“叶”) .
最后一点:Windows x64 ABI 与其他操作系统上使用的 AMD64 ABI 略有不同。特别是,它没有“红区”的概念。超出的区域rsp
被认为是不稳定的,随时可能被覆盖。相反,它要求调用者在堆栈上分配一个本地地址空间,然后在它需要溢出任何寄存器传递的参数的情况下,它可供被调用者使用。