RISC-V Recursive Factorial Function Debugging - recursion

Im trying to create a recursive factorial function in RISCV but having some problems.
Here's what we have so far:
.globl factorial
.data
n: .word 8
.text
main:
la t0, n
lw a0, 0(t0)
jal ra, factorial
addi a1, a0, 0
addi a0, x0, 1
ecall # Print Result
addi a1, x0, '\n'
addi a0, x0, 11
ecall # Print newline
addi a0, x0, 10
ecall # Exit
factorial:
la t1, n
beq x0, t1, finish
addi t0, t1, -1
mul a0, t0, a0
j factorial
finish:
ret
ecall
We tried adding and changing around the registers to use, but its still not loading the correct values to the correct registers. We're also kinda stuck on how to do this recursively. Would love some help!

Your main code looks fine. All of the issues I see are in the factorial function. First, there are four clear issues with your factorial function:
factorial:
# This loads the address of n not the value at label n
# You need to additionally lw t1, 0(t1) to get the value
la t1, n
# t1 is never getting modified so why would this loop ever terminate?
beq x0, t1, finish
# You should do these two operations in the opposite order
# if t1 = 1, a0 would become 0
addi t0, t1, -1
mul a0, t0, a0
j factorial
finish:
ret
# Why ecall here? You have already returned. This is unreachable.
ecall
However, you can't just fix those and expect it to work. Your current implementation is lacking a plan of how to actually compute the factorial. I assume you were trying to make an implementation like the following:
int factorial_recursive(int n) {
if (n == 0) {
return 1;
}
int recursive = factorial_recursive(n-1);
return n * recursive;
}
A direct translation of that C code would need to use the stack to save n and the return address and properly follow calling convention. I am not prepared to write out a complete explanation of that though, so I will explain how to convert the looping version of factorial to get you started in the right direction.
The C code I will implement is RISC-V assembly:
int factorial_loop(int n) {
int out = 1;
while (n > 0) {
out *= n;
n -= 1;
}
return out;
}
For this code, n will start out in a0, but eventually it will need to be moved out so we can return out so we will allocate our registers so the function will look like:
int factorial_loop(int a0) {
int a1 = 1;
while (a0 > 0) {
a1 *= a0;
a0 -= 1;
}
a0 = a1;
return a0;
}
From here it is pretty easy to do a direct conversion.
factorial_loop:
li a1, 1 # int a1 = 1;
loop:
beq a0, x0, finish # while (a0 > 0) {
mul a1, a1, a0 # a1 *= a0;
addi a0, a0, -1 # a0 -= 1;
j loop # }
finish:
mv a0, a1 # a0 = a1;
ret # return a0;

Related

ARM assembly recursive sum of 1..n

So I've had the following task that needs to be written in ARM Assembly
Please test the subroutine by passing the value of n equals 5. The result
should equal 15. If n equals 3, the result is 6.
I was given the corresponding Java code:
int sum (int n)
{
int result;
if (n == 1)
return 1;
result = Sum (n‐1) + n;
return result;
}
And I have written the following code in ARM Assembly
NAME main
PUBLIC main
SECTION .text: CODE (2)
THUMB
main
LDR R4, =0x005 ; Value n
BL SUM
STOP B STOP
SUM
MOV R1, #0x01
PUSH {R1, LR}
ADD R5, R5, R4 ; R5 = result
CMP R5, #1 ; Compare result to 1
BEQ ADD1
ADD0
SUB R4, R4, #1 ; Value n - 1
CMP R4, #0 ; Compare n to 0
BEQ ADD1
BL SUM
ADD1
POP {R4, LR}
BX LR ; Branch and exchange instruction set
END
The code is running fine but I want to know if there could be any slight improvements/shortcuts.
I am also a little unsure about the comments but I believe what I have written is correct.

The program returns error "attempt to execute non-instruction at 0x00000000"

I have been given HW regarding RISC-V.
The tasks is to solve the recurring equation T(n) = 2T(n/2)+n if the n or input is >=2, otherwise it returns 1. I have tried to create the solution code but it keeps giving me the (error) attempt to execute non-instruction at 0x00000000. Can someone please tell me where is my mistake and how to fix it?
Thank you for your time!
Notes: I can only starts to write the code from the "Write your recursive code here...."
.globl __start
.rodata
msg_input: .string "Enter a number: "
msg_result: .string "The result is: "
newline: .string "\n"
.text
__start:
# prints msg_input
li a0, 4
la a1, msg_input
ecall
# read from standard input
li a0, 5
ecall
################################################################################
# write your recursive code here, input is in a0, store the result(integer type) to t0
jal findsum
findsum:
li t0, 2 #t0==2
blt a0, t0, L1 #if n<2 return 1
addi sp, sp, -8 #reserve stack area
sw ra, 0(sp) #save return address
sw a0, 4(sp) #save input
li t0, 2 #t0==2
div a0, a0, t0 #n=n/2
jal findsum #call findsum(n/2)
#a1=FindSum(n/2)
li t0, 2 #t0=2
mul a1, t0, a1 #a1=2*FindSum(n/2)
addi a1, a1, 2 #a1=2*FindSum(n/2)+2
j done
L1:
li a1, 1
done:
lw ra, 0(sp)
addi sp, sp, 8
jr ra
################################################################################
result:
# prints msg_result
li a0, 4
la a1, msg_result
ecall
# prints the result in t0
li a0, 1
mv a1, t0
ecall
# ends the program with status code 0
li a0, 10
ecall

ARMSim Recursion

New to ARMsim, trying to figure out recursion in the Fibonacci number sequence. If I input n I want to find the value of the fibonacci sequence at this index through the recursive case:
int fib(int N) {
# if (N == 0) return 0;
# if (N == 1) return 1;
# return fib(N-1) + fib(N-2);
# }
My current code:
.text
.global _start
_start:
mov r1 , #8
fib:
SUB sp,sp,#8
STR lr,[sp,#0]
STR r1,[sp,#4]
CMP r1, #1
BGT Else
MOV r1, #1
ADD sp,sp,#8
MOV pc,lr
Else:
SUB r1,r1,#1
BL fib
MOV r2,r1
LDR r1,[sp,#4]
SUB r1,r1,#1
BL fib
MOV r3,r1
LDR r1,[sp,#4]
LDR lr,[sp,#0]
ADD sp,sp,#8
ADD r1,r1,r2
ADD r1,r1,r3
MOV pc,lr
I was wondering where I am going wrong.
Any advice appreciated!

Recursive Procedure Whose Calls Resemble a Binary Tree in MIPS

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

using recursive to compute fibonalci sequence (mips code)

i have this c code for fibonalcci sequence using recursive:
int fib(int n)
{
if (n==0) return 0;
if (n==1) return 1;
else return fib(n-1)+fib(n-2);
}
and i want to translate that c code into mips code (register a0 contain the value of n and register s0 contain the value of fib(n) )
here is what i have done:
F: bne $a0,$zero,L
addi $s0,$zero,0
addi $s1,$zero,1
jr $ra
L: addi $sp,$sp,-4
sw $ra,0($sp)
addi $a0,$a0,-1
jal F
addi $t0,$s1,0
add $s1,$s0,$s1
addi $s0,$t0,0
lw $ra,0($sp)
addi $sp,$sp,4
jr $ra
but it seem not do exactly what the c code do and i want to fix the mips code to let it do exactly like the c code. How do i fix that mips code?

Resources