A product that does not fit
Two 32-bit numbers multiplied can need 64 bits: 0xFFFFFFFF × 0xFFFFFFFF
is about 1.8 × 10¹⁹. No single register holds that. MIPS’s answer is to
:
the pair of registers you met in §2.1. mult rs,rt reads the operands as
signed, multu rs,rt as unsigned. Neither names a destination.
mult $t2,$t3 from the smoke program (0x014B0018): rd is zero
Where would the destination be, and why is that field empty?
What to notice
R-format, funct 24, two sources in rs and rt. The rd field is 0
because there is nothing to choose: the result always goes to HI and LO. No
general register changes when mult runs.
Four instructions are the only doors to the pair, the :
| instruction | does |
|---|---|
mfhi rd | rd = HI |
mflo rd | rd = LO |
mthi rs | HI = rs |
mtlo rs | LO = rs |
So a multiply that you want to use is two instructions: mult, then
mflo (or mfhi). The smoke program’s mult $t2,$t3; mflo $t4 made 42.
The 64 bits, drawn
Click bits and watch the product split. Both readings are shown.
0xFFFFFFFF × 2, signed and unsigned
Which half changes between the two readings?
-1 (signed) · 0xffffffff · 4294967295 if read unsigned
2 (signed) · 0x00000002
mult (signed): -1 × 2 = -2
-1 (signed) · 0xffffffff · 4294967295 if read unsigned
-2 (signed) · 0xfffffffe · 4294967294 if read unsigned
multu (unsigned): 4294967295 × 2 = 8589934590
1 (unsigned) · 0x00000001
4294967294 (unsigned) · 0xfffffffe
- the 64-bit product in HI and LO
- where signed and unsigned disagree
Click any input bit to flip it.
What to notice
Signed: −1 × 2 = −2, a 64-bit −2, so HI is all ones and LO is
0xFFFFFFFE. Unsigned: 4294967295 × 2 = 8589934590 = 0x1_FFFFFFFE, so HI
is 1 and LO is again 0xFFFFFFFE. The low words agree. Only the
differs.
That is why two instructions exist. The low 32 bits of a product never depend on how the operands are read. The high 32 bits do, because a negative operand sign-extends into 64 bits before the multiply.
Watch the pair change
The runner multiplies the same two registers signed, then unsigned, and
prints all four halves. It pauses before the multu.
The same two registers multiplied signed and unsigned: only HI differs
Where does a 64-bit product go, and what does signed versus unsigned change?
program (address · word · instruction)
| PC → | 0xbfc00000 | 0x3c191000 | _start:lui $t9,0x1000 |
| 0xbfc00004 | 0x37390ff0 | ori $t9,$t9,0xff0 | |
| 0xbfc00008 | 0x2408ffff | li $t0,-1 | |
| 0xbfc0000c | 0x24090002 | li $t1,2 | |
| 0xbfc00010 | 0x01090018 | mult $t0,$t1 | |
| 0xbfc00014 | 0x00005010 | mfhi $t2 | |
| 0xbfc00018 | 0x00005812 | mflo $t3 | |
| 0xbfc0001c | 0xaf2a0004 | sw $t2,4($t9) | |
| 0xbfc00020 | 0x24180020 | li $t8,32 | |
| 0xbfc00024 | 0xaf380000 | sw $t8,0($t9) | |
| 0xbfc00028 | 0xaf2b0004 | sw $t3,4($t9) | |
| 0xbfc0002c | 0xaf380000 | sw $t8,0($t9) | |
| 0xbfc00030 | 0x01090019 | multu $t0,$t1 | |
| 0xbfc00034 | 0x00006010 | mfhi $t4 | |
| 0xbfc00038 | 0x00006812 | mflo $t5 | |
| 0xbfc0003c | 0xaf2c0004 | sw $t4,4($t9) | |
| 0xbfc00040 | 0xaf380000 | sw $t8,0($t9) | |
| 0xbfc00044 | 0xaf2d0004 | sw $t5,4($t9) | |
| 0xbfc00048 | 0xaf200008 | sw $zero,8($t9) |
registers — read · written this tick
console (what the doors printed)
▮tick 0 of 19
Nothing has run yet. The PC (program counter) points at the first instruction.
mult writes no general register: only the HI and LO chips change. mfhi and mflo copy them out. Then multu on the same bits: LO is the same 0xFFFFFFFE, but HI is 1 instead of 0xFFFFFFFF, because 0xFFFFFFFF now means 4294967295, not −1. The putint door prints signed, so LO shows as −2 both times.
What to notice
At each mult or multu, no general register turns green: only the HI and
LO chips change. mfhi and mflo then turn a register green. The console
shows −1 and −2, then 1 and −2: same LO, different HI. The number door prints
signed, which is why LO reads −2 both times.
Before reading on, decide:
$t0 = 6 and $t1 = 7. After `mult $t0,$t1`, what are HI, LO, $t0 and $t1?
Go deeper: why HI and LO exist, and how Carolyne describes them
The original MIPS multiplier took many cycles. Writing its result into the
general file would have blocked the single write port, or needed a second
port. Giving the multiplier its own two output registers let it run beside
the pipeline: the program issued mult, did other work, and fetched the
answer with mflo when it was ready. The pair also solved the 64-bit
problem with no wide register. RISC-V, designed for out-of-order cores with
renaming, dropped the pair and pays with two instructions when both halves
are needed.
Carolyne describes HI and LO as two register classes of one register each,
both renamed like the general file. mult and multu are µops with two
destinations, one in each class; mfhi and mflo read one class through
an operand that carries no index, because a one-register class has nothing
to choose. This is the second-destination path the description was built to
exercise, the same shape x86’s FLAGS will use.
Can you now explain it?
mult and multu write the 64-bit product into HI:LO and name no destination. mflo and mfhi copy the halves into general registers. The low word is the same signed or unsigned; only HI differs.
If you remember only three things
- The product lives in the pair. rd is zero in the word because HI and LO are the only place it can go. Nothing else changes.
- Two instructions to use it. mult then mflo. Forget the mflo and the program silently prints an old value.
- u means unsigned here. Unlike addu, multu really reads its operands as unsigned. LO agrees with mult; HI does not.
Now try to explain this one yourself: mult $t0,$t1 with $t0 = −3 and
$t1 = 5. Give HI and LO. Then give multu’s HI and LO on the same
registers, and say why LO is the same.
Go deeper: a model answer, once you have tried
Signed: −3 × 5 = −15. As 64 bits that is 0xFFFFFFFF_FFFFFFF1: HI =
0xFFFFFFFF, LO = 0xFFFFFFF1. Unsigned: $t0 is 4294967293, times 5 is
21474836465 = 0x4_FFFFFFF1: HI = 4, LO = 0xFFFFFFF1. LO is identical
because the low 32 bits of a product depend only on the low 32 bits of the
operands, which are the same bits either way. HI differs because sign
extension changes the top 32 bits of the operands before the 64-bit
multiply.
Check your understanding
0/5 answered1.After `multu $t0,$t1` with $t0 = 0xFFFFFFFF and $t1 = 2, what is HI?
2.Which instruction brings the low word of a product into a general register?
3.mult is R-format, yet its rd field is always 0. Why does it name no destination?
4.A program executes `mult $t0,$t1` and then immediately prints $t0. What does it print?
5.In what way do mult and multu differ?