How to implement factorial function in RISC-V - factorial

C-code :
int factorial(int n)
{
if (n<1) return 1;
else return n * factorial (n-1);
}
I've tried to implement it but haven't managed to do a lot. So here is my try:
goto:
factorial:
int factorial(int n) {
if (n<1) goto Lthen;
Lelse:
tmp=factorial(n-1);
return n*tmp;
goto Lend;
Lthen: return 1;
Lend;
}
RISC V:
.factorial
addi sp, sp, -16
sw ra, (sp)
sw s0, 4(sp) //n
sw s1, 8(sp) //tmp
mv s0, a0 //a--->s0
addi t1, zero,1
blt s0, t1, Lthen
.Lelse
mv t0, s0 // copy of n to t0
addi s0, s0, -1 // n-1
mv a0, s0; // n-1--->a0
jal factorial // factorial(a0)
mv s1, a0 // s1=factorial(a0) //tmp
mul a0,t0,s1 // n*tmp ----> a0
ret
j LEND
Lthen: li a0,1
ret
LEND jr ra, 0
Can somebody tell me is that okay, because I don't know how to test it.
And I am not sure about for example return 1 /or any other value/expression, can we just put it in a0 and say ret..
Thank you for your time!

You should use $s0 for the preserved value, instead of $t0.
You should be subtracting one from $a0 after copying from $s0 before the recursive call.
The multiply then would go between the $a0 return value and the $s0 preserved value.
The reason $s0 will work but $t0 won't (to preserve the original $a0 input), is that you are making the (correct) effort to save the s registers.
However, you are not restoring the saved values in function epilogue, nor cutting back the stack, nor reloading $ra...

Here's a recursive factorial function in RISC-V from my RV32I assembly programmer's quick reference: (comments welcome!)
.text # recursive implementation of factorial
.globl __start
fact: # arg: n in a0, returns n! in a1
addi sp, sp, -8 # reserve our stack area
sw ra, 0(sp) # save the return address
li t0, 2
blt a0, t0, ret_one # 0! and 1! == 1
sw a0, 4(sp) # save our n
addi a0, a0, -1
jal fact # call fact (n-1)
# a1 <- fact(n-1)
lw t0, 4(sp) # t0 <- n
mul a1, t0, a1 # a1 <- n * fact(n-1)
j done
ret_one:
li a1, 1
done:
lw ra, 0(sp) # restore return address from stack
addi sp, sp, 8 # free our stack frame
jr ra # and return
__start:
li a0, 5 # compute 5!
jal fact
li a0, 1 # print it
ecall
li a0, 17
ecall # and exit
To test it, my favorite lightweight RISC-V simulators are Venus and Jupiter.

Related

Cant scanf and printf in risk-v rars

Hello i am a beginer risc-v learner and i want to do a scanf of an int and print it on console
upon searching a lot on the internet i couldnt find how to proper do it, i try
using compiler explorer https://godbolt.org/ i want to use this c code
#include<stdio.h>
int main(){
int number;
printf("enter a number:");
scanf("%d",&number);
printf("number is:%d ",number);
return 0;
}
and its giving me this risc-v code
main: # #main
addi sp, sp, -32
sw ra, 28(sp) # 4-byte Folded Spill
sw s0, 24(sp) # 4-byte Folded Spill
addi s0, sp, 32
mv a0, zero
sw a0, -20(s0) # 4-byte Folded Spill
sw a0, -12(s0)
lui a0, %hi(.L.str)
addi a0, a0, %lo(.L.str)
call printf
lui a0, %hi(.L.str.1)
addi a0, a0, %lo(.L.str.1)
addi a1, s0, -16
call scanf
lw a1, -16(s0)
lui a0, %hi(.L.str.2)
addi a0, a0, %lo(.L.str.2)
call printf
lw a0, -20(s0) # 4-byte Folded Reload
lw s0, 24(sp) # 4-byte Folded Reload
lw ra, 28(sp) # 4-byte Folded Reload
addi sp, sp, 32
ret
.L.str:
.asciz "enter a number:"
.L.str.1:
.asciz "%d"
.L.str.2:
.asciz "number is:%d "
but the part
call printf
call scanf
doesnt work and i cant figure how to do such a esy work any tips?
linker will give undefined reference error because directly you not compiled it in C instead took assembly code from gcc and gave it to linker.
if you directly compile with c you will get reference to printf and scanf and for risk v you can tell c to give static binary instead of dynamic
If i not understood what you want to know than plz explain

