Recursive Procedure Whose Calls Resemble a Binary Tree in MIPS - recursion
I'm working on an assignment and am having difficulty understanding how to properly code the following problem in C.
int choose(int n, int k){
if (k == 0) {
return 1;
} else if (n == k) {
return 1;
} else if (n < k) {
return 0;
} else {
return choose(n-1, k-1) + choose(n-1, k);
}
}
My thoughts were to use three registers for storing the values onto the stack with each call $s0, $s1, $s2, where $s0 will contain the value of updated n; $s1 would maintain the value of k; and $s2 would hold the value of k in the second choose(n-1, k) since that value will only decrease when the parent call changes it. The reason I chose this is because the value of k isn't subtracted from each call in this one, it should be the same until the parent decrements it in a previous call.
Here is the Choose procedure that I'm trying to do. Problem is that I'm not getting the correct answer, of course.
Choose:
#store current values onto stack
addi $sp, $sp, -16
sw $ra, 0($sp)
sw $s0, 4($sp)
sw $s1, 8($sp)
sw $s2, 12($sp)
#check values meet criteria to add to $v0
beq $s1, $0, one
beq $s0, $s1, one
blt $s0, $s1, zero
beq $s2, $0, one
#no branches passed so decrement values of n and k
subi $s0, $s0, 1
subi $s1, $s1, 1
#move values of registers to $a's for argument passing
move $a0, $s0
move $a1, $s1
jal Choose #call procedure again
#this is where I'm having my problems
#not sure how to loop the procedure to get
#the second half of the equation Choose(n-1,k)
#which is the reason for the next 2 lines of code
move $a2, $s2
jal Choose
add $v0, $v0, $v1
j Choose_Exit
#add one to $v1 from branch passed
one:
addi $v1, $v1, 1
j Choose_Exit
#branch returns 0
zero:
addi $v1, $v1, 0
j Choose_Exit
#return values to caller from stack
Choose_Exit:
lw $s2, 12($sp)
lw $s1, 8($sp)
lw $s0, 4($sp)
lw $ra, 0($sp)
addi $sp, $sp, 16
jr $ra
So I'm having a problem understanding how to properly implement this recursive procedure twice to add them together. I can understand how to create a recursive procedure in MIPS to perform a factorial, since that is always the definition of recursion for any language. But using recursion with differing arguments and then add them all together is confusing me to no end.
When written out on paper, I understand that this procedure can be represented by a binary tree of parents and children. The parent being the single function Choose(n,k) and the children being Choose(n-1, k-1) + Choose(n-1, k) and once one of the leaf children branches from the if statement, it passes a digit to the parent who will wait for the other callee portion of the addition to return its value, etc etc etc.
Any help to point me in the correct direction as to what I'm doing wrong with my approach would be great. I understand the beginning, I understand the end, just need some assistance to help understand the most important part of the middle.
You were pretty close.
You established your stack frame with four words for: return address, arg1, arg2, and save for return value.
Your main snag was that after the first call to your function, you have to save the $v0 onto the stack [as Margaret mentioned above].
Here's some code that I believe will work. It is very similar to yours, but I wrote it from scratch. It has the correct "push"/"pop" of the first call's return value.
I did add one small optimization for the early escape [non-recursive] cases: they omit creating the stack frame.
Anyway, here it is:
##+
# int
# choose(int n, int k)
# {
#
# if (k == 0)
# return 1;
#
# if (n == k)
# return 1;
#
# if (n < k)
# return 0;
#
# return choose(n - 1,k - 1) + choose(n - 1,k);
# }
##-
.text
# choose -- choose
#
# RETURNS:
# v0 -- return value
#
# arguments:
# a0 -- n
# a1 -- k
#
# registers:
# t0 -- temp for 1st return value
choose:
beqz $a1,choose_one # k == 0? if yes, fly
beq $a0,$a1,choose_one # n == k? if yes, fly
blt $a0,$a1,choose_zero # n < k? if yes, fly
# establish stack frame (preserve ra/a0/a1 + space for v0)
sub $sp,$sp,16
sw $ra,12($sp)
sw $a0,8($sp)
sw $a1,4($sp)
addi $a0,$a0,-1 # get n - 1 (common to both calls)
# choose(n - 1,k - 1)
addi $a1,$a1,-1 # get k - 1
jal choose
sw $v0,0($sp) # save 1st return value (on _stack_)
# choose(n - 1,k)
addi $a1,$a1,1 # get k (from k - 1)
jal choose
lw $t0,0($sp) # "pop" first return value from stack
add $v0,$t0,$v0 # sum 1st and 2nd values
# restore from stack frame
lw $ra,12($sp)
lw $a0,8($sp)
lw $a1,4($sp)
add $sp,$sp,16
jr $ra # return
choose_one:
li $v0,1
jr $ra
choose_zero:
li $v0,0
jr $ra
UPDATE:
First off, I like how you noted the procedure as you did before you called it. I'm going to steal that!
Be my guest! It's from many years of writing asm. For a primer on my thoughts about how to write asm well, see my answer: MIPS linked list
I've tried this and it works. I need to experiment with your code to understand why the stack is manipulated when it is (always thought it had to be at the very beginning and end of a proc).
Normally, the stack frame is established at the proc start and restored from at the proc end. Your code for handling the "quick escape" [non-recursive] cases was correct, based on having already established the frame.
This was just a small optimization. But, it comes from the fact that because mips has so many registers that, for small functions, we don't even need a stack frame, particularly if the function is a "leaf" or "tail" (i.e. it doesn't call any other function).
For smaller [non-recursive] functions, sometimes we can get away with a one word stack frame that just preserves $ra (e.g.): fncA calls fncB, but fncB is a leaf. fncA needs a frame but fncB does not. In fact, if we control both functions and we know that fncB does not modify a given temp register (e.g. $t9), we can save the return address there instead of creating a stack frame in fncA:
fncA:
move $t9,$ra # preserve return address
jal fncB # call fncB
jr $t9 # return
fncB:
# do stuff ...
jr $ra # return
Ordinarily, we couldn't rely upon fncB preserving $t9 because, according to the mips ABI, fncB is at liberty to modify/trash any register that is not $sp or $s0-$s7. But, if we craft the functions such that we consider fncB to be "private" to fncA (e.g. like a C static function that only fncA has access to), we can do whatever we want.
Believe it or not, fncA above is ABI conforming.
A given callee (e.g. fncA) does not need to preserve $ra for [the sake of] its caller, just for itself. And, what is important is the value inside $ra, not the specific register. A callee only needs to preserve $s0-$s7, ensure that $sp has the same value at exit as entry, and that it returns to the correct address in caller [which the jr $t9 does--because it has the value that was in $ra when fncA was called].
I like your use of the temp register.
An extra register is required because, in mips, we can not do arithmetic operations from memory operands. mips can only do lw/sw. (i.e.) There is no such thing as:
add $v0,$v0,0($sp)
I used $t0 for simplicity/clarity because, when you need a temp reg, $t0-$t9 are the usual ones to use. The code "reads better" when using $t0.But, this is just a convention.
In the mips ABI, $a0-$a3 can be modified, as can $v1 as only $s0-$s7 need to be preserved. And, "modification" means that they can be used to hold any value or used for any purpose.
In the above link, note that strlen increments $a0 directly to find the end of the string. It is using $a0 for a useful purpose, but, as far as the caller to it is concerned, $a0 is being "trashed" [by strlen]. This usage is ABI conforming.
In choose, I could have used just about any register: $v1, $a2-$a3 instead of $t0. In fact, at that particular point in choose, $a0 is no longer needed, so it could have been used in place of $t0. Although for choose, we are non-ABI conforming (because we save/restore $a0-$a1), this would work in choose, because we restore the original value of $a0 from the function epilog [stack frame pop], preserving the recursive nature of the function.
As I said, $t0-$t9 are the usual registers to use for scratch space. But, I've written functions that use all 10 of them, and still needed more (e.g. drawing into a frame buffer using the Bresenham circle algorithm). $v0-$v1 and $a0-$a3 can be used as temp regs to get an additional 6. If necessary, $s0-$s7 can be preserved in the stack frame, solely to free them up to use as more temp regs.
Disclaimer: I rewrite the assembly code WITHOUT checking it.
There are delay-slots to consider so better to make smart use of them and to make them explicit to avoid aggregations of implicit nop instructions after branch instructions.
Reverse the order of calls between choose(n - 1,k - 1) and choose(n - 1,k) for smarter usage of $a0 and $a1 and of the stack.
Restrain stack usage for only calling choose(n - 1,k) and use tail calling for calling choose(n - 1,k - 1).
It makes more sense to stack the value of a0-1 and not a0.
We accumulate everything into $v0 instead of stacking it. We keep $t0 as a local result to add to $v0 because it is cheap while we can discard it by doing direct things with $v0.
As a result, the overall changes should make icache happier with less instructions and dcache happier with less stack space.
The assembly code:
##+
# int
# choose(int n, int k)
# {
#
# if (k == 0)
# return 1;
#
# if (n == k)
# return 1;
#
# if (n < k)
# return 0;
#
# return choose(n - 1,k - 1) + choose(n - 1,k);
# }
##-
.text
# choose -- choose
#
# RETURNS:
# v0 -- return value
#
# arguments:
# a0 -- n
# a1 -- k
#
# registers:
# t0 -- temp for local result to accumulate into v0
choose:
j choose_rec
addui $v0, $zr, 0 # initialize $v0 to 0 before calling
choose_rec:
beqz $a1,choose_one # k == 0? if yes, fly
addui $t0,$zr,1 # branch delay-slot with $t0 = 1
beq $a0,$a1,choose_one # n == k? if yes, fly
nop # branch delay-slot with $t0 = 1 already done
blt $a0,$a1,choose_zero # n < k? if yes, fly
addui $t0,$zr,0 # branch delay-slot with $t0 = 0
# establish stack frame (preserve ra/a0/a1)
sub $sp,$sp,12
addui $a0,$a0,-1 # get n - 1 (common to both calls)
sw $ra,8($sp)
sw $a0,4($sp)
jal choose_rec # choose(n - 1,k)
sw $a1,0($sp)
# restore from stack frame
lw $ra,8($sp)
lw $a0,4($sp)
lw $a1,0($sp)
add $sp,$sp,12
# choose(n - 1,k - 1)
j choose_rec # tail call: jump instead of call
addi $a1,$a1,-1 # get k - 1
choose_one:
choose_zero:
jr $ra # return
addui $v0,$v0,$t0 # branch delay-slot with $v0 += $t0
Related
MIPS Assembly - how to calculate the square of n recursively
I want to write a program that calculates the square of n using recursion based on the equation n^2 = (n - 1)^2 + 2(n - 1) + 1 But I don't know how to write the nonbasecase: part. Can anyone help? A python program would be def square(n) { if (n==0): return 0 else: return square(n-1) + 2*(n-1) + 1 } Here is what I got so far. start: li $a0, 0x0003 #$a0 contains the number to be squared jal square # recursive call square: subi $sp, $sp, 8 # decrement the stack pointer $sp sw $ra, 4($sp) # push the return address register $ra sw $a0, 0($sp) # push argument register $a0 li $t0, 0x0001 # load $t0 with 1 as part of test for base case bne $a0, $t0, nonbasecase # branch if not the base case li $v0, 0x0001 # return base result in $v0 addi $sp, $sp 8 # recover stack space jr $ra # jump to return address in $ra nonbasecase: #not sure how to write when it is not the base case jr $ra # jump to contents of return address register $ra
Write the function so that it inputs n2 and n and returns n2+2*n+1 and n+1.
trouble with simple recursion in MIPS assembly
so I'm trying to write a program in mips that just does some simple recursion, ive read every other article and question on the subject i can find but to no avail. anyways i'm trying to print my name backwards. so i'm reading one byte at a time looking for the 0 at the end of my string, if i find it i start unwinding and printing the bytes i saved. Thanks ahead of time for any help. code: .data # print name backwards Yn: .asciiz "frodobaggins" #yn stands for your name .text main: li $v0, 4 #the v registers tell system what to do 4 == print_string la $a1, Yn #load Yn's address into a0 addi $sp, $sp, -4 #increment stack pointer, always initially points to garbage sw $a1, 0($sp) #store address of a1 (Yn) into stack pointer jal recurse #recursive func call addi $sp, $sp, 4 #increment sp back to beggining addi $v0, $0, 10 #v0 = exit syscall #exit recurse: lw $a3, 0($sp) #load my name into reg $a3 lw $t6, 0($a3) #read byte addi $sp, $sp, -12 #increment stack ptr sw $ra, 8($sp) #store address of $ra onto stack sw $t6, 4($sp) #store the byte loaded into $t6 onto stack bne $t6, $0, otherwise #if t6 == 0 goto otherwise add $a3, $a3, 1 #a1 = a1 + 1 sw $a3, 0($sp) #store the word, without the byte $t6 used, onto the stack jal recurse lw $t6, 4($sp) #load the byte that was stored into $t6 lw $ra, 8($sp) #get $ra ready for returning addi $v0, $0, 4 #setup $v0 to tell pc to print to console lw $a0, 0($t6) #console always reads from $a0 syscall addi $sp, $sp, 12 #put stack pointer back jr $ra #string is finished, return. otherwise: #because this is just returning, i need to get the ra that was just stored on the stack lw $ra 8($sp) jr $ra #go back to original call of jal
There were a few problems. Loading $t6 should be a byte operation. The conditional branch to otherwise had an incorrect sense. The syscall to print should have been 11 [putc] and not 4 [puts]. The stack frame restore at otherwise was problematic. I've created three versions. An annotated version that shows the bugs. A cleaned up and working version. And, a version I created from scratch. Here's the annotated version [please pardon the gratuitous style cleanup]: .data Yn: .asciiz "frodobaggins" # yn stands for your name # print name backwards .text main: li $v0,4 # syscall for print string la $a1,Yn # load Yn's address into a0 addi $sp,$sp,-4 # increment stack pointer sw $a1,0($sp) # store address of a1 (Yn) into stack pointer jal recurse # recursive func call addi $sp,$sp,4 # increment sp back to beggining addi $v0,$0,10 # v0 = exit syscall # exit recurse: lw $a3,0($sp) # load my name into reg $a3 # NOTE/BUG: this should be lb # NOTE/BUG: by loading $t6 _before_ we've saved it, we are destroying the # value that _caller_ set ###lw $t6,0($a3) # read byte lb $t6,0($a3) # read byte addi $sp,$sp,-12 # increment stack ptr sw $ra,8($sp) # store address of $ra onto stack sw $t6,4($sp) # store the byte loaded into $t6 onto stack # NOTE/BUG: this should be beq and _not_ bne ###bne $t6,$0,otherwise # if t6 == 0 goto otherwise beq $t6,$0,otherwise # if t6 == 0 goto otherwise add $a3,$a3,1 # a1 = a1 + 1 sw $a3,0($sp) # push the word, without the byte $t6 used jal recurse lw $t6,4($sp) # load the byte that was stored into $t6 lw $ra,8($sp) # get $ra ready for returning # NOTE/BUG: this is the puts syscall -- what we want is putc (i.e. 11) ###addi $v0,$0,4 # setup $v0 to tell pc to print to console addi $v0,$0,11 # setup $v0 to tell pc to print to console # NOTE/BUG: we want to load the byte into $a0 # NOTE/BUG: at this point $t6 has the byte value and _not_ a pointer ###lw $a0,0($t6) # console always reads from $a0 move $a0,$t6 syscall addi $sp,$sp,12 # put stack pointer back jr $ra # string is finished, return. otherwise: # because this is just returning, i need to get the ra that was just # stored on the stack lw $ra,8($sp) # NOTE/BUG: by _not_ restoring the $t6 value of _caller_ things are broken jr $ra # go back to original call of jal Here's the working version. Note the early stack frame setup and the full stack frame pop at otherwise. This is somewhat more common. That is, one and only one place to establish the stack frame. And, one and only one place to pop the frame and return: .data Yn: .asciiz "frodobaggins" # yn stands for your name # print name backwards .text main: li $v0,4 # syscall for print string la $a1,Yn # load Yn's address into a0 addi $sp,$sp,-4 # increment stack pointer sw $a1,0($sp) # store address of a1 (Yn) into stack jal recurse # recursive func call addi $sp,$sp,4 # increment sp back to beggining addi $v0,$0,10 # v0 = exit syscall # exit recurse: lw $a3,0($sp) # load my name into reg $a3 lb $t6,0($a3) # read byte addi $sp,$sp,-12 # establish stack frame sw $ra,8($sp) # store address of $ra onto stack sw $t6,4($sp) # store the byte loaded into $t6 onto stack beq $t6,$0,otherwise # if t6 == 0 goto otherwise add $a3,$a3,1 # a1 = a1 + 1 sw $a3,0($sp) # push the word, without the byte $t6 used jal recurse lw $t6,4($sp) # load the byte that was stored into $t6 lw $ra,8($sp) # get $ra ready for returning addi $v0,$0,11 # setup $v0 to tell pc to print to console move $a0,$t6 syscall otherwise: lw $ra,8($sp) # restore return address from stack lw $t6,4($sp) # store the byte loaded into $t6 onto stack addi $sp,$sp,12 # put stack pointer back jr $ra # string is finished, return. Here's my refactored version. It is a bit more mips ABI conformant. There are a few slight variations possible: .data name: .asciiz "frodobaggins" nl: .asciiz "\n" .text .globl main main: li $v0,4 # print string syscall la $a0,name # string address syscall li $v0,4 # print string syscall la $a0,nl # string address syscall la $a0,name # string address jal recurse1 li $v0,4 # print string syscall la $a0,nl # string address syscall li $v0,10 syscall # recurse1 -- reverse print string # # arguments: # a0 -- current string pointer recurse1: sub $sp,$sp,8 # create stack frame sw $ra,4($sp) # save return address sw $a0,0($sp) # save current string pointer lb $t0,0($a0) # get current byte beqz $t0,recurse1_exit # is it EOS? if yes, fly addu $a0,$a0,1 # advance to next char jal recurse1 # print other chars subu $a0,$a0,1 # go back to our character lb $a0,0($a0) # get current byte li $v0,11 # putc syscall number syscall recurse1_exit: lw $ra,4($sp) # restore return address lw $a0,0($sp) # restore current string pointer add $sp,$sp,8 # pop stack frame jr $ra # return # recurse2 -- reverse print string # # arguments: # a0 -- current string pointer recurse2: sub $sp,$sp,8 # create stack frame sw $ra,4($sp) # save return address sw $a0,0($sp) # save current string pointer lb $t0,0($a0) # get current byte beqz $t0,recurse2_exit # is it EOS? if yes, fly addu $a0,$a0,1 # advance to next char jal recurse2 # print other chars lw $a0,0($sp) # restore our pointer lb $a0,0($a0) # get current byte li $v0,11 # putc syscall number syscall recurse2_exit: lw $ra,4($sp) # restore return address lw $a0,0($sp) # restore current string pointer add $sp,$sp,8 # pop stack frame jr $ra # return # recurse3 -- reverse print string # # arguments: # a0 -- current string pointer recurse3: sub $sp,$sp,8 # create stack frame sw $ra,4($sp) # save return address lb $t0,0($a0) # get current byte sw $t0,0($sp) # save current char value beqz $t0,recurse3_exit # is it EOS? if yes, fly addu $a0,$a0,1 # advance to next char jal recurse3 # print other chars lw $a0,0($sp) # get current byte li $v0,11 # putc syscall number syscall recurse3_exit: lw $ra,4($sp) # restore return address add $sp,$sp,8 # pop stack frame jr $ra # return
MIPS translation from java
I'm working on making a recursive maze solver program in MIPS and i'm trying to implement an algorithm that our professor gave us. However, i'm stuck on how i would implement boolean p = solveMaze(r - 1, c, r, c); when i'm creating the program in MIPS. Basically, how i would turn a java boolean expression such as this into MIPS.
In assembly, booleans are usually represented by integers. Also functions return values using registers. Essentially the program will look similar to this ... main: # store parameters in registers $a0 to $a3 jal solveMaze mov $t0, $v0 li $v0, 1 syscall # prints 1 or 0 depending on what was returned solveMaze: # $a0 = r - 1, $a1 = c, $a2 = r, $a3 = c ... # Do what needs to be done here li $v0, 1 # $v0 contains 1 which means true, change to 0 for false jr $ra # return to the caller of the function
the answer is always wrong in this MIPS recursion . got 10, supposed to be 55
This code is supposed to print the sum of numbers from 10 to 0. It should be printing 55, but is printing 10 instead. Can you help me figure out where it's going wrong? main: # initialize values to 3 registers addi $a0,$zero,10 jal sum # call method # Print out the summation upto 10 li $v0,1 # print integer add $a1,$v0,$zero # load return value into argument syscall li $v0,10 # Exit syscall sum: addi $sp,$sp,-8 # allocate space on stack sw $ra,0($sp) # store the return address sw $a0,4($sp) # store the argument slti $t0,$a0,1 # check if n > 0 beq $t0,$0,recurse # n > 0 case add $v0,$0,$0 # start return value to 0 addi $sp,$sp,8 # pop 2 items off stack jr $ra # return to caller recurse: addi $a0,$a0,-1 # calculate n-1 jal sum # recursively call sum(n-1) lw $ra,0($sp) # restore saved return address lw $a0,4($sp) # restore saved argument addi $sp,$sp,8 # pop 2 items off stack add $v0,$a0,$v0 # calculate n + sum(n-1) jr $ra # return to caller
Functions return their value in $v0; the print integer syscall expects its argument in $a0. You need to move $a0 to $v0 before you do the print, otherwise you'll be printing whatever happened to be in $a0 (which in the case is the last value that you recursively added; i.e. 10).
I am using MIPS simulator to calculate the sum of numbers from N (10 in my case) to 1
I tried with the following code, but the answer is wrong. It should be 55 but i got 10. any help folks? main: # initialize values to 3 registers addi $a0,$zero,10 jal sum # call method # Print out the summation upto 10 li $v0,1 # print integer add $a1,$v0,$zero #load return value into argument syscall li $v0,10 # Exit syscall sum: addi $sp,$sp,-8 # allocate space on stack sw $ra,0($sp) # store the return address sw $a0,4($sp) # store the argument slti $t0,$a0,1 # check if n > 0 beq $t0,$0,recurse # n > 0 case add $v0,$0,$0 # start return value to 0 addi $sp,$sp,8 # pop 2 items off stack jr $ra # return to caller recurse: addi $a0,$a0,-1 # calculate n-1 jal sum # recursively call sum(n-1) lw $ra,0($sp) # restore saved return address lw $a0,4($sp) # restore saved argument addi $sp,$sp,8 # pop 2 items off stack add $v0,$a0,$v0 # calculate n + sum(n-1) jr $ra # return to caller
You are moving the sum from $v0 to $a1 after you store 1 in $v0. Also, You are storing the sum in $a1, while $a0 is the register that gets printed.. add $a1,$v0,$zero #load return value into argument ^ HERE This should fix it. add $a0,$v0,$zero #load return value into argument li $v0,1 # print integer syscall