如何在程序集循环后转移控制权?

How do I transfer control after a loop in assembly?

我想将下面的 C 代码翻译成汇编语言。

但是,我没有看到我需要在这个例子中使用堆栈。

此外,我想知道 "beq" 是否像 "jal" 那样在 $ra 中保存了以下指令的地址,因为当循环结束时,我想得到回到原来的函数foo,继续指令(这里只是返回)

     int foo(int* a, int N) {
         if(N > 0) 
             {
             for(int i = 0; i != N; i = i + 1) 
                { 
                a[i] = bar(i << 4, a[i]);
                } 
             }
     return N & 7; 
     }
#assume *a in $a0, N $N in $a1
foo:
slt $t0, $zero, $a1 #put 1 in $t0 if 0 < N
li $t1,0 # use $t1 as loop counter
beq $t0, 1, loop  # enter loop if 0 < N
and $v0, $a1, 7 # do bitwise and on N and 7 and save in $v0 as return value

loop:
beq $t1, $a1, exit # exit loop when i = N
sll $t3, $t1, 2 # obtain 4 * i
add $t3, $a1, $t3 # obtain address of a[i] which is address of a plus 4i
lw $t3, o($t3)  # load a[i] into $t3
sll $t4, $t1, 4 #perform  i<< 4 and save in $t4
# the 2 previous load arguments for bar
jal bar # assume bar saves return value in $v2
sw $t3, 0($v1)
j loop

exit:
and $v0, $a1, 7

beq 用于条件分支,而不是调用——它更改 PC(有条件地)但不更改 $ra。我们用它来将结构化语句(例如 if、for)翻译成汇编语言的 if-goto 风格。

However, I do not see that I need to use the stack in this example.

必须 为这段代码使用堆栈,因为对 bar 的调用(如 jal bar) 将消灭 foos $ra,而 bar 将能够 return 回到 foo , foo 将无法 return 给它的调用者。由于这需要一个堆栈,因此您需要 prologue and epilogue 来分配和释放一些堆栈 space。

您的代码没有正确地向 bari << 4 传递参数,例如,应该在 $a0 中传递参数,而 a[i] 应该在 $a1.

您在 foo 中没有 return 指令 — 它缺少 jr $ra

如果您的任何一个 beq 指令确实设置了 $ra,这些将不会对 return 返回有用的点。 但是既然你问了:

I'd like to know whether or not "beq" saves the address of the following instruction in $ra like "jal" does

如果指令助记符不是以al结尾(表示AndLink),则不会在$ra中保存一个return地址。

Classic MIPS 有以下指令 link,来自这个 somewhat incomplete reference(缺少 nor 和 IDK 还有什么)。

  • jal target(跳转Link)
  • BGEZAL $reg, target(条件分支 if >= 0 And Link)
  • BLTZAL $reg, target(条件分支 if < 0 And Link)

请注意,条件分支实际上是在寄存器的符号位上分支。

balbgezal $zero, target 的别名,可用于执行与 PC 相关的函数调用。 (MIPS 分支对分支位移使用完全相对编码,MIPS 跳转使用区域绝对编码替换 PC+4 的低 28 位。这对位置无关代码很重要)。


None 其中与您的情况特别相关;您的 foo 需要在 entry/before jr $ra 上 save/restore $ra 因为您需要使用 jal 或 [=18 调用 bar =].使用 linking 分支作为循环分支不会有任何影响(除了使您的代码效率更低,并使使用特殊预测器进行 return-地址预测的真实 CPU 上的性能更差假设 jaljr $ra 正确配对)。

使用 bal / jal 不会自动让你跳转到任何地方 return;只有当目标曾经使用 jr $ra 时才会发生(可能在将 $ra 复制到其他地方然后恢复它之后)。