简体   繁体   中英

C to MIPS translation for 2D array and bitwise operation

Im trying to translate the below piece of C code to MIPS, but don't know how to translate the part grid[row][col] |= IS_RVLD_MASK; what is the MIPS code for the above?

grid[row][col]

the above is declared as an int

void reveal_grid(void) {
  for (int row = 0; row < N_ROWS; row++) {
    for (int col = 0; col < N_COLS; col++) {
      grid[row][col] |= IS_RVLD_MASK;
    }
  }
}

My MIPS code

loop0:
    bge $t1, N_ROWS, end0   # if (row >=  N_ROWS) goto end0;

    li $t2, 0              # $t2 = col = 0
loop1:
    bge $t2, N_COLS, end1   # if (col >= N_ROWS) goto end1;

    la   $t0, grid        #  grid[row][col] |= IS_RVLD_MASK;
    mul  $s1, $t1, 20
    add  $s2, $s1, $t0
    mul  $t3, $t2, 4
    add  $t4, $t3, $s2

end1:

    add $t1, $t1, 1     # row++
    b loop0

end0:
    jr $31

Let's decompose this statement:

m |= constant;

This in a category called an assignment operator, and these are expanded like this:

m = m | constant
^   ^
|   |
|   +--- source/read
|
+-- target/written

So, whereas regular assignment ( = ) only writes, these assignment operators both read and write target expression on the left-hand side. ( col++ is also a read-modify-write operation, and can be thought of as col += 1 as well as col = col + 1 ). Once understood, these assignment operators are handy and succinct, and also help in that we don't have to repeat a complex expression, such as the array indexing in your example.

The | operator stands for logical or . In C this operator will perform the logical or operation on every single bit of the two inputs to produce an output of the same size, so for int of 4 bytes large, will perform 32 individual "or" operations, position by position.

"Or"ing with a constant is used to set corresponding bits — where there is a 1 in the constant, the output for that bit will be 1, and where there is a 0 in the constant, the output for that bit will be the value of the other input (at that bit position).

What this is saying is: take the current value of m , then OR in the constant, and write that value to become the new current value of m .

MIPS has an or instruction and and ori instruction.

If the variable m is in a register, this can be done in one instruction, assuming that the constant will fit into 16-bit immediate form (and otherwise, a programmer, in some cases the assembler, may convert that into two or three instructions, one or two extra to manifest the full constant value, then the MIPS or instruction). That or instruction will both source and target the register for m .

However, if the variable, m is in memory, then, because MIPS is a load/store machine, we have to load a copy of m into a register, then do the or operation (as described above), and then store it back into memory to update the variable where it is living.

Arrays — more specifically all their elements — are necessarily in memory, so they will need this full treatment. You already have a computation to obtain the address of the element.


Most would use a shift: sll $t3, $t2, 2 to multiply by 4 as this can be cheaper on lower-end hardware.

Don't forget to initialize row=0; at the start, instead of relying on the simulator to have cleared the registers.

(And don't forget to increment col++ .)

The la of grid can be moved to outside of and before the loop as one optimization, since the value it produces doesn't change during the loop's execution. (This is called loop invariant code motion .) If you do that, just don't clobber $t0 in another part of the code, as long it is still needed.

There are other optimizations possible in assembly, such as doing the first multiplication ( row times column size) in the outer loop instead of in the inner loop, since within the inner loop, the row variable doesn't change, and so that is a constant value for each execution of the inner loop.

Each of these code motion optimizations requires the usage of an additional CPU register (for the duration of some loop), which is one reason why MIPS and other newer processors have so many registers.

(Further optimizations are also possible by changing from array indexing to pointers, and then from re-computing the element's byte address, to maintaining that address as a variable that is live throughout the loop(s).)

Suggest sticking with $t registers in general, there's 10 of them so plenty for your code. ( $s registers are supposed to be saved & restored when used, they are used by code that makes function calls, so if the body of your loop had a function call as part of it or as part of the assignment statement, we would choose $s registers for row and col and anything else we want to both be in registers and survive the function call.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM