0

I have the following C Code that I have translated into MIPS.

// TEST 1, comment out when running with TEST2
int sx[5] = {1, 2, 3, 4, 5};
int sy[5] = {5, 4, 3, 2, 1};

// TEST 2, uncomment when running with TEST2
//int sx[5] = {1, 2, 3, 4, 5};
//int sy[10] = {1, 0, 1, 0, 1, 0, 1, 0, 1, 0};

void g(int sa, int incx, int incy)
{
  int i, ix, iy;
  
  if (incx == 1 && incy == 1) {
    for (i=0; i<5; i++)
      sy[i] = sy[i] + sa*sx[i];
  }
  else {
    ix = 0, iy = 0;
    for (i=0; i<5; i++) {
      sy[iy] = sy[iy] + sa*sx[ix];
      ix = ix + incx;
      iy = iy + incy;
    }
  }
}

main()
{
  g(2, 1, 1); // TEST1
  //g(2, 1, 2); // TEST2
}

Here is the following MIPS code. Note that the output is correct when compared to output of C code. But were calling conventions used correctly? For academic plagiarism purposes, I am leaving out most of the arithmetic instructions that is done inside the if-then / for-loops.

I also would like to know whether the .end g at the end of my translation is in the right spot. I lost track of it and now I have no clue where it should go (I assume after the nop in g?)

    .global main
    
    .data
## TEST 1
sx: .word 1, 2, 3, 4, 5
sy: .word 5, 4, 3, 2, 1
    
## TEST 2
#sx: .word 1, 2, 3, 4, 5
#sy: .word 1, 0, 1, 0, 1, 0, 1, 0, 1, 0
    
    .text
    .ent main
main:
    ## TEST 1
    li      $a0, 2  # int sa
    li      $a1, 1  # int incx
    li      $a2, 1  # int incy
    
    ## TEST 2
    #li     $a0, 2  # int sa
    #li     $a1, 1  # int incx
    #li     $a2, 2  # int incy
    
    jal     g       # function call to g

loop:
    j   loop
    nop
    .end main
    
    .ent g
    
## void g(int sa, int incx, int incy)
g:
    li      $s0, 0  # int i = 0
    li      $s1, 0  # int ix = 0
    li      $s2, 0  # int iy = 0
    
    ## if (incx == 1 && incy == 1) {
    # for (i=0; i<5; i++)
      # sy[i] = sy[i] + sa*sx[i];
      
    ## if either incx or incy are not equal to 1, branch to L2
    bne $a1, 1, L2
    nop
    bne $a2, 1, L2
    nop
## if-body
L1:
    slti    $t0, $s0, 5     # if $s0 (index i) is less than 5, set $t0 to 1
    beqz    $t0, loop       # if $t0 = 0 (meaning i >= 5), exit if-block
    nop 

### arithmetic code

    sw      $t7, 0($t4)     # loads $t7 into sy[i]
    addi    $s0, $s0, 1     # i++
    j       L1
    nop
## else-body
L2:
    slti    $t0, $s0, 5     # $s0 (index i) is less than 5, set $t0 to 1 
    beqz    $t0, loop       # if index i reaches max, exit to loop
    nop

#### arithmetic code

    sw      $t7, 0($t4)     # $t7 (arithmetic) stored into address for sy[iy]
    addi    $s0, $s0, 1     # i++
    j L2
    nop
    .end g

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Lilnephew
  • 23
  • 2
  • `g()` overwrites s0..2 without saving/restoring them. Unlike t0..9, the `$s` registers are [call-preserved](https://stackoverflow.com/a/56178078/224132) in the standard calling convention, they stand for "temporary" vs. "saved". https://en.wikibooks.org/wiki/MIPS_Assembly/Register_File . Use `$t`, `$a`, and `$v` registers in leaf functions, or things that don't need to survive across a call in non-leaf functions. – Peter Cordes Oct 27 '22 at 04:13
  • 1
    What MIPS system are you writing for? MARS, SPIM? Real MIPS running Linux? I notice you seem to be targeting a MIPS with branch-delay slots, vs. most students with courses using MIPS are actually doing a fake / simplified MIPS like MARS simulates by default, where there are no branch-delay slots. – Peter Cordes Oct 27 '22 at 04:21
  • I haven't seen `.end` directives before; GNU assembler allows something similar, at least for ELF metadata, a `.size` directive. It's normal to do `.size g, . - g` to set the size of `g` equal to the current position (`.`) minus the address of the start of `g`. And yeah, you'd put that after the final instruction of the function. In your case it's a `nop`, since you didn't fill the branch delay slow with the `addi`. I guess there's a `jr $ra` somewhere else? – Peter Cordes Oct 27 '22 at 04:23
  • Your loops are weird, with an unconditional branch at the bottom, instead of doing the normal thing and [making asm that's like a C `do{}while()`](https://stackoverflow.com/questions/47783926/why-are-loops-always-compiled-into-do-while-style-tail-jump). I guess if you're trying to be really literal about translating the C, similarly counting up towards `5` instead of just making a 5-iteration loop like `add $t7, $t7, -1` / `bnez $t7, top_of_loop`, especially in the loop where you're going to increment separate pointers (or integers that you scale to byte offsets every time) – Peter Cordes Oct 27 '22 at 04:26
  • 1
    By far the most weird thing is `loop: j loop` and having code in `g:` jump to it, or if `g` returns, `main` falls into it. So that's not well-structured code, or it's a tailcall to an infinite loop. Most MIPS simulators allow some kind of exit system. – Peter Cordes Oct 27 '22 at 13:09
  • So, a couple things: `loop: j loop` was apart of the template .S file my Professor gave me. Also, this is for a PIC32 MCU from Microchip. Basically, the assignment is to do a direct translation, but I wanted to know what the ‘formal’ translation would be. Are you saying that I shouldn’t have L1 and L2? And instead merge them into g? – Lilnephew Oct 28 '22 at 02:39
  • Your L1: and L2: labels are already part of your implementation of the high-level function `g()`. You have a `g:` label before them, and no other label that's the start of a different C function before them. Normally you'd give them names like `.L1` since they're just local labels inside the function, and `.L` at the start of a label name makes it not appear in the symbol table of the object file (with the GNU assembler anyway). – Peter Cordes Oct 28 '22 at 03:14
  • What you should do is have `g` actually return with `jr $ra`, then your main or top-level code can decide what to do. In this case apparently fall into an infinite loop. That makes sense if you're programming a microcontroller, so there's no OS to exit to. What you've written is basically a `goto` from part of `g()` to some label in another function. – Peter Cordes Oct 28 '22 at 03:15
  • Or you could look at it as having put the infinite loop *inside* `g()` instead of in `main` or tailcalled by `main`, if you think about functions without worrying about their machine code being contiguous or in order. But if you consider that infinite loop block to be part of `g()`, then you'd have to look at it as optimizing away the tail of `main`, based on `g()` being `__attribute__((noreturn))`. But it isn't in the C source. (You `jal g`, but `g` never returns, it unconditionally jumps to a specific place, not wherever it was called from.) – Peter Cordes Oct 28 '22 at 03:19

0 Answers0