如果我正在查看 C 程序的 objdump,这是我的 int binomial(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
栈帧将包含
任何基于内存的局部变量,例如数组
必须保存/恢复才能使用的呼叫保留寄存器。
保存的值属于调用堆栈更上层的某个调用者
它们被保存和恢复,以便被调用者可以使用这些寄存器,但必须在返回之前恢复它们。这些是
s8
, s1
, s0
.
函数调用后需要调用破坏的寄存器
这里是
ra
,它被嵌套函数调用(对 fact
)破坏,但需要其值才能返回调用者(binomial
)。
需要在函数调用中存活或编译器只想映射到内存的临时变量
这里的 16(s8) 是一个变量,它似乎保存了最终将成为 v0 中的返回值的内容。
对齐填充,最多四舍五入到 8 个字节,这使堆栈帧很好地对齐双精度类型的变量,以防某些被调用者将一个存储到内存中。
难以解释的随机因素,例如编译器是否需要堆栈大小的 16 位对齐,或者编译器分配了一些未使用的堆栈空间(这不是一个好主意,但也不违法)。
被调用函数的参数空间。
这里的堆栈帧是 40 个字节,也就是 10 个字,它用于存储 ra、s8、s1、s0 的值,以及一个(可能是临时的)内存变量,以及被调用者参数的空间(
fact
一个参数),即使该参数是在 CPU 寄存器中传递的,a0.
我数,那么,
总共10个字。
此功能使用
s8
作为框架指针,这在一些教育设置中很流行,但对于此功能来说是一个不必要的设备。请注意,帧指针和堆栈指针在整个函数中都保持相同的值,这使得帧指针非常冗余。
此外,像 s0 和 s1 这样的值将存储在堆栈帧中的什么位置?
属于被调用者的新分配的(10 个字的)堆栈空间中的某处。
还要注意 a0 和 a1 存储在调用者的堆栈空间中,该空间应该包括所有参数的存储空间,即使实际参数值在寄存器中传递也是如此。这个函数知道空间必须在那里,并且将它的参数存储在那里而不是为它们分配新的堆栈空间(这也可以,尽管更浪费堆栈空间)。