在 avr-gcc 网站(https://gcc.gnu.org/wiki/avr-gcc#Frame_Layout)上,它说帧指针(Y 寄存器)偏移了一个字节,因此 Y+1 指向框架的底部。然而,当我将测试 C 函数编译到汇编文件并查看序言时,在我看来,Y 和 SP 似乎指向同一位置,并且与框架没有偏移。
但是在主体中,它确实看起来像使用Y,就好像它刚刚脱离框架一样。
我不确定这个偏移量是从哪里来的。这是编译器为我提供的函数的汇编代码,上面标记了我的问题:
func:
push r29
push r28 <- save Y as it is preserved
rcall . <- this seems to be used to decrement the stack pointer to allocate stack space 2 bytes at a time
rcall .
rcall .
in r28,__SP_L__
in r29,__SP_H__ <- the stack pointer and Y should be aligned and both pointing to last local
/* prologue: function */
/* frame size = 6 */
std Y+4,r24
std Y+6,r23 <- should this not be overwriting the saved r28 register?
std Y+5,r22
ldi r24,lo8(97)
std Y+3,r24
ldi r24,lo8(23)
ldi r25,hi8(23)
std Y+2,r25
std Y+1,r24 <- does seem to use Y as if it is one less than the stack frame. but after the decrements
ldd r24,Y+4
mov r18,r24
clr r19
sbrc r18,7
com r19
ldd r24,Y+5
ldd r25,Y+6
add r18,r24
adc r19,r25
ldd r24,Y+3
clr r25
sbrc r24,7
com r25
add r18,r24
adc r19,r25
ldd r24,Y+1
ldd r25,Y+2
add r24,r18
adc r25,r19
/* epilogue start */
adiw r28,6
in __tmp_reg__,__SREG__
cli
out __SP_H__,r29
out __SREG__,__tmp_reg__
out __SP_L__,r28
pop r28
pop r29
ret
我希望 Y 会有额外的减量,或者主体使用
Y
.. Y+3
而不是 Y+1
.. Y+4
。在我看来,偏移或减少似乎并不明显。我已经检查了指令集手册,但假设我理解它告诉我的内容,似乎没有任何指令以有意义的方式工作。
我知道这可能是一个愚蠢的问题,但我通常不明白这个偏移量来自哪里。我什至问过克劳德,它也听不懂。
在我看来,Y 和 SP 似乎指向同一个位置
是的。 这就是函数序言如何设置帧指针(假设需要 FP)。 这是一个小例子:
void fn_frame (void)
{
int volatile x; // Forces a frame
x = 0;
}
使用
-S -Os
和 avr-gcc v8 生成程序集如下:
fn_frame:
push r28
push r29
rcall .
in r28,__SP_L__
in r29,__SP_H__
/* prologue: function */
/* frame size = 2 */
/* stack size = 4 */
.L__stack_usage = 4
std Y+2,__zero_reg__
std Y+1,__zero_reg__
/* epilogue start */
pop __tmp_reg__
pop __tmp_reg__
pop r29
pop r28
ret
这意味着
x
位于 Y+1
...Y+2
,并且框架的底部是 Y+1
,而不是 Y
。
这是关于avr-gcc中框架如何实现的决定。 虽然我不知道这一设计决定的确切原因,但有一点支持它:
帧指针的初始化只是设置 FP = SP 的两条
IN
指令,以便代码可以访问堆栈帧。 当 FP 设置在框架底部的 SP + 1
处时,设置 FP 将需要针对该测试用例的 3 条指令:
in r28,__SP_L__
in r29,__SP_H__
adiw r28,1
另一方面,地址
Y+1
与Y
一样有效;仅当上述寻址模式用尽时,差异才变得相关 Y+63
。