4

I know that when you read %r0 in SPARC CPU (and MIPS), always return 0, but I like to know Why ?

What design decision is behind this and why ?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Zardoz89
  • 590
  • 5
  • 17
  • 2
    g0 or r0. I think it is g0 in SPARC! – Rahul Tripathi Oct 02 '13 at 06:02
  • in MIPS it's $zero or $0 – phuclv Oct 02 '13 at 06:14
  • It does not only appear in SPARC or MIPS but also common in many RISC architectures since zero is a very common value. In ARMv8 AArch64 mode, the register x31 is also hardwired to 0 – phuclv Oct 02 '13 at 06:36
  • 1
    @RahulTripathi: SPARC registers (by encoding in the instruction format) are `r0..r32` and `%g0` _is_ `r0`. Just in assembly language / mnemonics, they're "syntactically split" `%g0..%g7`, `%o0..%o7`, `%l0..%l7` and `%i0..%i7`. If you say `%r0` in SPARC assembly sources, you get `%g0`. – FrankH. Oct 02 '13 at 16:35

2 Answers2

6

It's just the way the CPU was designed. Ensuring that r0 is always set to zero is, for one thing, a way to avoid potentially costly memory accesses for a very common value.

On one hand (reading), it's handy to have a register set aside to contain the value of zero so that you can use it. Otherwise, you would have to load zero into a register yourself.

Many RISC processors tend to favour data manipulation in registers, accessing memory only for load and store operations. That's not a hard and fast rule of RISC, more of a tendency. Setting aside a register so that you always have zero available can be useful - it's a trade-off since you get one less register to use for general purpose values but the MIPS designers obviously thought it was worth it.

On the other hand (writing), because r0 is tied to the value zero, you can put what you want in there and it will make no difference - it will remain at zero. That means you can use it as a target if you want to throw the value away.

Again, this has to do with the philosophy behind RISC. It tends to favour a very small number of instruction formats such as the MIPS R, I and J formats (register, immediate and jump). As an example, rather than having multiple instruction formats depending on whether you want to store the result or not, you can have one set which stores the result always, then just store it into r0 if you don't care about it.

Hence, if you wanted to check if adding two registers would result in an overflow but didn't want to store the result anywhere, you could use:

add $0, $7, $8  ; r0 <- r7 + r8, but r0 remains at 0.

The MIPS documentation, MIPS32 Architecture for Programmers Volume I: Introduction to the MIPS32 Architecture, confirms the above:

R0 is hard-wired to a value of zero, and can be used as the target register for any instruction whose result is to be discarded. R0 can also be used as a source when a zero value is needed.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • I working in a RISC like cpu of 16 bits. I asked this, because I was thinking if in my case r0 being always zero, could be useful. In my case, setting a register to 0 value is cheap (a single instruction do it)); plus I only have a few instructions that uses 3 registers (load/store only), the ALU operation reuses one register as output (like ADD rd, rs -> rd += rs). I think that r0 being always zero, in my case is wasteful. Thanks for your answer! – Zardoz89 Oct 02 '13 at 15:54
  • 1
    @paxdiablo: I think you meant to say `add $0, $7, $8`. The destination register comes first in MIPS. – markgz Oct 02 '13 at 18:24
  • I wouldnt say "philosophy behind RISC", it was simply a design decision, RISC has nothing to do with it you could just as easily have used another register and simply not saved or re-used the value. By taking away r0 you lose a gpr, a heavy cost, so it is more of a burden not a savings as far as throwaway values. – old_timer Oct 03 '13 at 01:26
  • @dwelch, I may not have been clear. The design philosophy suggests simplified instruction formats, not the use of a particular register as always-zero. That is, after all where reduced instruction set computing comes from. – paxdiablo Oct 03 '13 at 04:48
  • Yes, I fully understand risc design, cisc design, etc. I was just pointing out that r0 being read only, has nothing to do with cisc nor risc design. It does support the overall MIPS instruction set design, not as RISC, but just as an instruction set. No matter what instruction set you are using there will be times where you want to perform an instruction but discard the result, and for some of those the instruction set discards the result, or you burn a register, memory location, or other. – old_timer Oct 03 '13 at 05:15
  • If MIPS didn't have a zero-register, they would have provided a mov-immediate instruction. There's lots of opcode coding space for new `I` format instructions, isn't there? So it would be like ARM, which has no zero-register: you just zero a register with `mov $reg, #0`, or any other 16-bit constant, instead of `addui` with `$0`. With MIPS not having flags, the discard-write use-case is not a big deal either, just the trap on signed-overflow check. (SPARC can just `sub` into the zero register for `cmp` so it's a nice RISCification, but MIPS needs compare-into-register instructions.) – Peter Cordes Feb 19 '18 at 14:17
1

The main reason from instruction-set-design point of view is that the existance of a /dev/null-register allows for certain "condensing" of what otherwise would have to be two (or more) different instructions into a single one. Few examples:

  • function call and return, using link registers, can be done with a single instruction:
    • As function call instruction: jmp <tgtreg>,<pc>,<linkreg>
      (SPARC jmp is an atomic mov <pc>,<linkreg>; mov <tgtreg>,<pc>), and
    • As function return instruction: jmp <linkreg>,<nullreg>,<nullreg>
  • cmp or tst can be a simple sub <a>,<b>,<nullreg>

Complete tables of these can be found from Oracle's documentation, SPARC synthetic instructions and sparcv9 synthetic instructions; most of these involve %g0 somewhere.

FrankH.
  • 17,675
  • 3
  • 44
  • 63
  • The zero-register is analogous to `/dev/zero`, not `/dev/null`. Both discard writes, but `/dev/zero` reads as zero, while `/dev/null` reads as empty (i.e. can't read from it). And BTW, MIPS doesn't have flags (instead using [compare-into-register instructions like `sltu dst, src1, src2`](http://www.mrc.uidaho.edu/mrc/people/jff/digital/MIPSir.html)), so `sub` with the zero reg as a destination only makes sense on CPUs with flags, [like SPARC](http://faculty.kutztown.edu/spiegel/CSc235/PowerPoint/FLAGS_AND_BRANCHING.HTML) or ARM. But ARM doesn't have a zero-reg, and isn't nearly as RISCy. – Peter Cordes Feb 19 '18 at 14:08