-2

I am having some trouble converting C into MIPS. I am currently having trouble converting this C code into MIPS:

int arraySum(int* array, int arraySize)
{
   int result;

   if(arraySize == 0)
      result = 0;
   else
      result = *array + arraySum(&array[1], arraySize-1);

   return result;
}

The current code that I have created in MIPS is here:

    arraySum:
         addi $sp, $sp, -4
         sw $ra, 0($sp)
         
         beqz $a1, rec_done
         li $v0, 0
         j rec
    rec:
        addi $a1, $a1, -1
        jal arraySum
        addi $a1, $a1, 1
    rec_done:
        sll $t2, $a1, 2
        add $t2, $a0, $t2
        lw $t2, 0($t2)
        addu $v0, $t2, $v0

        lw $ra, 0($sp)
        addiu $sp, $sp, 4
        jr $ra

Sorry that it is not commented, I usually type up my comments after the code is finished

  • 1
    Here is what GCC produces for your code: https://godbolt.org/z/vGE699v97 The register names look different but maybe it will help. – Jerry Jeremiah May 04 '21 at 01:46
  • Why is "Compile C to MIPS assembly by hand" such a popular assignment? – Shawn May 04 '21 at 01:54
  • Great question Shawn, I'd like to know it myself, but alas I am clueless. – Ishaan Aggarwal May 04 '21 at 01:56
  • Thank you Jerry for this tip, I will try it out. – Ishaan Aggarwal May 04 '21 at 01:57
  • @Shawn Perhaps they think it will help to gain some understanding how compilers work. Actually I use this knowledge to find compiler errors. And yes, there are such beasts. However, the other way around looks more appropriate to me. Just my $0.02. – the busybee May 04 '21 at 06:20
  • @Shawn Given that you have to specify the assignment somehow, my guess is that (1) C is a standard language, so no ambiguity (unlike prose or pseudocode), (2) students are likely to know C (possibly even required by course prerequisites), and (3) C offers a reference implementation you can test against. – Raymond Chen May 04 '21 at 14:01

1 Answers1

1

You're doing this as homework, and the purpose is not to eliminate or optimize the recursion but to translate the function in the recursive spirit intended by the assignment, so you can learn about stack frames, return addresses, variables live across a call, etc..

The link provided for the compiler example in comments above, have optimized the code so severely that none of the lessons you're supposed to learn remain in the resulting code.  (We all know that recursion is overkill for summing consecutive value of an array, but that's the assignment.)

You're code needs to do something after jal, since at some point (when working properly) it will return to the instruction after the recursive jal.

Something needs to go into $v0 on each return path — there's a difference between the return address (register $ra; sometimes referred to as linkage in older terminology) and the return value (register $v0).  Both are necessary, but they are separate concepts, so don't use/confuse one for the other.  The return address is a pointer to a machine code instruction used to return control of the processor's instruction stream to the caller; whereas the return value here is just an integer value like 0 or the sum, used to return the function answer aka return value to the caller.

Recursion is one function calling another (that turns out to be the same function).  When dealing with calling a function (modulo advanced optimization), we need to preserve values that are defined before and used after a call.

For one, the return address is a parameter value that is needed at the very end of the function and used to return to the caller (jr $ra).

For another, let's look more closely at this expression:

*array + arraySum(&array[1], arraySize-1)

Obviously, we cannot add (+) until the call to arraySum has return an operand.  Therefore, depending on how you code this up, either array or *array is needed after the call.  (It doesn't matter that it is recursive: any call of anything in the same manner will require similar analysis.)

By the C language standard, a C compiler (implementation) is free to dereference array (as in *array) before the call to arraySum or after.  Either way, one more value needs to survive the function call: if the dereference is done beforehand then the value of the dereferenced element needs to be preserved for use by add after the function call, or if the dereference is delayed then the array parameter itself must be preserved in order to do the dereference (and next the addition) after the call.

So, this function requires two items to survive the function call.  One is the return address and the other is either the array parameter or the *array element value. 


Let's arbitrarily choose to dereference the array parameter after the function returns.

Preserving these items is done using stack space — by allocating a stack frame.  Allocating a stack frame implies deallocating it at the end before returning to the caller.  Since this code is expressed without loops, there's no need or advantage to using $s registers here, so the function prologue should simply allocate 2 words of stack space and store the return address there ($ra), and array ($a0).  In order to do the addition after the function call, the value of the array parameter should be reloaded from the stack, dereferenced, and added to the return value ($v0) of the function call.

Function epilogue should reload the return address ($ra) and deallocate the stack space.  (There is no need to restore ($a0) for the caller.)


The other approach is to dereference array before the function call, which means one word of stack space will be used to save the value of the dereference, so it can be recreated after the function call.

Similarly, allocate 2 words of stack space and store the return address in one of the words.  The other word is used in evaluation of the above addition expression, so the dereference happens before the call, and its value is stored in the other stack allocated word.  After the function call, that dereferenced value is restored to a register so that both operands of the add are now in registers and can be summed to $v0.

Epilogue needs to restore the return address, otherwise deallocate stack space, and return to the caller.

Erik Eidt
  • 23,049
  • 2
  • 29
  • 53
  • I have updated my code in the original post. Please take a look at it and see if it is doing what you meant. – Ishaan Aggarwal May 04 '21 at 17:04
  • It is an improvement in saving & restoring the return address, `$ra`. Parameter passing to the recursive call is worse: it needs to pass 2 parameters. Not sure why it is doing `array[arraysize]` as that is not in the C code at all. – Erik Eidt May 04 '21 at 17:43
  • Technically, we shouldn't rely on the argument (`$a`) registers retaining their values after the call, probably get marks off for that, but I don't know your grader. I did suggest using a 2nd word of stack for that instead, which follows the calling convention. – Erik Eidt May 04 '21 at 17:44