2

I have a problem with conversing C code to MIPS assembly code of combination function (nCr).

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

and when I put int 5 for n and 3 for r (digit data), the result has to be 10.

I want to use the recursion and stack pointer, but I have an error with stack overflow.

I have my MIPS code below.

What's wrong with my code?

I cannot recognize the problem well...

##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
dbush
  • 205,898
  • 23
  • 218
  • 273
Yoom
  • 23
  • 2

2 Answers2

1

There are multiple problems with your code.

First, the recurrence relation you have chosen is very inefficient and much more complicated than necessary. Even Wikipedia has several better recurrence relations, for example

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

avoids recursing into the function multiple times (thus allowing the function to be written tail-recursively).

The recurrence you are using has the additional disadvantage that you must both check for k==0 and k==n.

This brings us to your implementation, which fails to check both conditions correctly:

##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

The first of these tests jumps over the second test if a!=b, regardless of whether b==0, so the second test is unreachable. You would have to change the code to

##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

Finally, you do not preserve the values of the function arguments before the recursive calls. If you want to be ABI compliant, you cannot assume the values in the function argument registers are preserved across the call.

In particular after

##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)

the following

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

will have incorrect values for $a0 and $a1.

If ABI compliance does not matter to you, you could restore the values before returning by incrementing the arguments again.

EOF
  • 6,273
  • 2
  • 26
  • 50
  • I really appreciate your help! :) I added code to preserve arguments in comb! like this: comb: addi $sp, $sp, -16 sw $a0, 12($sp) sw $a1, 8($sp) sw $ra, 4($sp) sw $s0, 0($sp) and restore $a0, $a1 before I call the second recursive like this: lw $a1, 8($sp) lw $a0, 12($sp) addi $a1, $a1, 1 #$a1 (b) jal comb #call comb(a-1, b) but I have my return value wrong..! Would you tell what did I do wrong again..? Sorry ;-( – Yoom Oct 11 '18 at 01:32
  • @Yoom If you restore the arguments to their original values between the calls, you have to do `addi $a0, $a0, -1` to compute `n-1` from `n` rather than `addi $a1, $a1, 1` to compute `k` from `k-1`. Alternatively, if you store the value of `n-1` and the value of `k` you can avoid the extra arithmetic. In general, I would advise writing the algorithm in a higher-level language like c first and translating it afterwards. – EOF Oct 11 '18 at 04:59
0

Your basic problem is that you're not saving register values across the (recursive) calls, so the values are being clobbered. $a0 and $a1 are caller-save registers, so any call might clobber them, and your function comb does in fact clobber them. But that means after the first recursive call, the values are gone, so the second recursive call is garbage.

You need to save the values of $a0 and $a1 in your stack frame before the first recursive call and restore them after it returns (before the second call). You'll also need to save the value of $v0 around the second call, as $v0 is similarly clobbered by calls.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • `$v0` *is* preserved by `add $s0, $v0, $0 #$s0 comb(a-1, b-1)`. – EOF Oct 10 '18 at 20:52
  • Thanks a lot for your help!!! I really appreciate you :) Then, can I add 'sw' just before the first part of comb, and 'lw' before I change the value of $a0 before the second call...? – Yoom Oct 11 '18 at 01:36