2015-10-05

MIPS, part 2: ALU instructions

Here are some common instructions in MIPS I and their effects. Some of those effects are important for optimization purposes, so they'll be described in greater detail.

These instructions are common on the Sony PlayStation. Counter-intuitively, they are also much more common than 64-bit instructions on the Nintendo 64: because its data bus is 32-bit, loading and storing 64-bit quantities to memory would take twice as long, so most games were written with 32-bit data and instructions only. Some games could even be run on a pure 32-bit MIPS processor!

You'll want to read MIPS part 1: Registers and calling convention first.

In this post: Arithmetic logic unit (ALU) instructions!

ADD: 32-bit addition with signed overflow check (#add)

ADD Rd, Rs, Rt ; Rd <- Rs + Rt

Adds the two 32-bit quantities in registers Rs and Rt, storing the result in register Rd. On 64-bit MIPS processors, ADD sign-extends the 32-bit result by copying bit 31 into 32..63.

ADD raises the Arithmetic Overflow exception if adding two positive integers, or two negative integers, and the sign of the result has been flipped. If that happens, the Rd register is not written.

ADDU: 32-bit unsigned addition (#addu)

ADDU Rd, Rs, Rt ; Rd <- Rs + Rt

Adds the two 32-bit quantities in registers Rs and Rt, storing the result in register Rd. Despite the nature of this instruction as an "unsigned" addition, on 64-bit MIPS processors, ADDU sign-extends the 32-bit result by copying bit 31 into 32..63.

ADDU does not raise any exceptions.

In programs, ADDU is often used with $0, whose value is always 0, for two effects: ADDU Rd, Rs, $0 ; Rd <- Rs (32-bit move; sign-extends on 64-bit) ADDU Rd, $0, $0 ; Rd <- 0

ADDI: 32-bit addition with immediate, with signed overflow check (#addi)

ADDI Rt, Rs, Imm16 ; Rt <- Rs + Imm16

Adds the 32-bit quantity in register Rs and the value in Imm16 sign-extended to 32 bits. Stores the result in register Rt. On 64-bit MIPS processors, ADDI sign-extends the 32-bit result by copying bit 31 into 32..63.

If bit 15 of the immediate is set, the immediate is negative. Some MIPS assemblers recognize the pseudo-instruction SUBI for this.

ADDI raises the Arithmetic Overflow exception if adding two positive integers, or two negative integers, and the sign of the result has been flipped. If that happens, the Rd register is not written.

ADDIU: 32-bit unsigned addition with immediate (#addiu)

ADDIU Rt, Rs, Imm16 ; Rt <- Rs + (int16_t) Imm16

Adds the 32-bit quantity in register Rs and the value in Imm16 sign-extended to 32 bits. Stores the result in register Rt. On 64-bit MIPS processors, ADDIU sign-extends the 32-bit result by copying bit 31 into 32..63.

If bit 15 of the immediate is set, the immediate is negative. Some MIPS assemblers recognize the pseudo-instruction SUBIU for this.

ADDIU does not raise any exceptions.

Despite its name, ADDIU sign-extends its immediate. The word unsigned in the description only refers to skipping the signed overflow check. This code, for example: ADDIU $4, $4, 65535 really subtracts 1 from the value of $4, due to 65535 being the two's complement representation of -1.

AND: Bitwise and (#and)

AND Rd, Rs, Rt ; Rd <- Rs & Rt

Performs the bitwise and operation on the values in registers Rs and Rt, storing the result in register Rd. On 64-bit MIPS processors, AND performs the operation on all 64 bits. However, the last operation that wrote the values in Rs and Rt may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.

In programs, AND is often used to mask away bits. For example, if one of the operands is 0x1FFFFF, only bits 0..20 may be set in the result. This can also be used to restrict values to a certain range or test whether a certain bit is set.

ANDI: Bitwise and with immediate (#andi)

ANDI Rt, Rs, Imm16 ; Rt <- Rs & (uint16_t) Imm16

Performs the bitwise and operation on the value in register Rs and the 16-bit immediate, zero-extended all the way, storing the result in register Rt. On 64-bit MIPS processors, ANDI performs the operation on all 64 bits. However, bits 16..63 will always be unset in the result.

In programs, ANDI is often used to mask away bits. For example, if the immediate is 0x00FF, only the low byte may be set in the result. This can also be used to restrict values to a certain range or test whether a certain bit is set.

DIV: 32-bit signed division (#div)

DIV Rs, Rt ; (HI, LO) <- (int32_t) Rs / (int32_t) Rt

Divides the 32-bit quantity in register Rs by the 32-bit quantity in register Rt, interpreted as signed integers.

Results are written to the multiplier unit's registers, HI and LO. The quotient goes to LO and the remainder goes to HI. Both are signed as well. On 64-bit MIPS processors, DIV sign-extends the 32-bit results by copying bit 31 of each register into its bits 32..63.

Unlike on other architectures, DIV does not raise any exceptions. If Rt contains the value 0, the contents of HI and LO become undefined. The division of -2147483648 by -1 (= +2147483648), whose result can't be represented in 32 bits, also makes HI and LO undefined.

DIVU: 32-bit unsigned division (#divu)

DIVU Rs, Rt ; (HI, LO) <- (uint32_t) Rs / (uint32_t) Rt

Divides the 32-bit quantity in register Rs by the 32-bit quantity in register Rt, interpreted as unsigned integers.

Results are written to the multiplier unit's registers, HI and LO. The quotient goes to LO and the remainder goes to HI. Both are unsigned as well. Despite the nature of this instruction as an "unsigned" division, on 64-bit MIPS processors, DIVU sign-extends the 32-bit results by copying bit 31 of each register into its bits 32..63.

Unlike on other architectures, DIVU does not raise any exceptions. If Rt contains the value 0, the contents of HI and LO become undefined.

LUI: Load upper immediate (#lui)

LUI Rt, Imm16 ; Rt <- Imm16 << 16

Shifts the immediate value left by 16 bits (i.e. into the upper part of 32-bit registers) and writes it to register Rt. On 64-bit MIPS processors, LUI sign-extends this 32-bit result by copying bit 31 into 32..63. It does not instead shift the immediate value by 48 bits.

The lower 16 bits are filled with 0. Therefore, none of the bits that were in register Rt are preserved.

In programs, LUI is often paired with ADDIU or ORI to make 32-bit constants. LUI can also be used with load and store instructions, which support 16-bit offsets from these upper parts, to access constant addresses in memory.

MFHI: Move from HI (#mfhi)

MFHI Rd ; Rd <- HI

Retrieves into register Rd the last result written to the multiplier unit's HI register by a preceding DIV, DIVU, MULT or MULTU instruction.

Due to the pipelining in early MIPS processors, it is not possible to grab the value in the HI register then immediately request another DIV, DIVU, MULT or MULTU. The multiplication or division request has already been seen and put into the pipeline 2 cycles ago, and it may have already written partial results into HI and LO.

For example, this instruction sequence is illegal: MFHI $8 DIVU $4, $5 because the DIVU has already started.

Two unrelated instructions (or NOP, if no meaningful work can be scheduled) must appear between MFHI and the next request: MFHI $8 NOP NOP DIVU $4, $5

MFLO: Move from LO (#mflo)

MFLO Rd ; Rd <- LO

Retrieves into register Rd the last result written to the multiplier unit's LO register by a preceding DIV, DIVU, MULT or MULTU instruction.

The notes on MFHI also apply to MFLO.

MULT: 32-bit signed multiplication (#mult)

MULT Rs, Rt ; (HI, LO) <- (int32_t) Rs * (int32_t) Rt

Multiplies the 32-bit quantities in registers Rs and Rt, interpreted as signed integers.

Results are written to the multiplier unit's registers, HI and LO, exactly as if the signed 64-bit result was chopped into HI (bits 63..32) and LO (bits 31..0). If the width of the result is 32 or less, the HI register will be filled with copies of bit 31 of LO. On 64-bit MIPS processors, MULT further sign-extends the chopped 32-bit results by copying bit 31 of each register into its bits 32..63.

MULTU: 32-bit unsigned multiplication (#multu)

MULTU Rs, Rt ; (HI, LO) <- (uint32_t) Rs * (uint32_t) Rt

Multiplies the 32-bit quantities in registers Rs and Rt, interpreted as unsigned integers.

Results are written to the multiplier unit's registers, HI and LO, exactly as if the unsigned 64-bit result was chopped into HI (bits 63..32) and LO (bits 31..0). If the width of the result is 32 or less, the HI register will be 0. Despite the nature of this instruction as an "unsigned" multiplication, on 64-bit MIPS processors, MULTU further sign-extends the chopped 32-bit results by copying bit 31 of each register into its bits 32..63.

NOR: Bitwise not-or (#nor)

NOR Rd, Rs, Rt ; Rd <- ~(Rs | Rt)

Performs the bitwise or operation on the values in registers Rs and Rt, then toggles all bits of the result and stores it in register Rd. On 64-bit MIPS processors, NOR performs the operation on all 64 bits. However, the last operation that wrote the values in Rs and Rt may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.

In programs, NOR is often used with $0, whose value is always 0, for two effects: NOR Rd, Rs, $0 ; Rd <- ~Rs (toggle all bits) NOR Rd, $0, $0 ; Rd <- -1

OR: Bitwise or (#or)

OR Rd, Rs, Rt ; Rd <- Rs | Rt

Performs the bitwise or operation on the values in registers Rs and Rt, storing the result in register Rd. On 64-bit MIPS processors, OR performs the operation on all 64 bits. However, the last operation that wrote the values in Rs and Rt may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.

In programs, OR is often used with $0, whose value is always 0, for two effects: OR Rd, Rs, $0 ; Rd <- Rs (32- or 64-bit move) OR Rd, $0, $0 ; Rd <- 0

ORI: Bitwise or with immediate (#ori)

ORI Rt, Rs, Imm16 ; Rt <- Rs | (uint16_t) Imm16

Performs the bitwise or operation between the value in register Rs and the immediate, zero-extended, storing the result in register Rt. On 64-bit MIPS processors, ORI performs the operation on all 64 bits. However, the last operation that wrote the values in Rs and Rt may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.

In programs, ORI is often after LUI to make the lower half of 32-bit constants, or to set bits in the value of a register.

SLL: Shift left logical (#sll)

SLL Rd, Rt, Imm5 ; Rd <- Rt << Imm5

Shifts the bits in register Rt to the left (i.e. towards the most significant bit) by the number of bits in the immediate. Bits that would be shifted into bit 32 and up are discarded. The lower bits are filled with 0. The result is written to register Rd. On 64-bit MIPS processors, SLL sign-extends the 32-bit result by copying bit 31 into 32..63.

In programs, SLL is often used to multiply values by powers of 2. Paired with SRA, it can sign-extend the lower bits of a register. Paired with SRL, it can mask away the high bits of a register.

SLLV: Shift left logical variable (#sllv)

SLLV Rd, Rt, Rs ; Rd <- Rt << (Rs & 0x1F)

Shifts the bits in register Rt to the left (i.e. towards the most significant bit) by the number of bits contained in the low 5 bits of register Rs. Bits that would be shifted into bit 32 and up are discarded. The lower bits are filled with 0. The result is written to register Rd. On 64-bit MIPS processors, SLLV sign-extends the 32-bit result by copying bit 31 into 32..63.

SLT: Set to signed less-than (#slt)

SLT Rd, Rs, Rt ; Rd <- (int) Rs < (int) Rt

If the value in register Rs is less than the value in register Rt, if both are interpreted as signed integers, writes 1 into register Rd. Otherwise, writes 0. On 64-bit MIPS processors, SLT performs the operation on all 64 bits. However, the last operation that wrote the values in Rs and Rt may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.

SLTU: Set to unsigned less-than (#sltu)

SLTU Rd, Rs, Rt ; Rd <- (unsigned int) Rs < (unsigned int) Rt

If the value in register Rs is less than the value in register Rt, if both are interpreted as unsigned integers, writes 1 into register Rd. Otherwise, writes 0. On 64-bit MIPS processors, SLTU performs the operation on all 64 bits. However, the last operation that wrote the values in Rs and Rt may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.

SLTI: Set to signed less-than immediate (#slti)

SLTI Rt, Rs, Imm16 ; Rt <- (int) Rs < (int16_t) Imm16

If the value in register Rs is less than the sign-extended immediate, if both are interpreted as signed integers, writes 1 into register Rt. Otherwise, writes 0. On 64-bit MIPS processors, SLTI performs the operation on all 64 bits. However, the last operation that wrote the value in Rs may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.

SLTIU: Set to unsigned less-than immediate (#sltiu)

SLTIU Rt, Rs, Imm16 ; Rt <- (unsigned int) Rs < (unsigned int) (int16_t) Imm16

If the value in register Rs, interpreted as an unsigned integer, is less than the sign-extended immediate also reinterpreted as an unsigned integer, writes 1 into register Rt. Otherwise, writes 0. On 64-bit MIPS processors, SLTIU performs the operation on all 64 bits. However, the last operation that wrote the value in Rs may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.

Despite its name, SLTIU sign-extends its immediate before comparing it as an unsigned integer. This code, for example: SLTIU $1, $4, -16384 really compares the value of register $4 with the value 4294950912. On 64-bit MIPS processors, it will be even more than that.

SRL: Shift right logical (#srl)

SRL Rd, Rt, Imm5 ; Rd <- (uint32_t) Rt >> Imm5

Shifts the bits in register Rt to the right (i.e. towards the least significant bit) by the number of bits in the immediate. Bits that would be shifted below bit 0 are discarded. The upper bits are filled with 0. The result is written to register Rd. On 64-bit MIPS processors, SRL sign-extends the 32-bit result by copying bit 31 into 32..63. Therefore, if the shift amount is greater than 0, bits 32..63 will become 0.

In programs, SRL is often used to divide unsigned values by powers of 2. Paired with SLL, it can mask away the high bits of a register.

SRLV: Shift right logical variable (#srlv)

SRLV Rd, Rt, Rs ; Rd <- (uint32_t) Rt >> (Rs & 0x1F)

Shifts the bits in register Rt to the right (i.e. towards the least significant bit) by the number of bits contained in the low 5 bits of register Rs. Bits that would be shifted below bit 0 are discarded. The upper bits are filled with 0. The result is written to register Rd. On 64-bit MIPS processors, SRLV sign-extends the 32-bit result by copying bit 31 into 32..63.

SRA: Shift right arithmetic (#sra)

SRA Rd, Rt, Imm5 ; Rd <- (int32_t) Rt >> Imm5

Shifts the bits in register Rt to the right (i.e. towards the least significant bit) by the number of bits in the immediate. Bits that would be shifted below bit 0 are discarded. The upper bits are filled with copies of bit 31 in register Rt. The result is written to register Rd. On 64-bit MIPS processors, SRA also copies bit 31 into 32..63.

In programs, SRA is often used to divide signed values by powers of 2. Paired with SLL, it can sign-extend the lower bits of a register.

SRAV: Shift right arithmetic variable (#srav)

SRAV Rd, Rt, Rs ; Rd <- (int32_t) Rt >> (Rs & 0x1F)

Shifts the bits in register Rt to the right (i.e. towards the least significant bit) by the number of bits contained in the low 5 bits of register Rs. Bits that would be shifted below bit 0 are discarded. The upper bits are filled with copies of bit 31 in register Rt. The result is written to register Rd. On 64-bit MIPS processors, SRLV sign-extends the 32-bit result by copying bit 31 into 32..63.

SUB: 32-bit subtraction with signed overflow check (#sub)

SUB Rd, Rs, Rt ; Rd <- Rs - Rt

Subtracts the 32-bit quantity in register Rt from that of register Rs, storing the result in register Rd. On 64-bit MIPS processors, SUB sign-extends the 32-bit result by copying bit 31 into 32..63.

SUB raises the Arithmetic Overflow exception if subtracting a positive value from a negative value, or vice versa, and the sign of the result has been flipped. If that happens, the Rd register is not written.

SUBU: 32-bit unsigned subtraction (#subu)

SUBU Rd, Rs, Rt ; Rd <- Rs - Rt

Subtracts the 32-bit quantity in register Rt from that of register Rs, storing the result in register Rd. Despite the nature of this instruction as an "unsigned" addition, on 64-bit MIPS processors, SUBU sign-extends the 32-bit result by copying bit 31 into 32..63.

SUBU does not raise any exceptions.

XOR: Bitwise exclusive-or (#xor)

XOR Rd, Rs, Rt ; Rd <- Rs ^ Rt

Performs the bitwise exclusive-or operation on the values in registers Rs and Rt, storing the result in register Rd. On 64-bit MIPS processors, XOR performs the operation on all 64 bits. However, the last operation that wrote the values in Rs and Rt may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.

In programs, XOR is often used to toggle bits in the value of a register.

XORI: Bitwise exclusive-or with immediate (#xori)

XORI Rt, Rs, Imm16 ; Rt <- Rs ^ (uint16_t) Imm16

Performs the bitwise exclusive-or operation on the value in register Rs and the immediate, zero-extended, storing the result in register Rt. On 64-bit MIPS processors, XORI performs the operation on all 64 bits. However, the last operation that wrote the value in Rs may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.

In programs, XORI is often used to toggle bits in the value of a register.

2 comments :

  1. senquack (Dan Silsby)October 5, 2015 at 9:38 AM

    Great reference, I've printed it out as it is the clearest explanation I've found, so thank you.

    I think in the explanations for ADDU, NOR, and OR, when you mention how sometimes '$0 is used twice', it would be clearer to say, instead, that sometimes '$0 is used for both instruction parameters'.

    Also, I was wondering why the instructions that take immediates are listed with Rt as the destination register, instead of Rd like all the others.

    ReplyDelete
    Replies
    1. I'll admit that "$0 is used twice" wasn't the clearest thing for those instructions. I updated the post to show the uses of $0 with annotated instruction examples. You might want to print out the new post, and it should be the last one... I hope!

      As for the destination register of instructions with immediates being Rt, that's because the encoding of operands on MIPS is very regular.

      * Rs is encoded in bits 21..25.
      * Rt is encoded in bits 16..20.
      * Rd is encoded in bits 11..15.
      * Imm16 is encoded in bits 0..15.

      Instructions with Imm16 can't use Rd because they'd overlap, so they use Rt instead.

      Hope that helped!

      Delete