0
votes

I have a C code in front of me that I have to translate into a MIPS assembly language.

I am not looking for a direct answer, but I want someone to correct the way I'm thinking about the problem.

The C code in front of me is:

x = ((++z)<=y);

I have in the given that the x, y, and z are respectively stored in the registers $6, $7, $8

The problem is I can't use an operator to compare directly less than or equal. I am limited to use the following comparing operands: bne, beq, ori, slt.

The way I approached the problem was as such:

      addi   $8,$8,1     #this will increment z by 1 to have ++z
      slt    $1,$8,$7    #compares ++z to y if ++z is < than y, it will store 1 in $1
      beq    $8,$7,Label #compares if $8 = $7, if so the code jumps to Label
Label addi   $t0,$0,1    #if ++z = y, stores 1 in $t0
      ori    $6,$t0,$1   #Or's the t0 and t1 and accordingly stores 0 or 1 in x

Is this the right approach to this problem ?

2
Jumping to the very next instruction is pointless in almost every case. - Michael
beq $8,$7,Label / Label ... - Michael
What would you suggest to be done then ? - Ralph
My suggestion regarding that is just that when you write your code you should keep in mind that branching to the next instruction is pointless, since the next instruction will be reached anyway (regardless of whether the branch is conditional or not). - Michael
Aha i think i know where to go from here. Thanks - Ralph

2 Answers

0
votes

As usual, ask a compiler (e.g. on the Godbolt compiler explorer). See also How to remove "noise" from GCC/clang assembly output?


<= alone (without the increment) is best implemented with a reversed slt (to do b<a which is the opposite of a<=b) and then xori to flip the result.

int booleanize_le(int y, int z){
    return (z<=y);
}
# GCC5.4 -O3 -fno-delayed-branch
booleanize_le(int, int):
        slt     $2,$4,$5        # slt retval, y, z
        xori    $2,$2,0x1       # !retval, i.e. flip 0 <-> 1 since it was already boolean
        j       $31                       # return
  # (omitting the NOP in the branch-delay slot)

Fun fact: clang for RISC-V does the same thing, slt/xori, because RISC-V is like MIPS in only providing slt for compare-into-boolean. (Because you can booleanize any relation like ==, <=, >, or whatever into an integer register with at most 2 instructions.)


But in your case where you want to increment, and you're using addi so clearly z must be a signed int (otherwise your code would fault on increment from 0x7fffffffU to 0x80000000U; use addiu if you want well-defined wrap around).

C rules for signed-overflow being undefined behaviour basically match use of MIPS addi, which means compilers will also assume ++z doesn't wrap, and do the optimization we want. They and we we can just use the original z value. (z+1)<=y is the same thing as z<y if z+1 doesn't / can't wrap.

int booleanize_inc_le_signed(int y, int z){
    return ((++z)<=y);
}
booleanize_inc_le_signed(int, int):
        slt     $2,$5,$4             # z<y  before incrementing
        j       $31
    # increment optimized away in this function, but you'd do it with
    addiu   $5, $5, 1

If z had been unsigned, that optimization isn't possible and the compiler does just increment and then use the unsigned version of the 2-instruction <= slt/xori sequence:

int booleanize_inc_le_unsigned(unsigned int y, unsigned int z){
    return ((++z)<=y);
}
booleanize_inc_le_unsigned(unsigned int, unsigned int):
        addiu   $5,$5,1                # z++
        sltu    $2,$4,$5               # y<z (unsigned)
        xori    $2,$2,0x1              # return !(y<z)
        j       $31

Other relations

Exact equality, a == b

    xor     $2,$4,$5          # find bit-differences
    sltu    $2,$2,1           # (a^b) < 1U

Not equal, a != b

    xor     $2,$4,$5
    sltu    $2,$0,$2          # 0U < (a^b)

One integer being non-zero: a!=0, i.e. !!a

    sltu    $2,$0,$4          # 0U < a

Any others can obviously be derived, or just ask a compiler (use the Godbolt link).

-1
votes

As pointed out by Michael, you should have the label one line below like this:

         addi   $8,$8,1
         slt    $1,$8,$7
         beq    $8,$7,Label
         addi   $t0,$0,1
Label    ori    $6,$t0,$1

What's interesting is that you use slt but not seq, why not? You probably could do that:

         addi   $8,$8,1
         slt    $1,$8,$7
         seq    $t0,$8,$7
         ori    $6,$t0,$1

And by avoiding a branch, the program can be slightly faster (assuming that case happens many times.)