RISC-V - Iterate through a linked list and apply a function to each node

Given a very basic linked list in C:
struct node {
int value;
struct node *next;
};
I want to write the function "map" in RISC-V assembly language. The map function recursively applies a function to change the value at each node in the list. Ex: square all the data in the linked list. Function in C for reference:
void map(struct node *head, int (*f)(int))
{
if (!head) { return; }
head->value = f(head->value);
map(head->next,f);
}
The test output is as follows: prints the data in linked list, calls map with the function pointer called "square" which will square each value, then calling map again to a function pointer named "decrement" which will decrease each value by 1.
9 8 7 6 5 4 3 2 1 0
81 64 49 36 25 16 9 4 1 0
80 63 48 35 24 15 8 3 0 -1
Here is my RISC-V code that I am running in Venus, however I am having an issue with line 76 when I try and call the function passed into map.
.globl map
.text
main:
jal ra, create_default_list
add s0, a0, x0 # a0 (and now s0) is the head of node list
# Print the list
add a0, s0, x0
jal ra, print_list
# Print a newline
jal ra, print_newline
# === Calling `map(head, &square)` ===
# Load function arguments
add a0, s0, x0 # Loads the address of the first node into a0
# Load the address of the "square" function into a1 (hint: check out "la" on the green sheet)
### YOUR CODE HERE ###
la a1, square
# Issue the call to map
jal ra, map
# Print the squared list
add a0, s0, x0
jal ra, print_list
jal ra, print_newline
# === Calling `map(head, &decrement)` ===
# Because our `map` function modifies the list in-place, the decrement takes place after
# the square does
# Load function arguments
add a0, s0, x0 # Loads the address of the first node into a0
# Load the address of the "decrement" function into a1 (should be very similar to before)
### YOUR CODE HERE ###
# Issue the call to map
jal ra, map
# Print decremented list
add a0, s0, x0
jal ra, print_list
jal ra, print_newline
addi a0, x0, 10
ecall # Terminate the program
map:
# Prologue: Make space on the stack and back-up registers
### YOUR CODE HERE ###
addi sp, sp, -8
sw ra, 4(sp)
sw a0, 0(sp)
beq a0, x0, done # If we were given a null pointer (address 0), we're done.
add s0, a0, x0 # Save address of this node in s0
add s1, a1, x0 # Save address of function in s1
# Remember that each node is 8 bytes long: 4 for the value followed by 4 for the pointer to next.
# What does this tell you about how you access the value and how you access the pointer to next?
# Load the value of the current node into a0
# THINK: Why a0?
### YOUR CODE HERE ###
lw a0, 0(s0)
# Call the function in question on that value. DO NOT use a label (be prepared to answer why).
# Hint: Where do we keep track of the function to call? Recall the parameters of "map".
### YOUR CODE HERE ###
jalr ra, 0(a1)
# Store the returned value back into the node
# Where can you assume the returned value is?
### YOUR CODE HERE ###
sw a0 0(s0)
# Load the address of the next node into a0
# The address of the next node is an attribute of the current node.
# Think about how structs are organized in memory.
### YOUR CODE HERE ###
lw t0 4(s0)
mv a0 t0
# Put the address of the function back into a1 to prepare for the recursion
# THINK: why a1? What about a0?
### YOUR CODE HERE ###
mv a1 s1
# Recurse
### YOUR CODE HERE ###
jal ra, map
lw ra, 4(sp)
lw s0, 0(sp)
addi sp, sp, 8
jr ra
done:
# Epilogue: Restore register values and free space from the stack
### YOUR CODE HERE ###
addi sp, sp, -8
jr ra # Return to caller
# === Definition of the "square" function ===
square:
mul a0, a0, a0
jr ra
# === Definition of the "decrement" function ===
decrement:
addi a0, a0, -1
jr ra
# === Helper functions ===
# You don't need to understand these, but reading them may be useful
create_default_list:
addi sp, sp, -12
sw ra, 0(sp)
sw s0, 4(sp)
sw s1, 8(sp)
li s0, 0 # Pointer to the last node we handled
li s1, 0 # Number of nodes handled
loop: # do...
li a0, 8
jal ra, malloc # Allocate memory for the next node
sw s1, 0(a0) # node->value = i
sw s0, 4(a0) # node->next = last
add s0, a0, x0 # last = node
addi s1, s1, 1 # i++
addi t0, x0, 10
bne s1, t0, loop # ... while i!= 10
lw ra, 0(sp)
lw s0, 4(sp)
lw s1, 8(sp)
addi sp, sp, 12
jr ra
print_list:
bne a0, x0, print_me_and_recurse
jr ra # Nothing to print
print_me_and_recurse:
add t0, a0, x0 # t0 gets current node address
lw a1, 0(t0) # a1 gets value in current node
addi a0, x0, 1 # Prepare for print integer ecall
ecall
addi a1, x0, ' ' # a0 gets address of string containing space
addi a0, x0, 11 # Prepare for print char syscall
ecall
lw a0, 4(t0) # a0 gets address of next node
jal x0, print_list # Recurse. The value of ra hasn't been changed.
print_newline:
addi a1, x0, '\n' # Load in ascii code for newline
addi a0, x0, 11
ecall
jr ra
malloc:
addi a1, a0, 0
addi a0, x0, 9
ecall
jr ra
.data
start_msg: .asciiz "List before: "
end_msg: .asciiz "List after: "
.text
main:
jal ra, create_default_list
add s0, a0, x0 # $v0 = $s0 is head of node list
#print the list
add a0, s0, x0
jal ra, print_list
# print a newline
jal ra, print_newline
# load your args
add a0, s0, x0 # load the address of the first node into $a0
# load the address of the function in question into $a1 (check out la)
### YOUR CODE HERE ###
la a1,square
# issue the call to map
jal ra, map
#print the list
add a0, s0, x0
jal ra, print_list
addi a0, x0, 10
ecall #Terminate the program
map:
# Prologue: Make space on the stack and back-up registers
### YOUR CODE HERE ###
addi sp, sp, -12
sw ra, 0(sp)
sw s0, 4(sp)
sw s1, 8(sp)
beq a0, x0, done # If we were given a null pointer (address 0), we're done.
add s0, a0, x0 # Save address of this node in s0
add s1, a1, x0 # Save address of function in s1
# Remember that each node is 8 bytes long: 4 for the value followed by 4 for the pointer to next.
# What does this tell you about how you access the value and how you access the pointer to next?
# load the value of the current node into a0
# THINK: why a0?
### YOUR CODE HERE ###
lw a0 ,0(s0)
# Call the function in question on that value. DO NOT use a label (be prepared to answer why).
# What function? Recall the parameters of "map"
### YOUR CODE HERE ###
jalr s1
# store the returned value back into the node
# Where can you assume the returned value is?
### YOUR CODE HERE ###
sw a0, 0(s0)
# Load the address of the next node into a0
# The Address of the next node is an attribute of the current node.
# Think about how structs are organized in memory.
### YOUR CODE HERE ###
lw a0, 4(s0)
# Put the address of the function back into a1 to prepare for the recursion
# THINK: why a1? What about a0?
### YOUR CODE HERE ###
add a1, s1, x0
# recurse
### YOUR CODE HERE ###
jal ra map
done:
# Epilogue: Restore register values and free space from the stack
### YOUR CODE HERE ###
addi sp, sp, 12
lw ra, 0(sp)
lw s0, 4(sp)
lw s1, 8(sp)
jr ra # Return to caller
square:
mul a0 ,a0, a0
jr ra
create_default_list:
addi sp, sp, -12
sw ra, 0(sp)
sw s0, 4(sp)
sw s1, 8(sp)
li s0, 0 # pointer to the last node we handled
li s1, 0 # number of nodes handled
loop: #do...
li a0, 8
jal ra, malloc # get memory for the next node
sw s1, 0(a0) # node->value = i
sw s0, 4(a0) # node->next = last
add s0, a0, x0 # last = node
addi s1, s1, 1 # i++
addi t0, x0, 10
bne s1, t0, loop # ... while i!= 10
lw ra, 0(sp)
lw s0, 4(sp)
lw s1, 8(sp)
addi sp, sp, 12
jr ra
print_list:
bne a0, x0, printMeAndRecurse
jr ra # nothing to print
printMeAndRecurse:
add t0, a0, x0 # t0 gets current node address
lw a1, 0(t0) # a1 gets value in current node
addi a0, x0, 1 # prepare for print integer ecall
ecall
addi a1, x0, ' ' # a0 gets address of string containing space
addi a0, x0, 11 # prepare for print string syscall
ecall
lw a0, 4(t0) # a0 gets address of next node
jal x0, print_list # recurse. We don't have to use jal because we already have where we want to return to in ra
print_newline:
addi a1, x0, '\n' # Load in ascii code for newline
addi a0, x0, 11
ecall
jr ra
malloc:
addi a1, a0, 0
addi a0, x0 9
ecall
jr ra

