'How can I fix my MIPS code for nested for loops?

I am trying to find out where I am going wrong with my MIPS code.

I am tasked with translating the following C code:

for(i=0;i<a;i++)
{
    for(j=0;j<b;j++)
    {
        D[j]=i+j;
    }
}

I have been given the following information about the code: Values of a,b,i,j are stored in $s0,$s1,$t0,$t1 respectively. Also, the base address for the array D is stored in $s2.

Here is my current translation:

LoopOuter:   slt $t2, $t0, $s0 #(i<a), if true then $t2=1
             bne $t2, 0, Exit
LoopInner:   slt $t2, $t1, $s1 #(j<b), if true then $t2=1
             bne $t2, 0, ReturnOuter
             sll $t3, $t1, 2 #Get the offset.
             add $t3, $t3, $s2 #Contains the element D[j]
             add $t4, $t0, $t1 #Add i+j and save to $t5
             sw $t3, $t4 #Save to the D[j]=i+j
             addi $t1, $t1, 1 #j++
             j LoopInner
ReturnOuter: addi $t0, $t0, 1 #i++
             j LoopOuter
Exit:

Where am I going wrong and how do I fix it?

Edit: After reading Eriks amazing reply I have attempted to revise my code taking into account what he told me, here is the new updated code:

LoopOuter:   slt $t2, $t0, $s0
             beq $t2, $zero, Exit
             add $t1, $zero, $zero
LoopInner:   slt $t2, $zero, $s1
             beq $t2, $zero, ReturnOuter

             sll $t3, $t1, 2 #$t3 = 4*j
             add $t4, $t1, $t2
             sw  $t4, $t3($s2) #Save i+j to D[j]

             addi $t1, $t1, 1 #j++
             j LoopInner
ReturnOuter: addi $t0, $t0, 1 #i++
             j LoopOuter
Exit: 

Edit 2: The assumptions for this task are that all values in the required registers already contain the needed data, such that $t0 and $t1 are zeroed.



Solution 1:[1]

The bne instruction, when used after slt, says to branch when the condition is true.  But in assembly language, we often branch away from something, whereas in C we often say when to do or continue something.  Those bnes are being used to exit the loops, so you need to branch when the condition is false, e.g. using beq instead.  If the loop continuation condition is false then branch to exit the loop.


You have skipped some parts of the for loops.

Let's translate them into while loops to make it clear:

i = 0;                      <---- where is this??
while(i<a)
{
    j = 0;                  <---- where is this??
    while(j<b)
    {
        D[j]=i+j;
        j++;
    }
    i++;
}

Do you notice something that's in this both the for loop form and this while loop form, but missing in your assembly?  Check each line.


add $t3, $t3, $s2 #Contains the element D[j]

Just to be clear, this produces the memory address of element D[j].  Though it probably won't affect your program, this should be using addu because pointers are unsigned and pointer arithmetic should use unsigned arithmetic.  Your other adds and addis are ok assuming i & j are signed integers.


sw $t3, $t4 #Save to the D[j]=i+j

Not sure what assembler is allowing this, but (1) the operands are backwards.  $t3 is the address and should be the 2nd operand, and $t4 is the value of i+j and should be the first operand.

And (2) the memory address operand is traditionally written with ()'s surrounding, and sometimes even a leading constant, e.g. ($t3), or, 0($t3).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1