首页 > 解决方案 > C 到 MIPS 汇编 while 循环

问题描述

C语言

while (save[i] == k) 
i += 1;

i→ $s3

k → $s5

base addr of save → $s6
        
          sll $t1, $s3, 2        #$t1 = 4*$s3
          add $t1, $t1, $s6      #$t1 = address of save[i]
          lw $t0, 0($t1)         #t0 = save[i]
          bne $t0, $s5, Exit     #$t0!=$s5 Exit
    Loop: addi $s3, $s3, 1       #i+=1
          addi $t1, $t1, 4       #point to next cell
          lw $t0, 0($t1)         #t0 = save[i]
          beq $t0, $s5, Loop     #$t0==$s5 loop again
    Exit:

我想问的是,是否需要addi $s3, $s3, 1行?

因为,无论如何 $ t1 增加了 4。

标签: cassemblywhile-loopmips

解决方案


C 代码和汇编代码实际上并不匹配。汇编代码已被优化为使用指针而不是数组引用,并且循环的退出条件也已在循环外复制一次,从而允许循环结构使用更有效的 do while 循环(在迭代期间更有效,因为它与 while 循环的直接代码相比,在末尾保存了一个无条件分支)。

请注意,这些优化不仅在汇编中是可能的,而且在 C 中也是可行的。

程序集的 C 版本如下:

int *sp = save + i; // in C this addition is automatically scaled by 4
if ( *sp == k ) {
    do {
        i++;
        sp++;   // this C +1 is scaled so really is +4
    } while ( *sp == k );
}

当然,这在逻辑上等价于另一个。

你的问题是:我们需要i++;吗?

一个答案是:不,不在循环中。您是正确的,循环不使用i并且会访问数组的相同元素,并在与原始的更简单的 C 循环相同的点退出。

另一个答案是:是的,如果i在循环之后使用!  这个循环做的很少,但会检查一些内存。我们不得不问:循环的目的是什么,如果不是为i循环后面的一些代码设置?在这里,循环的目的可能是计算重复项,这将导致i成为循环的“输出”/期望结果:在结束时包含重复项的计数,然后运行下一条语句。

这就是为什么很难对小段代码进行推理的原因之一——本质上我们无法判断接下来会发生什么以及是否i会被使用。但是如果i不使用,那么循环就没有价值了..


如果我们要尝试将其放入编译器中,我们将被迫将代码段更改为完整的函数。编译器不会接受小于函数的代码语句。

完整的函数定义是您可以提供给编译器的最小代码段(函数定义之外的代码片段在 C 语言中不是合法的语句),并且包装使代码片段的环境变得清晰:

  • 参数是给定的输入
  • 局部变量是临时的并且超出范围
  • 返回值是预期的输出。

(当然也有副作用,例如,排序比函数更持久的东西,或者创建比函数更持久的堆对象。)

包装在一个函数中,然后我们可以确定地观察i在循环之后是否使用了局部变量。


当然,还有另一种方法可以i在循环之后计算 ' 的值,而无需在循环期间增加它。在循环之后,以下语句将恢复i应具有的值:

i = sp - save;  // NB: C pointer subtraction automatically descales

在汇编中,这将是减法,然后右移 2 以从字节差值到整数索引值。

(请注意,这些优化和转换很容易被淘汰,因此请检查工作。)


推荐阅读