Search code examples
assemblyx86x86-64cpu-architecture

Microarchitectural zeroing of a register via the register renamer: performance versus a mov?


I read on a blog post that recent X86 microarchitectures are also able to handle common register zeroing idioms (such as xor-ing a register with itself) in the register renamer; in the words of the author:

"the register renamer also knows how to execute these instructions – it can zero the registers itself."

Does anybody know how this works in practice? I know that some ISAs, like MIPS, contain an architectural register that is always set to zero in hardware; does this mean that internally, the X86 microarchitecture has similar "zero" registers internally that registers are mapped to when convenient? Or is my mental model not quite correct on how this stuff works microarchitecturally?

The reason why I am asking is because (from some observation) it seems that a mov from one register containing zero to a destination, in a loop, is still substantially faster than zeroing the register via xor within the loop.

Basically what it happening is that I would like to zero a register within a loop depending on a condition; this can either be done by allocating an architectural register ahead of time to store zero (%xmm3, in this case), which is not modified for the entire duration of the loop, and executing the following within it:

movapd  %xmm3, %xmm0

or instead with the xor trick:

xorpd   %xmm0, %xmm0

(Both AT&T syntax).

In other words choice is between hoisting a constant zero outside of the loop or rematerializing it within it for each iteration. The latter reduces the number of live architectural registers by one, and, with the supposed special case awareness and handling of the xor idiom by the processor, it seems like it ought to be as fast as the former (especially since these machines have more physical registers than architectural registers anyway, so it should be able to internally do the equivalent to what I've done in the assembly by hoisting out the constant zero or even better, internally, with full awareness and control over its own resources). But it doesn't seem to be, so I'm curious if anyone with CPU architecture knowledge can explain if there's a good theoretical reason for that.

The registers in this case happen to by SSE registers and the machine happens to be Ivy Bridge; I'm not sure how important either of those factors are.


Solution

  • Executive summary: You can run up to four xor ax, ax instructions per cycle as compared to the slower mov immediate, reg instructions.

    Details and references:

    Wikipedia has a nice overview of register renaming in general: http://en.wikipedia.org/wiki/Register_renaming

    Torbjörn Granlund's timings for instruction latencies and throughput for AMD and Intel x86 processors are at: http://gmplib.org/~tege/x86-timing.pdf

    Agner Fog nicely covers the specifics in his Micro-architecture study:

    8.8 Register allocation and renaming

    Register renaming is controlled by the register alias table (RAT) and the reorder buffer (ROB) ... The µops from the decoders and the stack engine go to the RAT via a queue and then to the ROB-read and the reservation station. The RAT can handle 4 µops per clock cycle. The RAT can rename four registers per clock cycle, and it can even rename the same register four times in one clock cycle.

    Special cases of independence

    A common way of setting a register to zero is by XOR'ing it with itself or subtracting it from itself, e.g. XOR EAX,EAX. The Sandy Bridge processor recognizes that certain instructions are independent of the prior value of the register if the two operand registers are the same. This register is set to zero at the rename stage without using any execution unit. This applies to all of the following instructions: XOR, SUB, PXOR, XORPS, XORPD, VXORPS, VXORPD and all variants of PSUBxxx and PCMPGTxx, but not PANDN etc.

    Instructions that need no execution unit

    The abovementioned special cases where registers are set to zero by instructions such as XOR EAX,EAX are handled at the register rename/allocate stage without using any execution unit. This makes the use of these zeroing instructions extremely efficient, with a throughput of four zeroing instructons per clock cycle.