堆栈帧大小

Stack frame size

提问人:Zuja Plays 提问时间:4/25/2023 最后编辑:Peter CordesZuja Plays 更新时间:4/25/2023 访问量:138

问:

如果我正在查看 C 程序的 objdump,这是我的 int 二项式(int n, int k) 函数的 Mips 细分,如果我分配 40 个字节,那就是堆栈帧的大小/大小,还是取决于代码以及其中使用了多少字节(比如它是 16 个字节,因为我只使用 4 个点)。此外,s0 和 s1 等值将存储在堆栈帧中的什么位置?

我有一个测验即将到来,并试图更好地理解 MIPS 功能的细分,因此任何回复都值得赞赏,因为谷歌更令人困惑而不是有帮助。

00400450 <binomial>:
binomial():
  400450:   27bdffd8    addiu   sp,sp,-40
  400454:   afbf0024    sw  ra,36(sp)
  400458:   afbe0020    sw  s8,32(sp)
  40045c:   afb1001c    sw  s1,28(sp)
  400460:   afb00018    sw  s0,24(sp)
  400464:   03a0f021    move    s8,sp
  400468:   afc40028    sw  a0,40(s8)
  40046c:   afc5002c    sw  a1,44(s8)
  400470:   8fc20028    lw  v0,40(s8)
  400474:   1840000b    blez    v0,4004a4 <binomial+0x54>
  400478:   00000000    nop
  40047c:   8fc2002c    lw  v0,44(s8)
  400480:   04400008    bltz    v0,4004a4 <binomial+0x54>
  400484:   00000000    nop
  400488:   8fc2002c    lw  v0,44(s8)
  40048c:   8fc30028    lw  v1,40(s8)
  400490:   0062102a    slt v0,v1,v0
  400494:   14400003    bnez    v0,4004a4 <binomial+0x54>
  400498:   00000000    nop
  40049c:   0810012c    j   4004b0 <binomial+0x60>
  4004a0:   00000000    nop
  4004a4:   afc00010    sw  zero,16(s8)
  4004a8:   0810014c    j   400530 <binomial+0xe0>
  4004ac:   00000000    nop
  4004b0:   8fc3002c    lw  v1,44(s8)
  4004b4:   8fc20028    lw  v0,40(s8)
  4004b8:   14620005    bne v1,v0,4004d0 <binomial+0x80>
  4004bc:   00000000    nop
  4004c0:   24020001    li  v0,1
  4004c4:   afc20010    sw  v0,16(s8)
  4004c8:   0810014c    j   400530 <binomial+0xe0>
  4004cc:   00000000    nop
  4004d0:   8fc40028    lw  a0,40(s8)
  4004d4:   0c1000e8    jal 4003a0 <fact>
  4004d8:   00000000    nop
  4004dc:   00408821    move    s1,v0
  4004e0:   8fc30028    lw  v1,40(s8)
  4004e4:   8fc2002c    lw  v0,44(s8)
  4004e8:   00621023    subu    v0,v1,v0
  4004ec:   00402021    move    a0,v0
  4004f0:   0c1000e8    jal 4003a0 <fact>
  4004f4:   00000000    nop
  4004f8:   00408021    move    s0,v0
  4004fc:   8fc4002c    lw  a0,44(s8)
  400500:   0c1000e8    jal 4003a0 <fact>
  400504:   00000000    nop
  400508:   02020018    mult    s0,v0
  40050c:   00001012    mflo    v0
    ...
  400518:   0222001a    div zero,s1,v0
  40051c:   14400002    bnez    v0,400528 <binomial+0xd8>
  400520:   00000000    nop
  400524:   0007000d    break   0x7
  400528:   00001012    mflo    v0
  40052c:   afc20010    sw  v0,16(s8)
  400530:   8fc20010    lw  v0,16(s8)
  400534:   03c0e821    move    sp,s8
  400538:   8fbf0024    lw  ra,36(sp)
  40053c:   8fbe0020    lw  s8,32(sp)
  400540:   8fb1001c    lw  s1,28(sp)
  400544:   8fb00018    lw  s0,24(sp)
  400548:   27bd0028    addiu   sp,sp,40
  40054c:   03e00008    jr  ra
  400550:   00000000    nop
C 组件 MIPS 堆栈框架

评论

0赞 Zuja Plays 4/25/2023
此函数是 main 调用的众多函数之一。我想知道这个函数的堆栈框架的大小是多少。
0赞 tadman 4/25/2023
调整应该作为一个指标,在这种情况下,堆栈看起来是向“向下”方向增长的。您是否尝试过在此处绘制堆栈的布局?sp

答:

2赞 Erik Eidt 4/25/2023 #1

堆栈帧将包含

  • 任何基于内存的局部变量,例如数组

  • 必须保存/恢复才能使用的呼叫保留寄存器。

    保存的值属于调用堆栈中更上游的某个调用方

    它们被保存和恢复,以便被叫方可以使用这些寄存器,但必须在返回之前恢复它们。这里是 , , .s8s1s0

  • 函数调用后需要的调用杂乱的寄存器

    这里是 ,它被嵌套函数调用 (to ) 破坏,但需要其值才能返回给调用者 (of)。rafactbinomial

  • 需要在函数调用中存活的临时变量,或者编译器只想映射到内存的临时变量

    这里 16(s8) 是一个变量,它似乎包含最终将成为 v0 中的返回值的内容。

  • 对齐填充,以四舍五入到 8 个字节,从而使堆栈帧与 double 类型的变量很好地对齐,以防某些被调用方将一个变量存储到内存中。

  • 难以解释的随机内容,例如编译器是否希望堆栈大小的 16 位对齐,或者编译器分配了一些未使用的堆栈空间(这不是一个好主意,但也不违法)。

  • 被调用函数参数的空间。


这里的堆栈帧是 40 个字节,也就是 10 个字,它用于存储来自 ra、s8、s1、s0 的值,以及一个(可能是临时的)内存变量,以及被调用方参数的空间(采用一个参数),即使该参数是在 CPU 寄存器中传递的,a0。fact

那么,我数了数,

  • S 变量原始值的 3 个字空间(使调用者受益)
  • RA 的 1 个字空间(使自己受益)
  • 返回值内存变量的 1 字空间(编译器的选择)
  • 参数寄存器的 4 个字影子空间
  • 1 个字的填充,用于 8 字节堆栈对齐

总共10个字。


此功能用作帧指针,在某些教育环境中很流行,但对于此功能来说,这是一个不必要的设备。请注意,帧指针和堆栈指针在整个函数中都保持相同的值,这使得帧指针非常冗余。s8


此外,s0 和 s1 等值将存储在堆栈帧中的什么位置?

在属于被调用方的新分配(10 个字)堆栈空间中的某个位置。

我们还要注意,a0 和 a1 存储在调用方的堆栈空间中,该空间应该包括所有参数的存储空间,即使实际参数值是在寄存器中传递的。这个函数知道空间必须在那里,并在那里存储其参数,而不是为它们分配新的堆栈空间(这也没关系,尽管更浪费堆栈空间)。

评论

1赞 Peter Cordes 4/25/2023
相关:为什么 GCC 在堆栈上分配的空间超出了对齐所需的空间? - 它只是有时会这样做,长期存在的 GCC 错过了优化错误。