Translating C function with args to MIPS: loop over an int array and count negatives

Except for some missing boilerplate, you were very close. Here’s a version annotated with the bugs:

main:
# NOTE/BUG: a0/a1 are _not_ set up for the call
    jal     countNegatives

# NOTE/BUG: we just fall through into countNegatives again [which is bad]

countNegatives:
    li      $t0,0                   # count = 0
    li      $t1,0                   # i = 0

loop:
    bge     $t1,$a1,endloop
    sll     $t2,$t1,2               # $t2 = 4*i
    add     $t2,$a0,$t2             # $t2 = &table[i]
    lw      $t3,0($t2)              # temp = table[i]
    bge     $t3,$zero,endif
    addi    $t0,$t0,1               # counter++

endif:
    addi    $t1,$t1,1               # i++
# NOTE/BUG: we need to loop here

endloop:
    jr      $ra

Here’s a working version [with the added boilerplate]:

    .data
arr:    .word   10 20 -5 7 -6 0 1 -1 37

    .text
    .globl  main
main:
    la      $a0,arr                 # point to array
    li      $a1,9                   # array count
    jal     countNegatives

    move    $a0,$v0
    li      $v0,1
    syscall

    li      $v0,10
    syscall

# countNegatives -- count number of negatives
#
# RETURNS:
#   v0 -- number of negative numbers found
#
# arguments:
#   a0 -- pointer to array
#   a1 -- array count
#
# temporaries:
#   t1 -- index variable "i"
#   t2 -- array offset / &table[i]
#   t3 -- temp (value of table[i])
countNegatives:
    li      $v0,0                   # count = 0
    li      $t1,0                   # i = 0

loop:
    bge     $t1,$a1,endloop         # i >= count? if yes, fly
    sll     $t2,$t1,2               # $t2 = 4*i
    addu    $t2,$a0,$t2             # $t2 = &table[i]
    lw      $t3,0($t2)              # temp = table[i]
    bge     $t3,$zero,endif
    addi    $v0,$v0,1               # counter++

endif:
    addi    $t1,$t1,1               # i++
    j       loop

endloop:
    jr      $ra

Here’s a just for fun version that uses slt instead of a conditional branch [and eliminates an extra jump inside the loop]:

    .data
arr:    .word   10 20 -5 7 -6 0 1 -1 37

    .text
    .globl  main
main:
    la      $a0,arr                 # point to array
    li      $a1,9                   # array count
    jal     countNegatives

    move    $a0,$v0
    li      $v0,1
    syscall

    li      $v0,10
    syscall

# countNegatives -- count number of negatives
#
# RETURNS:
#   v0 -- number of negative numbers found
#
# arguments:
#   a0 -- pointer to array
#   a1 -- array count
#
# temporaries:
#   t1 -- index variable "i"
#   t2 -- array offset / &table[i]
#   t3 -- temp (value of table[i])
countNegatives:
    li      $v0,0                   # count = 0
    li      $t1,0                   # i = 0
    j       loop_start              # start the loop

loop:
    sll     $t2,$t1,2               # $t2 = 4*i
    addu    $t2,$a0,$t2             # $t2 = &table[i]
    lw      $t3,0($t2)              # temp = table[i]
    slt     $t3,$t3,$zero           # temp = (temp < 0)
    add     $v0,$v0,$t3             # counter += temp

    addi    $t1,$t1,1               # i++

loop_start:
    blt     $t1,$a1,loop            # i < count? if yes, fly

    jr      $ra

Here’s another version that uses pointer arithmetic instead of index variables.

Note that under the mips ABI, only the s* regs must be preserved by callee, so a0 and a1 are used as temporaries.

Also note that when adding addresses/pointers, as good practice, we want to use the unsigned versions of the add instructions (i.e. addu and addiu) to prevent [the unlikely possibility of] an overflow exception.

    .data
arr:    .word   10 20 -5 7 -6 0 1 -1 37

    .text
    .globl  main
main:
    la      $a0,arr                 # point to array
    li      $a1,9                   # array count
    jal     countNegatives

    move    $a0,$v0
    li      $v0,1
    syscall

    li      $v0,10
    syscall

# countNegatives -- count number of negatives
#
# RETURNS:
#   v0 -- number of negative numbers found
#
# arguments:
#   a0 -- pointer to array (ptr)
#   a1 -- array count
#
# temporaries:
#   a1 -- array limit (endp)
#   t3 -- temp (value of table[i])
countNegatives:
    li      $v0,0                   # count = 0
    sll     $a1,$a1,2               # get byte offset
    addu    $a1,$a0,$a1             # endp = &arr[count]
    j       loop_start              # start the loop

loop:
    lw      $t3,0($a0)              # temp = *ptr
    slt     $t3,$t3,$zero           # temp = (temp < 0)
    add     $v0,$v0,$t3             # counter += temp

    addiu   $a0,$a0,4               # ptr += 4

loop_start:
    bne     $a0,$a1,loop            # ptr != endp? if yes, fly

    jr      $ra

So, the final asm version, translated back into C would look something like this:

int
countNegatives(int *table, int n)
{
    int *endp;
    int count = 0;

    endp = &table[n];

    for (;  table != endp;  ++table)
        count += (*table < 0);

    return count;
}

Leave a Comment