incorrect output in MIPs program

I'm implementing the following code from c to mips assembly
/*
Given an array arr terminated by entry -1, transform the array by adding
to each entry the sum of the remainder of the array after that entry.
That is, if initially arr == [a_0, a_1, ..., a_n, -1], then finally
arr == [b_0, b_1, ..., b_n, -1], where b_j = sum(a_i for i in j..n-1).
*/
uint32_t reverse_prefix_sum(uint32_t *arr) {
uint32_t r;
if(*arr == -1) return 0;
r = reverse_prefix_sum(arr+1) + (uint32_t)*arr;
*arr = r; /* may discard MSB */
return(r);
}
My current program is the following:
reverse_prefix_sum: # Let the address of array arr be in $a0
# adjust the stack pointer to save $a0 and $ra
addi $sp,$sp,-8
sw $a0,0($sp)
sw $ra,4($sp)
# Load *arr
lw $t0,0($a0)
beq $t0,-1,exit1 # r is in $v0
# *arr = *arr + 1
addu $a0,$a0,4 # recursive call
jal reverse_prefix_sum
addu $v0,$v0,$t0
# restore $a0
lw $a0,0($sp)
#restore $ra
lw $ra,4($sp)
add $sp,$sp,8
sw $v0,0($a0)
jr $ra
exit1:
# restore $a0
lw $a0,0($sp)
#restore $ra
lw $ra,4($sp)
addi $sp,$sp,8
# return 0
li $v0,0
jr $ra
With input 1 2 3 4 -1, I'm getting output -4 -3 -2.
I'm wondering where this could be improved to fix the output being incorrect.
The problem is that t0 isn’t preserved across the function call. Reload it from 0(a0) after restoring a0 after the function returns.

