You're looking at GCC output, using syntax that GAS and clang will accept. Assembly source syntax is defined by the tool and can differ from what the vendor uses in their ISA manuals. The manuals nail down how the binary machine-code works, but different assemblers/disassemblers can vary the syntax if they choose.
divu $zero, src1, src2
is the GAS / clang syntax for the bare machine instruction, with implicit outputs in LO (quotient) and HI (remainder). In MARS/SPIM, divu srcr1, src2
is how you'd write that; MARS/SPIM and other MIPS simulators with built-in assemblers are more like classic MIPS assemblers, not the GNU toolchain. (TODO: test what MARS accepts, and check the machine code.)
With a first operand other than $zero
, it's a destination for a pseudo-instruction. Clang expands it to trap on division by zero and mflo
into the destination. (I don't have MIPS Binutils to test with.)
For example, (keeping in mind this is clang so it's targeting real commercial MIPS with a branch-delay slot, not MARS's default of a simplified MIPS with branch delay disabled.)
$ clang -Wall -target mips -march=mips32r5 -O2 -c mips-div.s
$ llvm-objdump -d mips-div.o
...
00000000 <quot>:
# divu $v0, $4, $5 in the asm source assembled to
0: 14 a0 00 02 bnez $5, 12 <quot+0xc> # skip over the break 7 on $a1!=0
4: 00 85 00 1b divu $zero, $4, $5 # in the branch-delay slot of bnez
8: 00 07 00 0d break 7 <quot>
c: 00 00 10 12 mflo $2 # $v0
# end of pseudocode expansion for divu $v0, $4, $5
...
34: 00 85 00 1b divu $zero, $4, $5 # same source syntax
Strangely, clang at least treats div $4, $5
as div $4, $4, $5
, expanding it, instead of just assembling it to the bare 2-operand machine instruction. But clang supports this as a general pattern: addu $a0, $a1
assembles to addu $4, $4, $5
as if you'd written addu $a0, $a0, $a1
. Or as if MIPS supported a 2-operand "destructive" form that updates the destination. So with the 2-operand syntax for divu
, it seems this expansion takes precedence over the actual machine instruction.
Again, seems like a strange design choice for clang, or more likely for the GNU assembler many years ago. But given that fact, they did need a syntax that unambiguously indicates the bare machine instruction. They chose a $zero
destination.
It is an R-type instruction, and the field in the machine instruction that would normally the destination is required to be zero for div
and divu
. So perhaps this makes text parsing / formatting easier in the assembler and disassembler.
The compiler knows how to lay out branches itself, and can schedule mflo
where appropriate, so it uses the divu $zero, src, src
version.
MIPS32/64 r6 has a 3-operand machine instruction, replacing the old
The first section of this answer is about MIPS before that, from classic MIPS I to MIPS32/64 r5.
In MIPS32r6 / MIPS64r6, divu
is a 3-operand instruction with a different opcode than before. It writes the quotient to the first operand. (LO and HI don't exist, mflo
/mfhi
were removed in R6.)
# clang -target mips -march=mips32r6 div-mips.s && llvm-objdump -d div-mips.o
00 85 00 9b divu $zero, $4, $5 # $a0 / $a1 discarding the result
In earlier revisions of MIPS, divu
is documented as a 2-operand instruction where the implicit destination is LO (quotient) and HI (remainder). This form was removed in Release 6.
# clang -target mips -march=mips32r5
00 85 00 1b divu $zero, $4, $5 # same in the asm source
MIPS32/64 r6 in 2014 reorganized some opcodes as well as removing some rarely used ones, and is not binary compatible in general.
Current versions of tools that you're using, like GCC, post-date that. But MARS and SPIM haven't been updated for it AFAIK, and most people wouldn't want them to be unless it was an optional feature. Typical university courses that use MIPS or a MIPS-like ISA for teaching are more like MIPS I or II from the mid to late 1980s.
But I suspect clang is just being compatible with GNU Binutils, and that it's been using the 3-operand $zero
form to mean the bare instruction since long before 2014 when MIPS32/64 r6 was new. But I'd be curious if anyone has MIPS Binutils, especially an old version, that could test assembling / disassembling.
See also the MIPS64r6 ISA manual from mips.com. I'm looking at v6.06, page 194 from 2016 for the entry for the new form of divu
. (I don't expect any further updates to MIPS manuals; the ISA is commercially dead-ended.)
Availability and Compatibility:
These instructions are introduced by and required as of Release 6.
Release 6 divide instructions have the same opcode mnemonic as the pre-Release 6 divide instructions (DIV, DIVU,
DDIV, DDIVU). The instruction encodings are different, as are the instruction semantics: the Release 6 instruction
produces only the quotient, whereas the pre-Release 6 instruction produces quotient and remainder in HI/LO registers
respectively, and separate modulo instructions are required to obtain the remainder.
The assembly syntax distinguishes the Release 6 from the pre-Release 6 divide instructions. For example, Release 6
“DIV rd,rs,rt
” specifies 3 register operands, versus pre-Release 6 “DIV rs,rt
”, which has only two register
arguments, with the HI/LO registers implied. Some assemblers accept the pseudo-instruction syntax
“DIV rd,rs,rt
” and expand it to do “DIV rs,rt;MFHI rd
”. Phrases such as “DIV with GPR output” and
“DIV with HI/LO output” may be used when disambiguation is necessary.
That documentation doesn't exactly match what GCC and clang do, but assembly language text source syntax is really the choice of the tool, with the vendor docs and ISA really only having full control over the rules for the machine code.
There's also a modu
instruction on MIPS32/64 r6, which you can use instead
shouldn't we be getting the quotient, using "mflo" instead?
% (high-low+1)
is a remainder operation, not a /
operator where you'd want the quotient.