C to MIPS Code translation problem with nested procedure - c

I need to tranlate piece of C code
int main(){
int a, b, result;
if(a == b)
result = a*b;
else
result = assess(a, b);
return result;
}
int assess(int a, int b){
if(b<a)
return upgrade(a, b);
else
return demote(a, b);
}
int upgrade(int a, int b)
{return 4*(a+b);}
int demote(int a, int b)
{return 4*(b-a);}
a and b will be tested for a=8 b=8 a=3 b=5 a=5 b=3
here is what i tried
.text
main:
add $s0,$s0,5
add $s1,$s1,3
add $s3,$s3,0
beq $s0,$s1,Resultmul
bne $s0,$s1,assess
li $v0, 10
syscall
assess:
addi $sp,$sp,-8
sw $s3,0($sp)
sw $ra,4($sp)
jal upgrade
lw $ra,4($sp)
add $sp,$sp,4
jr $ra
Resultmul :
mul $s3,$s1,$s0
li $v0, 10
syscall
upgrade:
add $s3,$s0,$s1
mul $s3,$s3,4
jr $ra
demote:
sub $v0,$s1,$s0
mul $v0,$v0,4
jr $ra
But it gets stuck in jr $ra in the assess procedure can someone fix this issue that would be great.

You are branching to assess instead of calling it like a function via jal. Thus, there is no proper value in $ra upon the entry to assess for it use upon completion to return to main.
You are (almost) properly saving $ra and restoring it later, but it never had a good value in the first place, so the save & restore (which will be needed) doesn't help yet.
You should pop as many bytes off the stack as you push — you're pushing 8 but popping only 4.
You are also not restoring $s3 though you do save it.
You might consider $ra as a parameter passed to a function, and inspect its value upon function entry and during function execution to see where becomes incorrect. The value passed to the callee should be the address of the return point in the caller — a code address.

Related

Recursive Fibonacci MIPS

I started to read MIPS to understand better how my C++ and C code works under the computer skin. I started with a recursive function, a Fibonacci function.
The C code is:
int fib(int n) {
if(n == 0) { return 0; }
if(n == 1) { return 1; }
return (fib(n - 1) + fib(n - 2));
}
MIPS code:
fib:
addi $sp, $sp, -12
sw $ra, 8($sp)
sw $s0, 4($sp)
addi $v0, $zero, $zero
beq $a0, $zero, end
addiu $v0, $zero, 1
addiu $t0, $zero, 1
beq $a0, $t0, end
addiu $a0, $a0, -1
sw $a0, 0($sp)
jal fib #fib(n-1)
addi $s0, $v0, $zero
lw $a0, 0($sp)
addiu $a0, $a0, -1
jal fib #fib(n-2)
add $v0, $v0, $s0
end:
lw $s0, 4($sp)
lw $ra, 8($sp)
addi $sp, $sp, 12
jr $ra
When n>1 it goes until the code reaches the first jal instruction. What happens next? it return to fib label ignoring the code below (the fib(n-2) call will never be executed?)? If that happens, the $sp pointer decreases 3 words again and the cycle will go until n<=1. I can't understand how this works when first jal instruction is reached.
Can you follow how the recursion works in C?
In some sense, recursion has two components: the forward part and the backward part.  In the forward part, a recursive algorithm computes things before the recursion, and in the backward part, a recursive algorithm computes things after the recursion completes.  In between the two parts, there is the recursion.
See this answer: https://stackoverflow.com/a/71551098/471129
Fibonacci is just slightly more complicated as it performs recursion twice, not just once as in the above list printing example.
However, the principles are the same:  There is work done before the recursion, and work done after (either of which can be degenerate).  The before part happens as code in front of the recursion executes, and the recursion builds up stack frames that are placeholders for work after the recursion yet to be completed.  The after part happens as the stack frames are released and the code after the recursive call is executed.
In any given call chain, the forward part goes until n is 0 or 1, then the algorithm starts returning back to the stacked callers, for whom the backward part kicks in unwinding stack frames until it returns to the original caller (perhaps main) rather than to some recursive fib caller.&npsp; Again, complicated by use of two recursive invocations rather than one as in simpler examples.
With fib, the work done before is to count down (by -1 or -2) until reaching 0 or 1.  The work done after the recursion is to sum the two prior results.  The recursion itself effectively suspends an invocation or activation of fib with current values, to be resumed when a recursive call completes.
Recursion in MIPS algorithm is the same; however, function operations are spread out over several machine code instructions that are implicit in C.
Suggest single stepping over a call to fib(2) as a very small example that may help you see what's going on there.  Suggest first doing this in C — single step until the outer fib call has full completed and returned to the calling test function (e.g. main).
To make the C version just a bit easier to view in the debugger you might use this version:
int fib(int n) {
if (n == 0) { return 0; }
if (n == 1) { return 1; }
int fm1 = fib(n-1);
int fm2 = fib(n-2);
int result = fm1 + fm2;
return result;
}
With that equivalent C version, you'll be able to inspect fm1, fm2, and result during single stepping.  That will make it easier to follow.
Next, do the same in the assembly version.  Debug single step to watch execution of fib(2), and draw parallels with the equivalents in C.
There's another way to think about recursion, which is ignore the recursion, pretending that the recursive call is to some unrelated function implementation that just happens to yield the proper results of the recursive function; here's such a non-recursive function:
int fib(int n) {
if (n == 0) { return 0; }
if (n == 1) { return 1; }
int fm1 = fibX(n-1); // calls something else that computes fib(n-1)
int fm2 = fibX(n-2); // "
int result = fm1 + fm2;
return result;
}
With this code, and the assumption that fibX simply works correctly to return proper results, you can focus strictly on the logic of one level, namely, the body of this fib, without considering the recursion at all.
Note that we can do the same in assembly language — though the opportunities for errors / typos are always much larger than in the C, since you still have to manipulate stack frames and preserve critical storage for later use after the calling.
The code you've posted has a transcription error, making it different from the C version.  It is doing the C equivalent of:
return fib(n-1) + fib(n-1);