MIPS nested function calls

I'm trying to convert a C Code to MIPS code.
int main() {
int a;
int b;
int result = 0;
if (a != b)
result = test(a, b);
else
result = a + b;
return result;
}
int test(int a, int b) {
if (a > b)
return multiply(a, b)
else
return subtract(a, b)
}
int multiply(int a, int b) {
return a * b;
}
int subtract(int a, int b) {
return a - b;
}
This code contains nested function calls inside test function.
I have put the return address of test function into stack and trying to return the subtracted or multiplied value to the main.
But in my case, my code executes both subtract and multiply functions.
I'm trying to put my result to s0. After running s0 always shows subtraction of values. If I put multiply result to s1, s1 shows true value.
I think subtract function overrides value at s0. But when case is multiply, why subtract method is called? I have an if/else block, but this part seems to be not working.
Here is my MIPS Code, what am I doing wrong?
.data
numberA: .word 4
numberB: .word 2
.text
.globl main
main:
addi $s0, $0, 0 # result = 0
lw $a0, numberA
lw $a1, numberB
bne $a0, $a1, L1
add $s0, $a0, $a1
L1: jal test
add $s0, $v0, $0
li $v0,10
syscall
test:
addi $sp, $sp, -4
sw $ra, 0($sp)
slt $s1,$a1,$a0
bne $s1, 1, ELSE
jal multiply
ELSE: jal subtract
lw $ra, 0($sp)
addi $sp, $sp, 8
jr $ra
subtract:
sub $s0, $a0, $a1
jr $ra
multiply:
mult $a0, $a1
mflo $s0
jr $ra
The problem is, that after you return from multiply, you still call subtract in the next line. You have to return from the function test after calling multiply.
However, since the function calls are both the last instruction of your function, you can use the following shortcut:
test:
slt $s1,$a1,$a0
bne $s1, 1, ELSE
j multiply
ELSE: j subtract
This way, you do not have to store the $ra in your stack, but you can directly use it to jump back to the caller of test in the jr $ra of subtract and multiply. This way it should work as intended.
Alternatively, skip over jal subtract after returning from multiply:
jal multiply
j OUT
ELSE: jal subtract
OUT: ...

