将以下高级代码翻译成 MIPS 汇编代码
Translating the following high level code into MIPS Assembly code
我已经把这个C程序翻译成了MIPS汇编代码。
所以,在这里我想知道:
- 我是不是做错了什么?
- 如果我的汇编代码是正确的,我可以通过减少
指令数。我在这里使用了14条指令
这是我转换这个 C 程序的方法
这是我的 MIPS 汇编代码
#s0 = res
main:
addi $a0 , [=10=] , 27
addi $a1 , [=10=] , 3
jal division # call function
add $s0 , $v0 , [=10=] # res = returned value
division:
addi $t0 , [=10=] , 0 # $t0 = 0
addi $s0 , [=10=] , 0 # $s0 = val and val = 0
add $s1 , [=10=] , $a0 # $s1 = i and i = 27
loop:
slt $t1 , $s1 , $t0 # checking if i>0
bne $t1 , [=10=] , break # if $t1 = 0 then break
addi $s0 , $s0 , 1 # else val = val + 1
sub $s1 , $s1 , $a1 # i = i - y
add $v0 , $s0 , [=10=] # put return value in $v0
j loop
break:
jr $ra # return to caller
这是高级 C 程序
int main(){
int res;
res = division (27, 3);
}
int division(int x, int y)
{
int i;
int val = 0;
for (i = x; i>0; i = i-y){
val = val + 1;
}
return val;
}
错误:
关系被错误地否定了。您已将其翻译为
for ( i = x; i >= 0; i -= y )
所以,它会比你想要的多循环一次(当整数除法是精确的时候)。您知道循环条件测试需要对 if-goto-label 样式的程序集取反:
if ( ! (i > 0) ) goto break;
然而 ! (i > 0)
是 i <= 0
你有 i < 0
.
你没有在main
结束的时候正确终止程序,所以会不小心掉到divide子程序中。解决方案是向 main
.
添加一个退出系统调用
建议:
寄存器使用过多,使用较少的寄存器也会减少所需的指令数。此外,使用 $s
寄存器而不保留其原始值违反了调用约定。但是,最好的解决方案是简单地避免在该函数中使用它们——直接使用 $a0
和 $a1
而不是将它们复制到其他地方。
(可以在 main
中使用 s
寄存器而不保留它们的原始值,b/c main
位于调用链的顶部——它有没有调用者——相比之下,我们可能会将 division
视为可以从任何调用者调用的通用函数,因此应遵循调用约定。)
我们不仅要尽量减少指令,还要尽量减少循环中的指令数。您正在通过将 $s0
复制到 $v0
来完成“return val”的工作,但每次都在循环中,而只需要一次,因此最好留到循环之后.更好的是,首先简单地使用 $v0
作为计数器 (val
)。
slt
指令有一个等效的 slti
指令,在与常数进行比较时非常方便。使用 slti
,无需将常量 0
加载到 $t0
寄存器。如果您确实需要在寄存器中使用常量 0,那么总有 [=33=]
寄存器。
MIPS指令集有与零比较的关系分支指令,因此您可以直接在寄存器<= 0(或< 0)上分支而无需slt
/slti
.
我已经把这个C程序翻译成了MIPS汇编代码。 所以,在这里我想知道:
- 我是不是做错了什么?
- 如果我的汇编代码是正确的,我可以通过减少 指令数。我在这里使用了14条指令
这是我转换这个 C 程序的方法 这是我的 MIPS 汇编代码
#s0 = res
main:
addi $a0 , [=10=] , 27
addi $a1 , [=10=] , 3
jal division # call function
add $s0 , $v0 , [=10=] # res = returned value
division:
addi $t0 , [=10=] , 0 # $t0 = 0
addi $s0 , [=10=] , 0 # $s0 = val and val = 0
add $s1 , [=10=] , $a0 # $s1 = i and i = 27
loop:
slt $t1 , $s1 , $t0 # checking if i>0
bne $t1 , [=10=] , break # if $t1 = 0 then break
addi $s0 , $s0 , 1 # else val = val + 1
sub $s1 , $s1 , $a1 # i = i - y
add $v0 , $s0 , [=10=] # put return value in $v0
j loop
break:
jr $ra # return to caller
这是高级 C 程序
int main(){
int res;
res = division (27, 3);
}
int division(int x, int y)
{
int i;
int val = 0;
for (i = x; i>0; i = i-y){
val = val + 1;
}
return val;
}
错误:
关系被错误地否定了。您已将其翻译为
for ( i = x; i >= 0; i -= y )
所以,它会比你想要的多循环一次(当整数除法是精确的时候)。您知道循环条件测试需要对 if-goto-label 样式的程序集取反:
if ( ! (i > 0) ) goto break;
然而 ! (i > 0)
是 i <= 0
你有 i < 0
.
你没有在main
结束的时候正确终止程序,所以会不小心掉到divide子程序中。解决方案是向 main
.
建议:
寄存器使用过多,使用较少的寄存器也会减少所需的指令数。此外,使用 $s
寄存器而不保留其原始值违反了调用约定。但是,最好的解决方案是简单地避免在该函数中使用它们——直接使用 $a0
和 $a1
而不是将它们复制到其他地方。
(可以在 main
中使用 s
寄存器而不保留它们的原始值,b/c main
位于调用链的顶部——它有没有调用者——相比之下,我们可能会将 division
视为可以从任何调用者调用的通用函数,因此应遵循调用约定。)
我们不仅要尽量减少指令,还要尽量减少循环中的指令数。您正在通过将 $s0
复制到 $v0
来完成“return val”的工作,但每次都在循环中,而只需要一次,因此最好留到循环之后.更好的是,首先简单地使用 $v0
作为计数器 (val
)。
slt
指令有一个等效的 slti
指令,在与常数进行比较时非常方便。使用 slti
,无需将常量 0
加载到 $t0
寄存器。如果您确实需要在寄存器中使用常量 0,那么总有 [=33=]
寄存器。
MIPS指令集有与零比较的关系分支指令,因此您可以直接在寄存器<= 0(或< 0)上分支而无需slt
/slti
.