【问题标题】:convert C code to MIPS assembly - combination function using recursion将 C 代码转换为 MIPS 程序集 - 使用递归的组合函数
【发布时间】:2019-03-15 18:32:25
【问题描述】:

我在将 C 代码转换为组合函数 (nCr) 的 MIPS 汇编代码时遇到问题。

nCr = (n-1Cr-1) + (n-1Cr)

当我将 int 5 用于 n 并将 3 用于 r(数字数据)时,结果必须为 10。

我想使用递归和堆栈指针,但是堆栈溢出出现错误。

下面有我的 MIPS 代码。

我的代码有什么问题?

我无法很好地识别问题......

##data
.data
digit: .word 5, 3

.text
.globl main

main:
##load data
la $t0, digit
lw $a0, 0($t0)  #put 5 in a
lw $a1, 4($t0)  #put 3 in b

##call Function comb
jal comb
##save return value in $t1
move $t1, $v0

##print result
li $v0, 1
add $a0, $0, $t1
syscall

##exit
li $v0, 10
syscall

##Function int comb(int a, int b)
comb:
addi $sp, $sp, -8
sw $ra, 4($sp)
sw $s0, 0($sp)

##base case
bne $a0, $a1, gen  #if (a==b)
addi $v0, $0, 1 #$v0 (1)
j rtn
bne $a1, $0, gen  #if (b==0)
addi $v0, $0, 1 #$v0 (1)
j rtn

##recursive call
gen:
addi $a0, $a0, -1 #$a0 (a-1)
addi $a1, $a1, -1 #$a1 (b-1)
jal comb  #call comb(a-1, b-1)
add $s0, $v0, $0  #$s0 comb(a-1, b-1)
addi $a1, $a1, 1  #$a1 (b)
jal comb  #call comb(a-1, b)
add $v0, $v0, $s0 #$v0 (comb(a-1, b-1) + comb(a-1, b))
j rtn

rtn:
lw $s0, 0($sp)
lw $ra, 4($sp)
addi $sp, $sp, 8
jr $ra

.end

【问题讨论】:

    标签: c recursion mips stack-overflow stack-pointer


    【解决方案1】:

    您的代码存在多个问题。

    首先,您选择的递归关系效率非常低,而且比必要的复杂得多。甚至Wikipedia 也有几个更好的递归关系,例如

    n over k = (n-1 over k-1) * (n/k)

    避免多次递归到函数中(从而允许函数以尾递归方式编写)。

    您使用的重复有一个额外的缺点,即您必须同时检查k==0 k==n

    这将我们带到您的实施中,它未能正确检查这两个条件:

    ##base case
    bne $a0, $a1, gen  #if (a==b)
    addi $v0, $0, 1 #$v0 (1)
    j rtn
    bne $a1, $0, gen  #if (b==0)
    addi $v0, $0, 1 #$v0 (1)
    j rtn
    

    如果a!=b,无论是否b==0,这些测试中的第一个会跳过第二个测试,因此第二个测试无法访问。您必须将代码更改为

    ##base case
    bne $a0, $a1, isbzero  #if (a==b)
    addi $v0, $0, 1 #$v0 (1)
    j rtn
    isbzero:
    bne $a1, $0, gen  #if (b==0)
    addi $v0, $0, 1 #$v0 (1)
    j rtn
    

    最后,您不会在递归调用之前保留函数参数的值。如果您想符合 ABI,则不能假设函数参数寄存器中的值在调用过程中被保留。

    尤其是之后

    ##recursive call
    gen:
    addi $a0, $a0, -1 #$a0 (a-1)
    addi $a1, $a1, -1 #$a1 (b-1)
    jal comb  #call comb(a-1, b-1)
    

    以下

    add $s0, $v0, $0  #$s0 comb(a-1, b-1)
    addi $a1, $a1, 1  #$a1 (b)
    jal comb  #call comb(a-1, b)
    

    $a0$a1 的值将不正确。

    如果 ABI 合规性对您来说并不重要,您可以在返回之前通过再次递增参数来恢复值。

    【讨论】:

    • 非常感谢您的帮助! :) 我添加了代码来保留梳子中的参数!像这样:梳子:addi $sp, $sp, -16 sw $a0, 1​​2($sp) sw $a1, 8($sp) sw $ra, 4($sp) sw $s0, 0($sp)并在我这样调用第二个递归之前恢复 $a0, $a1: lw $a1, 8($sp) lw $a0, 1​​2($sp) addi $a1, $a1, 1 #$a1 (b) jal comb #call comb(a-1, b) 但我的返回值错误..!你能告诉我又做错了什么吗..?对不起;-(
    • @Yoom 如果在调用之间将参数恢复为其原始值,则必须执行 addi $a0, $a0, -1 以从 n 计算 n-1 而不是 addi $a1, $a1, 1k 计算k-1。或者,如果您存储n-1 的值和k 的值,则可以避免额外的算术运算。一般来说,我建议先用 c 之类的高级语言编写算法,然后再翻译。
    【解决方案2】:

    您的基本问题是您没有在(递归)调用中保存寄存器值,因此这些值被破坏了。 $a0 和 $a1 是调用者保存寄存器,因此任何调用都可能破坏它们,而您的函数 comb 实际上会破坏它们。但这意味着在第一次递归调用之后,值就消失了,所以第二次递归调用是垃圾。

    您需要在第一次递归调用之前将 $a0 和 $a1 的值保存在堆栈帧中,并在它返回后(在第二次调用之前)恢复它们。您还需要在第二次调用前后保存 $v0 的值,因为 $v0 类似地被调用破坏了。

    【讨论】:

    • $v0 add $s0, $v0, $0 #$s0 comb(a-1, b-1)保存。
    • 非常感谢您的帮助!!!我真的很感谢你:) 那么,我可以在comb 的第一部分之前添加'sw',然后在第二次调用之前更改$a0 的值之前添加'lw'...?
    猜你喜欢
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 2016-01-27
    • 2012-06-16
    • 1970-01-01
    • 2021-09-13
    • 2014-01-24
    相关资源
    最近更新 更多