当我们有一个红色区域时,为什么我们需要堆栈分配?

吉尔伽美什

我有以下疑问:

众所周知,System V x86-64 ABI在堆栈帧中为我们提供了一个固定大小的区域(128字节),即所谓的redzone。因此,因此,我们不需要使用sub rsp, 12只需制造mov [rsp-12], X此而已。

但是我不明白这一点。为什么这有关系?是否需要sub rsp, 12没有redzone?毕竟,堆栈大小在一开始就受到限制,所以为什么如此sub rsp, 12重要呢?我知道这使我们有可能关注堆栈的顶部,但是那一刻让我们忽略它。

我知道有些指令使用rsp值(例如ret),但在那一刻不关心它。

问题的症结在于:我们没有Redzone,我们已经做到了:

function:
    mov [rsp-16], rcx
    mov [rsp-32], rcx
    mov [rsp-128], rcx
    mov [rsp-1024], rcx
    ret

有区别吗?

function:
    sub rsp, 1024
    mov [rsp-16], rcx
    mov [rsp-32], rcx
    mov [rsp-128], rcx
    mov [rsp-1024], rcx
    add rsp, 1024
    ret
科迪·格雷

“红色区域”不是严格必需的。用您的话来说,它可以被认为是“毫无意义的”。使用红色区域可以执行的所有操作,也可以按照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被认为是易变的,随时都可能被覆盖。取而代之的是,它要求调用者在堆栈上分配一个本地地址空间,然后在需要溢出任何寄存器传递的参数的情况下,可供调用者使用。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

当我们已经有了更强大的向量时,为什么还需要堆栈?

ExecutorCompletionService?如果我们有invokeAll,为什么需要一个?

为什么我们需要argc而argv末尾总是有一个null?

当我们给一个名字分配一个函数时,内部会发生什么?

当我们为一个类的对象分配一个整数值时,为什么要调用参数化构造函数?

当我们有组件时,为什么还需要服务?

为什么当我们需要返回值时,我们需要递归的“返回”?

为什么我们需要一个“MultiRepositoryProvider”和一个单独的“MultiBlocProvider”?

当我们使2个对象的哈希码指向一个地址时,为什么它是临时的?

在角度上,当我们试图从定制服务返回一些数据时,为什么需要一个额外的return语句

当我有一个私有id字段时,为什么Hibernate要求我们实现equals / hashcode方法?

为什么当我们只给一个变量赋一个变量并打印时打印所有对象的属性

当我们已经准备好后端时,为什么我们需要Express服务器

为什么我们在Hadoop堆栈中需要ZooKeeper?

为什么我们需要两个接口来枚举一个集合?

当我没有在我的函数中使用它时,为什么我们需要 func.TimerRequest?

当我们只有一个通用特征时,Rust 处理常量数学的方法是什么

当我们只有一个片段时处理 `BackPressed`

当我们有一个列表时,SQL Server 中的 Json 值

当我们在结构内部有一个指针时,container_of宏

当我们导入一个函数时,是否有可能避免多个 ../ ?

当我们有ViewModels时,我们还需要onSaveInstanceState()吗?

当我们在一个接口中添加两个抽象方法并只实现一个方法时,为什么我们不能使用 lambda 实现另一个方法呢?

为什么我们在使用 Apache Kafka 时需要一个数据库?

为什么对于equals方法我们需要一个Object参数?

为什么我们需要在React Hook中返回一个函数?

Java LRUCache,为什么我们需要另一个集合

为什么说我们不需要克隆一个不可变类?

为什么我们需要一个NAT实例?