C ternary conditional operator to MIPS assembly with one side loaded from memory

C statement
A= A? C[0] : B;
Is is correct to write in assembly instruction this way?
Assuming $t1=A, $t2=B, $s1=base address of Array C:
beq $t1, $0, ELSE
lw $t1, 0($s1)
ELSE: add $t1, $t2, $0
No, it doesn't seem correct because add $t1, $t2, $0 will be executed even if $t1 != $0.
I hope that this works (not tested):
beq $t1, $0, ELSE
sll $0, $0, 0 # NOP : avoid the instruction after branch being executed
lw $t1, 0($s1)
j END
sll $0, $0, 0 # NOP : avoid the instruction after branch being executed
ELSE: add $t1, $t2, $0
END:
This code assumes that the elements of C are 4-byte long each.
You can avoid an unconditional j. Instead of structuring this as an if/else, always do the A=B (because copying a register is cheaper than jumping) and then optionally do the load.
On a MIPS with branch-delay slots, the delay slot actually helps us:
# $t1=A, $t2=B, $s1=base
beq $t1, $zero, noload
move $t1, $t2 # branch delay: runs always
lw $t1, 0($s1)
noload:
# A= A? C[0] : B;
On a MIPS without branch-delay slots (like MARS or SPIM in their default config):
# MIPS without branch-delay slots
# $t1=A, $t2=B, $s1=base
move $t3, $t1 # tmp=A
move $t1, $t2 # A=B
beq $t3, $zero, noload # test the original A
lw $t1, 0($s1)
noload:
# $t1 = A= A ? C[0] : B;
If we can clobber B and rearrange registers, we can save an insn without a branch-delay:
# MIPS without branch-delay slots
# $t1=A, $t2=B, $s1=base
beq $t1, $zero, noload
lw $t2, 0($s1)
noload:
# $t2 = A. B is "dead": we no longer have it in a register
A move $t3, $t2 before the BEQ could save B in another register before this sequence. Ending up with your variables in different registers can save instructions, but makes it harder to keep track of things. In a loop, you can get away with this if you're unrolling the loop because the 2nd copy of the code can re-shuffle to get registers back the way they need to be for the top of the loop.
move x, y is a pseudo-instruction for or x, y, $zero or ori x, y, 0. Or addu x, y, $zero. Implement it however you like or let your assembler do it for you.
Write a MIPS segment for the C statement
x=5; y=7;
Land(x,y,z) // z=x &y as procedure call
if (z>x) y=x+1

Resources