andi vs. addi instruction in MIPS with negative immediate constant

assembly, immediate-operand, mips, sign-extension, zero-extension

Solution

Your expectation is correct, but your interpretation of your experimental results is not

$t2 becomes 0x0005550 This is confirmed by the MIPS emulator.

No, this is incorrect. So, one of the following:

- Somehow, you're misreading what the emulator is doing. The actual value from the emulator is what you expected it to be.

- Or, you don't have `0x55555550` in `$t2` before the `andi` as you assume, but `0x5550` instead (i.e.) your test program doesn't set up `$t2` correctly.

However, it is not what I expected. I think the answer should be 0x55555550 & 0xFFFFFFFF = 0x55555550. I think the constant -1 was sign extended to 0xFFFFFFFF before the and logic.

Yes, this is correct. And, I'll explain what is happening and why below.

But it appears that the answer was 0x55555550 & 0x0000FFFF. Why -1 is sign extended to 0x0000FFFF instead of 0xFFFFFFFF

It wasn't. It was sign extended to `0xFFFFFFFF`. Again, you're reading the experimental results incorrectly [or your test program has a bug].

`mips` simulators and assemblers have pseudo ops.

These are instructions that may or may not exist as real, physical instructions. However, they are interpreted by the assembler to generate a sequence of physical/real instructions.

An example of a "pure" pseudo-op is `li` ("load immediate"). It has no corresponding instruction, but usually generates a two instruction sequence: `lui`, `ori` (which are physical instructions).

Pseudo-ops should not be confused with assembler directives, such as `.text`, `.data`, `.word`, `.eqv`, etc.

Some pseudo-ops can overlap with actual physical instructions. That is what is happening with your example.

In fact, the assembler examines any given instruction as a potential pseudo-op. It may determine that in can fulfill the intent with a single physical instruction. If not, it will generate a 1-3 instruction sequence and may use the [reserved] `$at` register [which is `$1`] as part of that sequence.

In `mars`, to see the actual real instructions, look in the `Basic` column of the source window.

For the sake of the completeness of my answer, all that follows is prefaced by the top comments.

I've created three example programs:

- The `addi` as in your original post

- The `andi` as in your corrected post

- An `andi` that uses an unsigned argument

(1) Here is the assembler source for your original question using `addi`:

    .text
    .globl  main
main:
    li      $t2,0x55555550
    addi    $t3,$t2,-1
    nop

Here is how `mars` interpreted it:

 Address    Code        Basic                     Source

0x00400000  0x3c015555  lui $1,0x00005555         4     li      $t2,0x55555550
0x00400004  0x342a5550  ori $10,$1,0x00005550
0x00400008  0x214bffff  addi $11,$10,0xffffffff   5     addi    $t3,$t2,-1
0x0040000c  0x00000000  nop                       6     nop

`addi` will sign extend its 16 bit immediate, so we have `0xFFFFFFFF`. Then, doing a two's complement add operation, we have a final result of `0x5555554F`

Thus, the assembler didn't need to generate extra instructions for the `addi`, so the `addi` pseudo-op generated a single real `addi`

(2) Here is the `andi` source:

    .text
    .globl  main
main:
    li      $t2,0x55555550
    andi    $t3,$t2,-1
    nop

Here is the assembly:

 Address    Code        Basic                     Source

0x00400000  0x3c015555  lui $1,0x00005555         4     li      $t2,0x55555550
0x00400004  0x342a5550  ori $10,$1,0x00005550
0x00400008  0x3c01ffff  lui $1,0xffffffff         5     andi    $t3,$t2,-1
0x0040000c  0x3421ffff  ori $1,$1,0x0000ffff
0x00400010  0x01415824  and $11,$10,$1
0x00400014  0x00000000  nop                       6     nop

Whoa! What happened? The `andi` generated three instructions.

A real `andi` instruction does not sign extend its immediate argument. So, the largest unsigned value we can use in a real `andi` is `0xFFFF`

But, by specifying `-1`, we told the assembler that we did want sign extension (i.e. `0xFFFFFFFF`)

So, the assembler could not fulfull the intent with a single instruction and we get the sequence above. And the generated sequence could not use `andi` but had to use the register form: `and`. Here is the `andi` generated code converted back into more friendly asm source:

    lui     $at,0xFFFF
    ori     $at,$at,0xFFFF
    and     $t3,$t2,$at

As to result, we're anding `0x55555550` and `0xFFFFFFFF` which is a [still unchanged] value of `0x55555550`

(3) Here is the source for an unsigned version of `andi`:

    .text
    .globl  main
main:
    li      $t2,0x55555550
    andi    $t3,$t2,0xFFFF
    nop

Here is the assembler output:

 Address    Code        Basic                     Source

0x00400000  0x3c015555  lui $1,0x00005555         4     li      $t2,0x55555550
0x00400004  0x342a5550  ori $10,$1,0x00005550
0x00400008  0x314bffff  andi $11,$10,0x0000ffff   5     andi    $t3,$t2,0xFFFF
0x0040000c  0x00000000  nop                       6     nop

When the assembler sees that we're using a hex constant (i.e. the `0x` prefix), it tries to fulfill the value as an unsigned operation. So, it doesn't need to sign extend. And, the real `andi` can fulfill the request.

The result of this is `0x5550`

Note that if we had used a mask value of `0x1FFFF`, that would be unsigned. But, it's larger than 16 bits, so the assembler would generate a multi-instruction sequence to fulfill the request.

And, the result here would be `0x15550`

Problem

Assume $t2=`0x55555550`, then executing the following instruction: ``` andi $t2, $t2, -1 ``` $t2 becomes `0x0005550` This is confirmed by the MIPS emulator1 However, it is not what I expected. I think the answer should be 0x55555550 & 0xFFFFFFFF = 0x55555550. I think the constant -1 was sign extended to 0xFFFFFFFF before the and logic. But it appears that the answer was 0x55555550 & 0x0000FFFF Why -1 is sign extended to 0x0000FFFF instead of 0xFFFFFFFF Footnote 1: Editor's note: MARS with "extended pseudo-instructions" enabled does expand this to multiple instructions to generate `0xffffffff` in a tmp register, thus leaving `$t2` unchanged. Otherwise MARS and SPIM both reject it with an error as not encodeable. Other assemblers may differ.

Original source