2

I am learning about MIPS and I am trying to express the codes written in C language with MIPS.

Among them, when I studied the registers that should be preserved, I thought that I decided whether to preserve them by simply considering the types of registers.

int leaf_example (int g, int h, int i , int j)
{
    int f;
    f = (g+h)-(i+j);
    return f;
 }

For example, there is the above code, and there are conditions that g~j is $a0~a3, f is $s0, and the return value is stored in $v0.

At this time, because of the condition in question, f is stored in $s and $s needs to be preserved, so I understood that the value of $s0 was stored on the stack and then $s was restored later.

void sort(int v[],int n)
{
    int i, j;
    for (i = 0 i < n; i++){
        for (j = i - 1; j >= 0 && v[j] > v[j + 1]; j-=1){
            swap(v, j);
        }
    }
}

However, when there is a code like the one above, it is expressed in MIPS as follows:

image

Why is it that the two values ​​that come in as arguments of the sort() function are stored separately and then used when they are not stored in $s? I don't know how to determine which registers should be preserved.

In the code above, I want to know why you need to preserve $a0 $a1. That said, I'd really appreciate it if you could tell me how to distinguish which values ​​should be preserved in the code.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • If you need your regsiters args (or some other values) after a call, you save them somewhere so you don't lose your only copy. See [What are callee and caller saved registers?](https://stackoverflow.com/a/56178078) (That's inconvenient terminology: call-preserved vs. call-clobbered are clearer). In leaf functions, you only use call-preserved registers (like `$s0..` regs) if you run out of call-clobbered regs (like `$t0..9`), so it choosing `$s0..7` for an `int f` local var in a leave function is a bad (inefficient) choice forcing you to save/restore the caller's `$s0` value. – Peter Cordes Oct 02 '21 at 00:15
  • Does this answer your question? [What are callee and caller saved registers?](https://stackoverflow.com/questions/9268586/what-are-callee-and-caller-saved-registers) - I think my answer there does cover everything you're asking about. Also related: [MIPS registers preservation](https://stackoverflow.com/q/19284198) – Peter Cordes Oct 02 '21 at 00:24
  • Thank you so much I didn't understand even though I wasted a lot of time, but thanks to you I solved it –  Oct 02 '21 at 00:26

1 Answers1

4

In C or pseudo code, we have logical variables that have limited lifetime, that come and go by scope (e.g. local variables and parameters), whereas in assembly we have physical storage.  When we translate an algorithm into assembly language, we must map the logical variables into physical storage.

The particular storage that is chosen for variables must meet their lifetime and usage requirements.  By a convention of software, MIPS subdivides the registers (physical storage), into call preserved and call clobbered registers.

What we need to do is an analysis on the variables and how they are used.  You want to answer the question of: for each variable, is the value it holds defined before a function call, and used after one.  If the answer is yes for any variable, then that variable must be stored in a preserved location that will survive the function call, and such locations are either $s registers or (local) stack memory.  But either way, memory is involved, directly in the latter case, and in former case with the usage of $s registers, those registers themselves must be preserved in order to follow the convention.

The $s registers are advantageous and desirable if the code uses the variables many times (by dynamic count), as is often the case with loops.  Otherwise if the variables are used only once (or on a dynamic path unexpected or unimportant for performance), for example, memory may be a better location for them than an $s register.


In the first function, it would be silly to use an $s register for local variable ff should go in $v0.  Why?  Two reasons:

  • $v0 is available, and won't be clobbered by anything this function does from the first use of f to the last, and,
  • the function ends with return f;, which means that at the end of the function, f's value needs to be in $v0.

So, putting it there in the first place makes sense, and avoids the overhead of using $s registers, plus no need to copy into $v0 at the end.


In the second function, it is calling swap inside the loop, and so, then

  • the function call is assumed to wipe out all argument registers (which are part of the call clobbered set)
  • sort itself is going to wipe out $a0 and $a1 as the parameter passing part of making that function call.

The variables i, j, v, and n are all defined before the function call and used after.  i++ is both a usage and another definition of i — the variable is expected to survive the function call.  Same for j--v and n are both defined upon function entry and repeatedly used, so obviously they must also survive the swap function call (or else a next iteration of the loop won't work).

Since these 4 variables are used rather frequently, $s registers are good candidates, as the alternative, memory, would require insertions of additional loads and stores into the bodies of the loops.

The trade off is of doing save & restore to $s registers once each in prologue and epilogue vs. doing loads & stores in the body of the loop, and if the loop executes more than once that is a good trade.


When we map logical variables into physical storage, those are not necessarily permanent, so sometimes a logical variable "lives" (is mapped to) different physical storage in one part of the code than another.

Erik Eidt
  • 23,049
  • 2
  • 29
  • 53
  • Thank you very much for the detailed reply. I think I almost understand. If so, $a0, which came in as an argument of sort, is used in swap. Since the value of $a0 is deleted at the end of the swap function, it is right to save $a0 in the $s register, which stores the value regardless of the function call, and then call it before the next swap call. ? –  Oct 02 '21 at 08:38
  • Yes, the variable `v` is moved from `$a0` where it starts, into an `$s` register. Of course, the `$s` register's original value must be saved on entry and (before that move of `v` from `$a0`), and restored on exit of `sort`, since some caller suspended and waiting for the `sort` function to complete, may already be using that `$s` register. It is by this agreement of behavior that the `$s` registers survive a function call. – Erik Eidt Oct 02 '21 at 12:17
  • @ErikEidt: Should I reopen this question? It's not an exact duplicate, and maybe I was hasty to close it, especially now that you've written an answer that approaches it from this angle which fits more closely with this question. – Peter Cordes Oct 02 '21 at 19:22
  • @PeterCordes, ok sure, why not? – Erik Eidt Oct 02 '21 at 21:20