I think that I am looking for an answer to a trivia question. I am trying to understand why the MIPS architecture uses an explicit "zero" value in a register when you can achieve the same thing by just XOR'ing any register against itself. One could say that the operation is already done for you; however, I cannot really imagine a situation where you would be using a lot of "zero" values. I read Hennessey's original papers, and it just assigns a zero as a matter of fact without any real justification.
Does a logical reason to have a hard-coded binary assignment of zero exist?
update:
In 8k of an executable from xc32-gcc for the MIPS core in the PIC32MZ, I have a single instance of "zero".
add t3,t1,zero
the actual answer:
I awarded the bounty to the person who had the information about MIPS and condition codes. The answer actually lies in the MIPS architecture for conditions. Although I was initially not wanting to assign time to this, I reviewed architecture for opensparc, RISC-V, MIPS-IV and OpenPOWER (this document was internal) and here are the summary findings. The R0 register necessary for comparison on branches due to the architecture of the pipeline.
- integer compare against zero and branch (bgez,bgtz,blez,bltz)
- integer compare two registers and branch (beq,bne)
- integer compare two registers and trap (teq,tge,tlt,tne)
- integer compare register and immediate and trap (teqi,tgei,tlti,tnei)
It just simply comes down to how the hardware looks in implementation. From the RISC-V manual, there is an unreferenced quote on page 68:
The conditional branches were designed to include arithmetic comparison operations between
two registers (as also done in PA-RISC and Xtensa ISA), rather than use condition codes (x86,
ARM, SPARC, PowerPC), or to only compare one register against zero (Alpha, MIPS), or
two registers only for equality (MIPS). This design was motivated by the observation that a
combined compare-and-branch instruction ts into a regular pipeline, avoids additional condition
code state or use of a temporary register, and reduces static code size and dynamic instruction
fetch trac. Another point is that comparisons against zero require non-trivial circuit delay
(especially after the move to static logic in advanced processes) and so are almost as expensive as
arithmetic magnitude compares. Another advantage of a fused compare-and-branch instruction
is that branches are observed earlier in the front-end instruction stream, and so can be predicted
earlier. There is perhaps an advantage to a design with condition codes in the case where multiple
branches can be taken based on the same condition codes, but we believe this case to be relatively
rare.
The RISC-V document does not hit at the author of the quoted section. I thank everyone for their time and consideration.
Best Answer
The zero-register on RISC CPUs is useful for two reasons:
It's a useful constant
Depending on restrictions of the ISA, you can't use a literal in some instructions encoding, but you can be sure you can use that
r0
to get 0.It can be used to synthesize other instructions
This is perhaps the most important point. As a ISA designer, you can trade-off a general purpose register to a zero-register to be able to synthesize other useful instructions. Synthesizing instructions is good because by having less actual instructions, you need less bits to encode an operation in a opcode, which frees-up space in the instruction encoding space. You can use that space to have e.g. bigger address offsets and/or literals.
The semantics of the zero-register is like
/dev/zero
on *nix systems: everything written to it is discarded, and you always read back 0.Let's see a few examples of how we can make pseudo-instructions with the help of the
r0
zero-register:The case of MIPS
I looked more closely at the MIPS instruction set. There are a handful of pseudo-instructions that uses
$zero
; they are mainly used for branches. Here are some examples of what I've found:As for why you have found only one instance of the
$zero
register in your disassembly, perhaps it's your disassembler that is smart enough to transform known sequences of instructions into their equivalent pseudo-instruction.Is the zero-register really useful?
Well, apparently, ARM finds having a zero-register useful enough that in their (somewhat) new ARMv8-A core, which implement AArch64, there's now a zero-register in 64-bit mode; there wasn't a zero-register before. (The register is a bit special though, in some encoding contexts it's a zero-register, in others it instead designates the stack pointer)