我在理解调用者和被调用者保存的寄存器之间的区别以及何时使用什么方面遇到了一些麻烦.
我使用的是MSP430:
程序:
mov.w #0,R7
mov.w #0,R6
add.w R6,R7
inc.w R6
cmp.w R12,R6
jl l$loop
mov.w R7,R12
ret
Run Code Online (Sandbox Code Playgroud)
上面的代码是被调用者,并且在教科书示例中使用,因此它遵循惯例.R6和R7被呼叫者保存,R12被呼叫者保存.我的理解是被调用者保存的regs不是"全局的",因为在过程中改变它的值不会影响它在程序之外的值.这就是您必须在开头将新值保存到被调用者注册表中的原因.
R12,保存的来电者是"全球性的",因为缺乏更好的词汇.该程序在通话后对R12产生持久影响.
我的理解是否正确?我错过了其他的东西吗?
考虑这个 C 代码:
void foo(void);
long bar(long x) {
foo();
return x;
}
Run Code Online (Sandbox Code Playgroud)
当我在 GCC 9.3 上使用-O3或编译它时-Os,我得到这个:
bar:
push r12
mov r12, rdi
call foo
mov rax, r12
pop r12
ret
Run Code Online (Sandbox Code Playgroud)
除了选择rbx而不是r12作为被调用者保存的寄存器之外,clang 的输出是相同的。
但是,我希望/期望看到看起来更像这样的程序集:
bar:
push rdi
call foo
pop rax
ret
Run Code Online (Sandbox Code Playgroud)
由于无论如何您都必须将某些内容推送到堆栈中,因此将您的值推送到那里似乎更短,更简单,并且可能更快,而不是将一些任意的被调用者保存的寄存器值推送到那里,然后将您的值存储在该寄存器中。call foo当你把东西放回去后,反之亦然。
我的组装错了吗?它在某种程度上比弄乱额外的寄存器效率低吗?如果这两个的答案都是“否”,那么为什么 GCC 或 clang 不这样做呢?
编辑:这是一个不太简单的例子,即使变量被有意义地使用,它也会发生:
long foo(long);
long bar(long x) {
return foo(x * x) - x;
}
Run Code Online (Sandbox Code Playgroud)
我明白了:
bar:
push rbx …Run Code Online (Sandbox Code Playgroud)