如何在 MIPS 32 的 for 循环中使用 set if less than (SLT) 语句?

How to use the set if less than (SLT) statement in a for-loop in MIPS 32?

我正在做一些家庭作业,我的问题是我应该在此 for 循环程序集中包含 SLT(如果小于则设置)行,但我不确定该怎么做。我发现 this question 有帮助,但我不能使用 BLT (branch if less than) 语句,教授确实介绍了宏,但这个问题指定使用 SLT。我最终只是在我的作业陈述中使用了它,但我觉得这几乎不能满足教授的要求,这里是陈述:slt $t2, $t0, $t1 #t2 holds y = 1 if i < x。它根本不适合代码,它没有任何用途我觉得它应该与 for 循环有关,有什么建议吗?

这是程序集代表的基本 for 循环:

for (i=0; i<x; i++) 
   y = y * 2; 

这是 MIPS 32 中的汇编代码:

.text
.globl main
main:
    li $t1, 7          #test val num of iterations, x = 7
    li $t0, 0          #t0 is our counter i = 0
    #li $t2, 1         #another way to assign y = 1
    slt $t2, $t0, $t1  #t2 holds y = 1 if i < x
    
loop:
    beq $t0, $t1, end  #if (i)t0 == (x)7 end loop
    add $t2, $t2, $t2  #body, y = y * 2
    addi $t0, $t0, 1   #add 1 to i, t0
    j loop             #jump back to the top
end:
    li $v0, 10
    syscall

你是对的,slt 不用于循环控制的目的,因为它不参与循环。

条件 i<x 是一个动态条件,作为循环迭代的一部分,因为 i 每次迭代都会改变。

如果您单步调试它,您可能已经注意到了这个缺陷。

但一个问题是您没有使用出色的伪代码,因为 y 从未被初始化。我们仍然可以使用它,但请注意,您已选择以某种不属于给定伪代码的方式初始化 y

您用汇编语言编写的内容具有等效的伪代码:

y = i < x;              // bad: outside the loop and modifying y
for (i=0; i!=x; i++)    // bad: using condition i!=x
    y = y + y;          // good: valid for y = y * 2;

你能看出这个伪代码和原来的不一样的几个地方吗? y 被初始化为原始值中没有的东西,并且循环退出条件 i!=x 与原始值不同。此伪代码在逻辑上不等价,不会 运行 与原始代码相同。


让我们从更基本的原则和逻辑转换出发,这样您就可以看到一些问题:

for (i=0; i<x; i++) 
   y = y * 2; 

我们首先将这个 for 循环转换为等效的 while 循环,因为 while 循环更明确:

i = 0;
while ( i < x ) {
    y = y * 2;
    i++;
}

接下来,我们再次将此 while 循环转换为汇编语言的 if-goto-label 样式,因为这更加明确,并且每一步都使我们更接近汇编语言。您将真正开始了解循环中的内容和不在循环中的内容,即使这仍然是伪代码(仅供参考,C 中允许使用此 if-goto-label)。

    i = 0;
loop1:
    if ( ! (i < x) ) goto loop1Done;  // Exit loop when i < x is false
    y = y * 2;
    i++;
    goto loop1;
loop1Done:

首先要注意的是,在汇编语言的 if-goto-label 风格中,如果我们希望程序做其他事情,我们使用 if-goto。希望您能看到,作为 if-goto 语义的结果,自然操作是告诉处理器何时退出循环——而不是像 C 中所做的那样何时留在循环中——因此对条件(MIPS 可以通过多种方式处理)。

接下来,我们可以清楚地看到循环内部需要关系运算i < x(及其否定),因此必须在循环的每次迭代中重复。

操作 if ( ! (i < x) ) goto ... 是合法的 C 代码,但可以在伪代码中重述为:if ( i < x is false ) goto ....

slt可以按照你在$t0中用操作数i$t1中的x描述的那样使用,所以那部分很好,但是结果需要转到一个新的临时寄存器,而不是破坏原始值 y(原始伪代码中未指定)。让我们想象一下使用 $t10 作为这个临时替代方案。因此,slt $t10, $t0, $t1,并注意它 在循环中的位置

现在测试条件测试的条件分支部分。我们要测试 i<x 比较的结果是否为假,如果是则分支退出循环。这样的分支需要测试 $t10,因为它保存了 slti<x 的结果,并且为了测试这个布尔值是否为假,我们将使用 beq $t10 [=36=] loop1Done,它表示如果$t10 等于 false([=38=] 始终为 false)然后执行退出循环的 goto/branch。

作为 reader 的练习,完成从 if-goto-label pseudo/C 代码到汇编代码的逻辑转换。


还要从上面的讨论中注意到,您的原始伪代码并不是独立存在的:y 的初始化是必要的,因此为了实际 运行 这个 MIPS 转换代码,您需要将不得不添加这样的初始化(如果 y 的寄存器恰好是 0,因为模拟器会初始化它,那么循环将是最没有意义的,因为 y = 0 * 2 不会重复做任何不同的事情)。