何时以MIPS汇编语言使用临时和已保存的寄存器?

时间:2018-05-13 19:03:10

标签: assembly mips

在这个问题中:

x = x + y - 10 - A [20]

x - $s0
y - $s1
A - $s3

对于我的回答,我写道:

add   $t0,   $s0,   $s1    # value of x ($s0) + y ($s1) gets stored in temp $t0
addi  $t0,   $t0,  - 10    # subtracts value of $t0 from 10. $t0 now holds the new value
lw    $t1,   80($s3)       # loads value of A[20] into new temp $t1
sub   $s0,   $t0,  $t1     # subtracts values $t0 from $t1 and stores it in x 
                           ($s0)

然而,模型解决方案说:

add   $s0,   $s0,   $s1   
addi  $s0,   $s0,  - 10    
lw    $t0,   80($s3)   
sub   $s0,   $s0,  $t0

我的原始答案是否正确?它输出的结果是否相同?如果我错了,请解释原因。

2 个答案:

答案 0 :(得分:3)

  

何时以MIPS汇编语言使用临时和已保存的寄存器?

Peter Cordes的补充答案。

MIPS程序集的公共调用约定要求您保留“已保存”寄存器中的值,并允许您自由修改“临时”寄存器,这些寄存器在从代码调用子例程时生效。

由于临时寄存器可以由子程序自由修改,如果需要该值,则必须在子程序调用周围保留/恢复它们,因此临时寄存器通常用于子程序调用之间,用于可能具有有限寿命的值

“保存”寄存器必须由代码保留,以便不为上面的调用者修改它们,即无论何时使用其他新保存的寄存器,都应将其原始值放在某处(通常放入堆栈内存中)然后恢复它在返回呼叫者之前。这会带来轻微的性能损失,因此您可能希望在代码中完全避免使用“已保存”的寄存器,除非您调用多个子例程,而是通过在保存的寄存器中使用具有更长生命周期的值来获得性能,而不必保留/在每次通话时恢复它们(如果它们在临时注册中)。

所以经验法则是:

  1. 根本不使用寄存器/值(通过更聪明的算法或代码结构消除,更好地重用已经受影响的寄存器)
  2. 对生命周期有限的值使用临时值,特别是如果它不与子例程调用冲突
  3. 使用已保存的寄存器获取在子程序调用期间必须保持的值(尤其是几个子程序调用)
  4. (这是“调用CONVENTION”定义的,即你可以定义+使用你自己的约定来破坏临时/保存的寄存器使用的规则..这不是在CPU内设计的东西)

答案 1 :(得分:2)

两个答案都是正确的;有许多方法来编译该表达式,使用您想要的任何寄存器,以及不同的操作顺序或不同的指令选择。

将结果写入指令作为输入读取的寄存器没有任何缺点。如果有更多的周围代码作为大型函数的一部分,那么使用较少的临时寄存器可能是一个优势。如果您将寄存器分配压力视为大函数的一部分,那么模型解决方案可能比您的更优化,反复修改$s0而不是使用更多临时值。

但由于没有周围的代码,因此没有理由说你的代码有任何问题。将原来的x值更长一段时间也许有用。

重新排列较短的关键路径延迟,并在超标量CPU上重新设置instruction-level parallelism 。 (例如MIPS r10k是4-wide superscalar with乱序执行)

需要三个添加/子操作,并且您的问题中的两个版本都具有贯穿所有三个ALU操作的串行依赖性。

2&#39的补码算术是关联的。但MIPS add在签名溢出时出错,因此您创建的临时结果确实很重要(就像带有舍入错误的FP一样)。但MIPS也有addu包裹而不是错误,所以如果您不关心在签名溢出上引发异常,请使用addu / addiu / subu然后,您可以将操作重新排序为(x - A[20]) + (y - 10),例如。

lw    $t0,   80($s3)       # load as early as possible
addiu $t1,   $s1,  -10     # y-10 in the shadow of the load delay slot
subu  $s0,   $s0,  $t0     # x-A[20]
addu  $s0,   $s0,  $t1

请注意,x$s0)在第3条指令之前不必准备就绪,因此我们可以隐藏x输入的延迟。如果您知道操作数的来源是什么,请订购您的操作,以便最后需要最后准备好的操作。 (特别是如果您正在调整有序CPU,与r10k不同)

当然,如果你期望一些加载延迟,那么在使用加载结果之前完成所有ALU工作会更有意义,即使这意味着通过使它们相互依赖来序列化所有三个add / sub操作。