I want to store 3*2^16 9
in $t0
. I did lui $t0,3
and I did addi $t0,$t0,3
. By the way, in the textbook, the value was created through the ori
operation after lui
. Is there something wrong with the way I did it? What is the difference between the two?
CodePudding user response:
What is the difference between the two?
Both ori
and addi
allow for a 16-bit immediate.
However, ori
will zero extend the immediate, so in other words, the immediate is always treated as a positive number, and thus in 32 bits the upper 16 bits of the extended immediate will be zeros.
Whereas addi
will sign extend the immediate, so it is treated as a 16-bit signed value, that could be positive or negative.
Either will work but when using addi
we have to be careful as follows: if the low 16 bits has its sign bit set, then by the nature of sign extension and addition, it will have the effect to decrement the upper portion, so need to add one to the upper immediate. For example, let's say we want to load a constant, 0x23458765. The lui
should be given 0x2346 not 0x2345, then the addi
gets 0x8765, which is negative as a 16-bit signed immediate, and thus, will subtract one from the 0x23460000 provided by the lui
, making it 0x23458765.
The other difference is that addi
will trap on overflow, which here would be a highly undesirable side effect. ori
will not trap on overflow. However, we could use addiu
instead, as that will also not trap on overflow and uses the same 16-bit signed immediate as addi
.
NOTE: the assemblers often do what it looks like you intended rather than exactly what you specify. Using 0x8765 with addi
or addiu
in some assemblers will generate multiple instructions to accommodate that number as positive. If you want to test the above sequences with MARS, for example, we would have to use -30875(10) instead of 0x8765 to get the assembler to generate an immediate of 0x8765, otherwise it will use multiple instructions to build 34661(10). (Using -30875 with ori
will also generate multiple instructions, so for that we have to use 0x8765 or 34661 to get that immediate in one instruction.)
Is there something wrong with the way I did it?
In summary, you can use whatever code sequence you like to build your constants. While some texts will only show one way, others are certainly possible, and, there's no "right" way.
That being said, however, if you are using assembler pseudo functions %hi
and %lo
, those are designed for use with addiu
— meaning that %hi
will do the 1 if the lower 16-bits appear as a negative immediate. These function are used as follows: lui $t0, %hi(label); addiu $t0, $t0, %lo(label)
— where label
is a (code or data) label though can be a 32-bit constant as well. (MARS does not support these functions, so with MARS we use li
or la
pseudo instructions instead, and it will generate one or two instructions as needed.)
The reason for %hi
and %lo
to work this way as that sometimes the sequence uses a load (e.g. lw
) or store (e.g. sw
) instead of an addiu
, and all the loads & stores also work with the same 16-bit signed immediates (so will also have the potential effect of decrementing the upper portion when the low 16 bits turns out negative). (Those sequences (lui
;lw
/sw
) can save an instruction here and there, if the intent was to access a global variable, for example.)