如何确定是否应保留寄存器
How to determine if a register should be preserved
正在学习MIPS,正在尝试用MIPS表达C语言写的代码
其中,我在研究应该保留的寄存器时,我认为我是单纯从寄存器的类型来决定是否保留的。
int leaf_example (int g, int h, int i , int j)
{
int f;
f = (g+h)-(i+j);
return f;
}
比如有上面的代码,还有条件g
~j
是$a0
~a3
,f
是$s0
,return 值存储在 $v0
.
此时因为条件问题,f
存入$s
需要保留$s
,所以理解为[=17的值=] 存储在堆栈中,然后 $s
稍后恢复。
void sort(int v[],int n)
{
int i, j;
for (i = 0 i < n; i++){
for (j = i - 1; j >= 0 && v[j] > v[j + 1]; j-=1){
swap(v, j);
}
}
}
但是,当有像上面这样的代码时,在MIPS中表示如下:
为什么sort()
函数中作为参数进来的两个值要分开存放,在$s
中没有存放时再使用呢?我不知道如何确定应该保留哪些寄存器。
在上面的代码中,我想知道为什么需要保留$a0
$a1
。也就是说,如果您能告诉我如何区分代码中应保留哪些值,我将不胜感激。
在 C 或伪代码中,我们有生命周期有限的逻辑变量,它们按作用域来来去去(例如局部变量和参数),而在汇编中我们有物理存储。当我们将算法翻译成汇编语言时,我们必须将逻辑变量映射到物理存储中。
为变量选择的特定存储必须满足其生命周期和使用要求。按照软件惯例,MIPS 将寄存器(物理存储)细分为调用保留寄存器和调用破坏寄存器。
我们需要做的是分析变量及其使用方式。您想回答以下问题:对于每个变量,它所持有的值是在函数调用之前定义的,还是在函数调用之后使用的。如果任何变量的答案是肯定的,那么该变量必须存储在一个保留的位置,该位置将在函数调用后继续存在,并且这些位置是 $s
寄存器或(本地)堆栈内存。但无论哪种方式,直接涉及内存,在后一种情况下,在使用 $s
寄存器的前一种情况下,必须保留这些寄存器本身以遵循约定。
如果代码多次使用变量(通过动态计数),就像循环中经常出现的情况,$s
寄存器是有利和可取的。否则,如果变量仅使用一次(或在动态路径上意外或对性能不重要),例如,内存可能是它们比 $s
寄存器更好的位置。
在第一个函数中,为局部变量 f
使用 $s
寄存器会很愚蠢 — f
应该放在 $v0
中。为什么?两个原因:
$v0
可用,并且不会被此函数从第一次使用 f
到最后一次使用时所做的任何事情所破坏,并且,
- 函数以
return f;
结束,也就是说在函数结束时,f
的值需要在$v0
.
所以,把它放在首位是有道理的,并且避免了使用 $s
寄存器的开销,而且不需要在最后复制到 $v0
。
在第二个函数中,它在循环中调用swap
,所以,then
- 假定函数调用会清除所有参数寄存器(它们是调用破坏集的一部分)
sort
本身将消除 $a0
和 $a1
作为进行该函数调用的参数传递部分。
变量i
、j
、v
、n
都是函数调用前定义,函数调用后使用。 i++
既是 i
的一种用法也是另一种定义——该变量应该在函数调用后继续存在。 j--
也一样。 v
和 n
都是在函数入口时定义并重复使用的,因此显然它们也必须在 swap
函数调用后继续存在(否则循环的下一次迭代将不起作用)。
由于这 4 个变量使用得相当频繁,$s
寄存器是很好的候选者,作为替代方案,内存需要在循环体中插入额外的加载和存储。
权衡是在序言和尾声中对 $s
寄存器进行一次保存和恢复,而不是在循环体中进行加载和存储,如果循环执行多次,那就是一笔不错的交易。
当我们将逻辑变量映射到物理存储时,这些变量不一定是永久性的,因此有时逻辑变量在代码的一部分中“存在”(映射到)不同的物理存储。
正在学习MIPS,正在尝试用MIPS表达C语言写的代码
其中,我在研究应该保留的寄存器时,我认为我是单纯从寄存器的类型来决定是否保留的。
int leaf_example (int g, int h, int i , int j)
{
int f;
f = (g+h)-(i+j);
return f;
}
比如有上面的代码,还有条件g
~j
是$a0
~a3
,f
是$s0
,return 值存储在 $v0
.
此时因为条件问题,f
存入$s
需要保留$s
,所以理解为[=17的值=] 存储在堆栈中,然后 $s
稍后恢复。
void sort(int v[],int n)
{
int i, j;
for (i = 0 i < n; i++){
for (j = i - 1; j >= 0 && v[j] > v[j + 1]; j-=1){
swap(v, j);
}
}
}
但是,当有像上面这样的代码时,在MIPS中表示如下:
为什么sort()
函数中作为参数进来的两个值要分开存放,在$s
中没有存放时再使用呢?我不知道如何确定应该保留哪些寄存器。
在上面的代码中,我想知道为什么需要保留$a0
$a1
。也就是说,如果您能告诉我如何区分代码中应保留哪些值,我将不胜感激。
在 C 或伪代码中,我们有生命周期有限的逻辑变量,它们按作用域来来去去(例如局部变量和参数),而在汇编中我们有物理存储。当我们将算法翻译成汇编语言时,我们必须将逻辑变量映射到物理存储中。
为变量选择的特定存储必须满足其生命周期和使用要求。按照软件惯例,MIPS 将寄存器(物理存储)细分为调用保留寄存器和调用破坏寄存器。
我们需要做的是分析变量及其使用方式。您想回答以下问题:对于每个变量,它所持有的值是在函数调用之前定义的,还是在函数调用之后使用的。如果任何变量的答案是肯定的,那么该变量必须存储在一个保留的位置,该位置将在函数调用后继续存在,并且这些位置是 $s
寄存器或(本地)堆栈内存。但无论哪种方式,直接涉及内存,在后一种情况下,在使用 $s
寄存器的前一种情况下,必须保留这些寄存器本身以遵循约定。
如果代码多次使用变量(通过动态计数),就像循环中经常出现的情况,$s
寄存器是有利和可取的。否则,如果变量仅使用一次(或在动态路径上意外或对性能不重要),例如,内存可能是它们比 $s
寄存器更好的位置。
在第一个函数中,为局部变量 f
使用 $s
寄存器会很愚蠢 — f
应该放在 $v0
中。为什么?两个原因:
$v0
可用,并且不会被此函数从第一次使用f
到最后一次使用时所做的任何事情所破坏,并且,- 函数以
return f;
结束,也就是说在函数结束时,f
的值需要在$v0
.
所以,把它放在首位是有道理的,并且避免了使用 $s
寄存器的开销,而且不需要在最后复制到 $v0
。
在第二个函数中,它在循环中调用swap
,所以,then
- 假定函数调用会清除所有参数寄存器(它们是调用破坏集的一部分)
sort
本身将消除$a0
和$a1
作为进行该函数调用的参数传递部分。
变量i
、j
、v
、n
都是函数调用前定义,函数调用后使用。 i++
既是 i
的一种用法也是另一种定义——该变量应该在函数调用后继续存在。 j--
也一样。 v
和 n
都是在函数入口时定义并重复使用的,因此显然它们也必须在 swap
函数调用后继续存在(否则循环的下一次迭代将不起作用)。
由于这 4 个变量使用得相当频繁,$s
寄存器是很好的候选者,作为替代方案,内存需要在循环体中插入额外的加载和存储。
权衡是在序言和尾声中对 $s
寄存器进行一次保存和恢复,而不是在循环体中进行加载和存储,如果循环执行多次,那就是一笔不错的交易。
当我们将逻辑变量映射到物理存储时,这些变量不一定是永久性的,因此有时逻辑变量在代码的一部分中“存在”(映射到)不同的物理存储。