MIPS Assembly how to store values to an array?

I am trying to create a program where I can store up to 8 values in an array and then compare all these values to find the smallest number. For some reason my loop overwrites the first position in the array every time. Here's what I have. I then add 4 to $t1 on the loop so once it goes back around it should store the next integer in the space after that. I don't see what I'm doing wrong here?
.data
myArray: .space 32
Msg1: .asciiz "Enter an integer: "
.text
main:
# Print Message
li $v0, 4
la $a0, Msg1
syscall
# Prompt the user to enter an integer
li $v0, 5
syscall
# Store the first integer in $t0
move $t0, $v0
# Declare $t1 for the array position that the integer will be stored at
addi $t1, $zero, 0
# Store the integer in the array
sw $t0, myArray($t1)
#Add 4 to $t1 so store the next value in the next array position
addi $t1, $zero, 4
beq $t0, $zero, Exit
j main
Exit:
# Declare an exit to the program
li $v0, 10
syscall
First, let's start with a working algorithm in C:
int a [] = { /* array elements */ };
int n = /* count of number of elements */;
...
int currMin = 0;
for ( int i = 0; i < n; i++ ) {
int next = a[i]; // next array element to check
if ( next < currMin ) // is it smaller than what we've seen so far?
currMin = next; // yes: capture new min value
}
// on exit from the loop currMin holds the min value
Ok, now we'll make some simple logical transformations on the way to taking this to assembly language.  First, we remove the for loop in favor of the slightly simpler while loop construct.
int currMin = 0;
int i = 0;
while ( i < n ) {
int next = a[i]; // next array element to check
if ( next < currMin ) // is it smaller than what we've seen so far?
currMin = next; // yes: capture new min value
i++;
}
// on exit from the loop currMin holds the min value
Next, we'll transform the while loop into assembly's if-goto-label.  (We could work the if-then first instead; the order we transform doesn't matter.)
int currMin = 0;
int i = 0;
loop1:
if ( i >= n ) goto endLoop1;
int next = a[i]; // next array element to check
if ( next < currMin ) // is it smaller than what we've seen so far?
currMin = next; // yes: capture new min value
i++;
goto loop1;
endLoop1:
// on exit from the loop currMin holds the min value
Next, we'll do the if-then statement.  We could have done it first, that wouldn't change the analysis or results.
int currMin = 0;
int i = 0;
loop1:
if ( i >= n ) goto endLoop1;
int next = a[i]; // next array element to check
if ( next >= currMin ) goto endIf1;
currMin = next; // yes: capture new min value
endIf1:
i++;
goto loop1;
endLoop1:
Next we'll take this to assembly language.
First, assign variables to physical storage, here good choice is registers.  Mental map:
$a0 array a
$a1 element count n
$v0 currMin, the result
$t0 loop control variable i, also used as index
$t1 temporary variable "next"
Second, translate code as per the last above:
li $v0, 0 # currMin = 0
li $t0, 0 # i = 0
loop1:
bge $t0, $a1, endLoop1
# array reference, variable index: a[i], capture in "next"/$t1
sll $t9, $t0, 2
add $t9, $a0, $t9
lw $t1, 0($t9)
# the if-then inside the loop body
bge $t1, $v0, endIf1
move $v0, $t1 # capture newly seen lowest value
endIf1:
# finish the rest of the while loop, having the for-loop i++ here
addi $t0, $t0, 1
j loop1
endLoop1:
All that's left is to put some starting and ending code around that, assuming the register numbers match up.
The starting code for this would put the address of the array into $a0, and the count of elements into $a1.  Could use different registers of course, with appropriate modifications.
The ending code should expect the result in $v0, to print or otherwise.
The starting code would be entirely before this code, and the ending code entirely after.
Yes, there are a few steps — but each one is a relatively simple and logical transformation.  Logical transformations enable translating the C code, first staying in C but simplifying to make it easy to go right to assembly.
Remove for loops in favor of while loops.
Change all control structures into if-goto-label
Each control structure (if, while) can be changed one at a time, stay in C, and keep checking that the code continues to work in C!  Order of control structure transformations doesn't matter (inside out, outside in).
Translate simplified C code into assembly:
a. Map logical variables of C into physical storage of machine code
b. Convert statements & expressions from C into assembly language

MIPS assembly: Why can main exit without deallocating its stack space?

I have a question for a university exercise that I don't understand. We have to translate from C to assembly MIPS. In the main I have to allocate 400 bytes for the a[100] vector, but in the solutions my professor is not deallocating it at the end of the function, why is this happening? are there cases in which I don't need to deallocate memory moving stack pointer?
Here's the code in C:
int idamax(int n, float * dx, int incx) {
float dmax;
int i, ix, itemp;
if (n < 1) return (-1);
if (n == 1) return (0);
if (incx != 1) {
ix = 1;
dmax = fabs(dx[0]);
ix = ix + incx;
for (i = 1; i < n; i++) {
if (dmax < fabs(dx[ix])) {
itemp = i;
dmax = fabs(dx[ix]);
}
ix = ix + incx;
}
} else {
itemp = 0;
dmax = fabs(dx[0]);
for (i = 1; i < n; i++) {
if (dmax < fabs(dx[i])) {
itemp = i;
dmax = fabs(dx[i]);
}
}
}
return (itemp);
}
int main() {
float a[100];
int l, k, n = 100, lda = 10;
for (k = 0; k < n; ++k) a[k] = (float)((k * k * k) % 100);
k = 4;
l = idamax(n - lda * k - k, &a[lda * k + k], 1) + k;
print_int(l);
exit;
}
Main assembly code:
main:
#______CALL_FRAME______
# 100 float: 400B
#______Totale 400B
addi $sp,$sp,-400
add $t9,$sp,$0 #&a
addi $t0, $0, 100 #n=100
addi $t1, $0, 10 #lda=10
#l in t2, k in t3
add $t3, $0, $0 #k=0
main_forini:
slt $t5,$t3,$t0 #k<?n
beq $t5,$0,main_forend
mult $t3, $t3 #k*k
mflo $t5
mult $t3, $t5
mflo $t5 #k*k*k
div $t5,$t0 #()%n
mfhi $t5
mtc1 $t5,$f0
cvt.s.w $f1,$f0 #(float)()
sll $t5,$t3,2 #k*4
add $t5,$t5,$t9 #&a[k]
swc1 $f1,0($t5) #a[k]=()
addi $t3, $t3, 1 #++k
j main_forini
main_forend:
addi $t3,$0,4 #k=4
mult $t1,$t3 #lda*k
mflo $t5
add $t5,$t5,$t3 #lda*k+k
sub $a0,$t0,$t5 #a0=n-lda*k-k
sll $t5,$t5,2
add $a1,$t5,$t9 #a1=&a[lda*k+k]
addi $a2,$0,1 #a2=1
jal idamax
addi $a0,$v0,4 #a0=l=retval+k
addi $v0,$0,1 #print_int
syscall
addi $v0,$0,10 #exit
syscall
Execution of main never reaches the bottom of the function so cleanup of the stack never needs to happen; exit() is a "noreturn" function.
If main did want to return with jr $ra instead of making an exit system call, you would need to restore the stack pointer along with other call-preserved registers. Otherwise you'd be violating the calling convention that main's caller expects main to follow.
(Updated since you added asm to the question that uses a MARS system call: that main is probably not a function if it's the top of your code: $ra isn't a valid return address on entry so it couldn't return. IMO don't call it main if it's not a function.)
The OS doesn't care where the user-space stack pointer is pointing when the process makes an exit system call, so there's no need for main to clean up before exiting.
(In a "normal" C implementation, the exit() function would compile to a jal exit or a simple tailcall j exit. But you're compiling by hand for the MARS simulator which has no C library, so you inline system calls instead of calling wrapper functions.)
Also note that ISO C exit(int) takes an arg, like MARS exit2 (syscall/$v0=17). In fact you didn't even call exit() as a function, you just wrote exit; in C which evaluates the exit as a function pointer without calling it or doing anything with that value.
Typically C main is called by CRT startup code that might for example run C library init functions and put argc and an argv[] pointer in the right registers. So main is usually not the actual process entry point from the OS, especially not in a hosted implementation. (i.e. compiled C programs run under an OS, rather than being their own kernel like a freestanding program.)
If you're just translating this for the MARS or SPIM simulators or something, then there is no C library or any code beyond what you write, so what you're writing is what would normally be called _start, not main.
In C main is a function, but in MARS you can't jr $ra from the top-level entry point so the entry point is not a function. Thus don't call it main.
In ISO C it's even legal for main to call itself recursively, or other functions to call main. That can only work if main truly is a function that cleans up the stack and returns properly. But that means it can't also be the process entry point that needs to make an exit system call. To run a program with a crazy recursive main that eventually does a C return statement (or falls off the end of main), main pretty much has to be compiled to a real function that can return with jr $ra. So it has to be a function that you jal main to from your _start entry point.
There are two possible answers here.
The first answer is that main is the first and last function of your program. The OS will clean up afterwards.
The second answer would be for other functions that use stack memory. Stack memory is generally freed by restoring the stack frame of the calling function (which main doesn't have, hence the exception).

Writing a C program to call another program without using any built-in libraries

I am trying to write a simple 'go-command' for boot-loader that takes me to specific address in RAM say 0x18000000 and it should execute a program that blinks led. I have two .c files say led.c and go.c in which led.c blinks two leds. But I am wondering and don't know that how can I pass a control/invoke its main() to this go.c file to go to that address and start blinking leds? But it should be done without including other header files, libraries, etc. Kindly help me!! Thanks in advance. Below code is for led.c
void delay ()
{
volatile int i;
for(i=0;i<1000000;i++)
{}
}
int main(void)
{
*led0=0;
*led1=0;
while(1)
{
*led0=1;
delay();
*led0=0;
*led1=1;
delay();
*led1=0;
}
}
In my go.c file I want to pass a control to invoke this led.c main() func
There is a lot you have omitted that is likely to be relevant, but taken at face value, you simply declare a function pointer, initialised with the absolute address and invoke the function through that pointer.
However it may not be that simple since the called program will use the C run-time environment of the calling program; any global static objects will not be initialised - you need to call the program entry point (which is notmain()) to establish the run-time environment expected by the program. In simple case it may work or appear to work to some extent.
GCC has an extension that allows jumping to an arbitrary address, so if you know the address of your led.c main you could do something like that:
void *func_ptr = (void *)0x1234567; // address of your led routine
goto *func_ptr;
However, you probably don't have the address of the led routine and it is not a very safe operation. Jumping to some unknown address will probably result in a crash!!
So I have an sifive riscv board, going to be a little different than yours but here is an led blinker program (compiled version):
Disassembly of section .text:
80001000 <_start>:
80001000: 80004137 lui x2,0x80004
80001004: 016000ef jal x1,8000101a <notmain>
80001008: 9002 ebreak
8000100a: a001 j 8000100a <_start+0xa>
8000100c <dummy>:
8000100c: 8082 ret
8000100e <PUT32>:
8000100e: 00b52023 sw x11,0(x10)
80001012: 8082 ret
80001014 <GET32>:
80001014: 00052503 lw x10,0(x10)
80001018: 8082 ret
8000101a <notmain>:
8000101a: 1101 addi x2,x2,-32
8000101c: c84a sw x18,16(x2)
8000101e: 10012937 lui x18,0x10012
80001022: 00890513 addi x10,x18,8 # 10012008 <_start-0x6ffeeff8>
80001026: 006805b7 lui x11,0x680
8000102a: ce06 sw x1,28(x2)
8000102c: ca26 sw x9,20(x2)
8000102e: c64e sw x19,12(x2)
80001030: cc22 sw x8,24(x2)
80001032: 3ff1 jal 8000100e <PUT32>
80001034: 00c90513 addi x10,x18,12
80001038: 006805b7 lui x11,0x680
8000103c: 3fc9 jal 8000100e <PUT32>
8000103e: 04090513 addi x10,x18,64
80001042: 4581 li x11,0
80001044: 001e84b7 lui x9,0x1e8
80001048: 37d9 jal 8000100e <PUT32>
8000104a: 006809b7 lui x19,0x680
8000104e: 0931 addi x18,x18,12
80001050: 48048493 addi x9,x9,1152 # 1e8480 <_start-0x7fe18b80>
80001054: 85ce mv x11,x19
80001056: 854a mv x10,x18
80001058: 3f5d jal 8000100e <PUT32>
8000105a: 4401 li x8,0
8000105c: 8522 mv x10,x8
8000105e: 0405 addi x8,x8,1
80001060: 3775 jal 8000100c <dummy>
80001062: fe941de3 bne x8,x9,8000105c <notmain+0x42>
80001066: 4581 li x11,0
80001068: 854a mv x10,x18
8000106a: 3755 jal 8000100e <PUT32>
8000106c: 4401 li x8,0
8000106e: 8522 mv x10,x8
80001070: 0405 addi x8,x8,1
80001072: 3f69 jal 8000100c <dummy>
80001074: fe941de3 bne x8,x9,8000106e <notmain+0x54>
80001078: bff1 j 80001054 <notmain+0x3a>
the disassembly spacing is a little broken but thats okay. The entry point IS NOT AT MAIN() for a normal program it is at the start in this case 0x80001000 NOT 0x8000101A which I intentionally name something other than main on purpose (for bare metal)...There should be no reason for you to to enter at main you should enter at the entry point...I will let you simply fail if you continue to try otherwise, you are on your own with that.
So this represents an srec of the above.
S00F00006E6F746D61696E2E737265631F
S3158000100037410080EF006001029001A0828023209A
S31580001010B500828003250500828001114AC83729E0
S31580001020011013058900B705680006CE26CA4EC68C
S3158000103022CCF13F1305C900B7056800C93F1305E7
S3158000104009048145B7841E00D937B709680031097C
S3158000105093840448CE854A855D3F014422850504F4
S315800010607537E31D94FE81454A85553701442285AF
S30F800010700504693FE31D94FEF1BFFD
S705800010006A
I/we assume you are not actually downloading that to ram as is, that will never execute, your bootloader has to parse that then write the program to ram, two different things (the program itself and a file format that describes that program).
Assuming you get to that point, all you need to do is branch to 0x80001000 in my case or 0x18000000 in yours.
so taking the answer already provided to you you can do this to launch the downloaded program
void hop ( void )
{
void *func_ptr = (void *)0x80001000;
goto *func_ptr;
}
which results in
Disassembly of section .text:
00000000 <hop>:
0: 800017b7 lui x15,0x80001
4: 8782 jr x15
Or my personal preference would be:
.globl HOP
HOP:
jr x11
which from C I would call with
HOP(0x80001000);
that way I can insure the instruction I wanted is used. YMMV.
How far along this path have you gotten? Are you stuck at the last step? Simone provided an answer that should work just fine, there are other variations on that theme from C that you can use, but that one already appears to work.

How to calculate the sum of even integers between two values in MIPS?

I am confused on how to convert C code to MIPS. I seem to to get the loops confused and I think I am possibly using the wrong command. The C code I made to do this is as follows:
int main()
{
int x, y;
int sum = 0;
printf("Please enter values for X and Y:\n ");
scanf("%d %d",&x,&y);
if (x > y)
{
printf("\n** Error");
exit(0);
}
while (x <= y)
{
if (x%2 == 0)
sum += x;
x++;
}
printf("\nThe sum of the even integers between X and Y is: %d\n\n",sum);
return 0;
}
My attempt at the MIPS translation is as follows:
.data
Prompt: .asciiz "Please enter values for X and Y:\n"
Result: .asciiz "The sum of the even integers between X and Y is: \n"
.text
li $v0,4 #load $v0 with the print_string code.
la $a0, Prompt #load $a0 with the message to me displayed
syscall
li $v0,5 #load $v0 with the read_int code for X
syscall
move $t0,$v0
li $v0,5 #load $v0 with the read_int code for Y
syscall
move $t1, $v0
while:
slt $t2, $t1,$t0 #$t1 = y $t0 = x
li $t3,2
div $t2,$t3
beq $t2,$0,else
add $s1,$s1,$t0 #s1 = s1 + x
addi $t0,$t0,1 #x++
j while
else:
li $v0,4
la $a0, Result
syscall
move $a0,$s1
li $v0,1
syscall
I think my error is in the loop in my MIPS code. My result keeps producing zero and I think my code is checking the loop and then just jumping to my else statement.
After further work, I got it to calculate the sum of all integers and I'm not exactly sure why it is doing so. Here is my most recent update:
while:
sle $t2, $t0,$t1 #$t1 = y $t0 = x
li $t3,2 #t3 = 2
div $t2,$t3 #$t2/2
beq $t2,$0, else #if ($t2/2 == 0), jump to the else, otherwise do else
add $s1,$s1,$t0 #s1 = s1 + x
addi $t0,$t0,1 #x++
j while
So now, if I enter 1 and 5, it calculates 1 and 3 is gives me 6 instead of just the even sum which should be just 2.
To answer my own question, the main confusion was with the the branches. I now understand that they kind of work like opposites so for example, I had to set the "beq" in my while loop to bnez so it would do the calculations when $t2 was != 0. Another minor fix was adding the increment outside of the loop. So, when $t2 != 0, I jump to my "else" which then incremented to find the next number. However, if the remainder was 0, it did the math of sum=sum + x. In conclusion, the main confusion came from thinking opposite about the branches. I now understand that if I wanted to say:
while(a1 < a2)
I would have to write it as
while:
bgeu $a1,$a2, done
addi "whatever"
b while
done:
do done stuff
Before this understanding, I was writing it as ble $a1,$a2,done and that is not the way it is to be typed. Logically, that says if a1 < a2...but it is really saying if a1 < a2, jump to the "done" and skip calculations. So I just had to think opposite.

Resources