26-bit immediates are only in jump instructions, and aren't sign- or zero-extended to 32 bit, because they're not displacements to be added/subtracted.
I-type instructions with 16-bit immediates are different.
addi
/ addiu
immediates are sign-extended (by duplicating the top/sign bit of the immediate to all higher bits).
https://en.wikipedia.org/wiki/Two%27s_complement#Sign_extension
This allows 2's complement numbers from -2^15
.. +2^15-1
to be encoded.
(0xFFFF8000 to 0x00007FFF)
ori
/andi
/xori
boolean immediates are zero-extended (by setting all higher bits to zero)
This allows unsigned / 2's complement numbers from 0 .. 2^16-1
to be encoded.
(0x00000000 to 0x0000FFFF)
For other instructions see this instruction-set reference which breaks down each instruction showing 016 || [I15..0] for zero-extension or [I15]16 || [I15..0] for sign-extension.
This makes it possible to use 16-bit immediates as inputs to a 32-bit binary operation that only makes sense with 2 equal-width inputs. (In a simple classic MIPS pipeline, the decode stage fetches operands from registers and/or immediates. Register inputs are always going to be 32-bit, so the ALU is wired up for 32-bit inputs. Extending immediates to 32-bit means the rest of the CPU doesn't have to care whether the data came from an immediate or a register.)
Also sign-extended:
- offsets in the reg+imm16 addressing mode used by
lw
/sw
and other load/store instructions
- relative branches (
PC += imm16<<2
)
maybe others, check the manual for instructions I didn't mention to see if they sign- or zero- extend.
You might be wondering "why does addiu
sign-extend its immediate even though it's unsigned?"
Remember that there's no subiu
, only addiu
with a negative immediate. Being able to add or subtract numbers in the range -2^15 .. +2^15-1
is more useful than only being able to add 0 .. 2^16-1
.
And usually you don't want to raise an exception on signed overflow, so normally compilers use addu
/ addiu
even on signed integers. addu
is badly named: it's not "for unsigned integers", it's just a wrapping-allowed / never-faulting version of add
/addi
. It sort of makes sense if you think of C, where signed overflow is undefined behaviour (and thus could use add
and raise an exception in that case if the compiler wanted to implement it that way), but unsigned integers have well-defined overflow behaviour: base 2 wraparound.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…