Why are there two ways to multiply arbitrary signed numbers in MIPS?
Asked Answered
L

1

6

If you need to multiply two arbitrary signed numbers in MIPS, is there a reason to prefer:

mul $t0 $s0 $s1

Or this:

mult $s0 $s1
mflo $t0

?

I'm finding inconsistent answers online with regard to what each one means. At first glance I would expect the former to be a pseudo-instruction for the latter. (And there's even a web page that claims that.) But looking at the machine code it appears that mult is a valid R-type instruction (opcode 0) whereas mul has a nonzero opcode (0x1c) and so shouldn't be an R-type, even though it contains 3 registers?!

RISC philosophy says to use pseudo-instructions frequently since we only have limited real instructions. But I'm just not thinking of why you'd need two different ways to multiply. Both affect lo and hi (using MARS), so you can check for overflow with either. So why the redundancy? Why not just tell everyone to use mul all the time?

Laws answered 10/10, 2018 at 20:43 Comment(4)
(And before anyone asks: I'm not a student. I'm a teacher trying to understand, to head off student questions.)Laws
"I'm finding inconsistent answers online with regard to what each one means." I'd suggest going by what MIPS32™ Architecture For Programmers Volume II: The MIPS32™ Instruction Set says before looking at any online answers.Tenorrhaphy
FYI there's Computer Science Educators for this purposeIrtysh
@phuclv: That's for questions about how to pass knowledge on to students. This question doesn't belong there, it's a strictly technical question about MIPS multiply instructions, not about how to teach students about them. (The Why not just tell everyone to use mul all the time? is about confusing online search results, not about how to teach students)Kila
F
4

mul is not a pseudo instruction. It does not modify either the hi or lo registers that mult does. They are different real instructions in the instruction set.

In general, we have a = b * c

Since multiplying two 32 bit numbers together produces a 64 bit result, in the general case we use mult and then get the lower 32 bits of the result with mflo and the upper 32 bits with mfhi. This allows greater accuracy at the expense of needing an extra instruction [or two] to get the result.

If we only care about the lower 32 bits of the result of the multiply (e.g. array index calculations), we can use mul which allows the result to be in a different register from the arguments (in a single instruction)

Consider a simple program:

    .text
    .globl  main
main:
    mul     $v0,$a0,$a1
    mult    $v1,$a2
    mflo    $v0

Now, if we assemble it using mars, we get:

00400000:   70851002    mul     $v0,$a0,$a1
00400004:   00660018    mult    $v1,$a2
00400008:   00001012    mflo    $v0

Notice that we have a real mflo instruction on line 3. If mul were a pseudo-op, mars would [have to] inject an mflo $v0 between the mul and mult lines


UPDATE:

That's interesting. And you're right about it not being a pseudo-instruction. (You'd see that when it was assembled, if it were.) But when I use MARS, both mul and mult modify hi and lo. Maybe this is a MARS bug?

Possibly. spim also modifies hi and lo.

Upon further reflection, this seems logical given the era of the original mips CPU cores (circa 1985) and the [extremely] limited number of gates they had.

But, real mips cores are still alive today. The company is "MIPS Technologies, Inc" and it still existed as of 2017.

The ISA reference manual from the company [AFAICT] has a copy here: https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00086-2B-MIPS32BIS-AFP-6.06.pdf

In that document, the mul instruction does not list changing hi or lo as a side effect.

In some document I've seen [I can't recall which], it states [for old/real hardware] that you have to have an intervening instruction between the mult and an mflo (e.g. a nop). The simulators don't require this.

As good practice, I probably wouldn't rely on lo/hi being valid for too long after mult and not rely on them at all for mul, so, for class work, it's a bit of a moot point.

It would be interesting to see what qemu does. It's harder to use than spim or mars [which I prefer] but may be closer to what actual hardware does.

Fullmouthed answered 10/10, 2018 at 21:16 Comment(6)
That's interesting. And you're right about it not being a pseudo-instruction. (You'd see that when it was assembled, if it were.) But when I use MARS, both mul and mult modify hi and lo. Maybe this is a MARS bug?Laws
this looks similar to single operand imul reg and multi-operand imul reg, reg[, imm] in x86Irtysh
Original MIPS (R2000) had a multiply unit that was only loosely connected to the rest of the integer core. (See doc.lagout.org/electronics/doc/mips/See%20mips%20run.pdf). Since it took many cycles to multiply (or divide), and they wanted integer register modifications to be complete by the WB stage in the 5-stage pipeline, something had to give. Having special instructions to read/write lo/hi let mult be super slow and not have to interact with the normal register file except for reading inputs. (Which aren't allowed to be modified for a few insns after running mult!)Kila
Later MIPS that could interlock instructions on GP integer registers (and had longer pipelines and much higher performance multiply units) were able to usefully introduce a new multiply instruction that improved code-density and reduced overhead of mflo instructions.Kila
@AdamSmith - that's interesting indeed. The "digital design and computer architecture" book (by Harris and Harris) book refers to mul as a pseudo instruction.Bice
@AdamSmith. Also: the ISA reference manual suggests that lo/hi are unpredictable after mul, so the MARS implementation may be correct. I just don't get why the textbook suggests that it's a pseudo instruction (together with less authoritative references online, e.g. mathcs.holycross.edu/~csci226/MIPS/SPIM.pdf).Bice

© 2022 - 2024 — McMap. All rights reserved.