Live data from Hacker News

XOR'ing a register with itself is the idiom for zeroing it out. Why not sub?

devblogs.microsoft.com

141–150 of 231 posts

Re: XOR'ing a register with itself is the idiom for zeroing it out. Why not sub?

#142

The hw implementation of xor is simpler than sub, so it should consume slightly less energy. Wondering how much energy was saved in the whole world by using xor instead of sub.

For a 32 bit number you're looking at going from using 256 to ~1800 transistors in the operation itself. A modern core will have roughly 1,000,000,000 transistors. Some of those are for vector operations that aren't involved in a xor or sub, but most of them are for allowing the core to extract more parallelism from the instruction stream. It's really just a dust mote compared to the power reduction you could get by, e.g., targeting a 10 MHz lower clock rate.

Re: XOR'ing a register with itself is the idiom for zeroing it out. Why not sub?

#143
post #14

Earlier quoted context omitted.

The non-obvious bit is why there isn't an even faster and shorter "mov ,0" instructions - the processors started short-circuiting xor , much later.

In x86, a basic immediate instruction with a 1 Byte immediate value is encoded like this: (1 Byte opcode), (1 Byte), (1 Byte) While xor eax, eax only uses 2 bytes. Since there are only 8 registers, meaning they can be encoded with 3 bits, you can pack two values into the field (ModR/M). Making mov eax, 0 only take two bytes would require significant changes of the ISA to allow immediate values in the ModR/M byte (or…

Some other architectures like PDP-11 and 680x0 had a dedicated "clear register" instruction.

It could have been added to x86, even as a group of single-byte opcodes with the register encoded in three bits (as with PUSH, POP, and INC/DEC outside of long mode). But the XOR idiom was already established on the 8080 by that point.

Re: XOR'ing a register with itself is the idiom for zeroing it out. Why not sub?

#144
SUB has higher latency than XOR on some Intel CPUs:

latency (L) and throughput (T) measurements from the InstLatx64 project (https://github.com/InstLatx64/InstLatx64) :

  | GenuineIntel | ArrowLake_08_LC | SUB r64, r64 | L: 0.26ns=  1.00c  | T:   0.03ns=   0.135c |
  | GenuineIntel | ArrowLake_08_LC | XOR r64, r64 | L: 0.03ns=  0.13c  | T:   0.03ns=   0.133c |
  | GenuineIntel | GoldmontPlus    | SUB r64, r64 | L: 0.67ns=  1.0 c  | T:   0.22ns=   0.33 c |
  | GenuineIntel | GoldmontPlus    | XOR r64, r64 | L: 0.22ns=  0.3 c  | T:   0.22ns=   0.33 c |
  | GenuineIntel | Denverton       | SUB r64, r64 | L: 0.50ns=  1.0 c  | T:   0.17ns=   0.33 c |
  | GenuineIntel | Denverton       | XOR r64, r64 | L: 0.17ns=  0.3 c  | T:   0.17ns=   0.33 c |
I couldn't find any AMD chips where the same is true.

Re: XOR'ing a register with itself is the idiom for zeroing it out. Why not sub?

#145

Earlier quoted context omitted.

It's still the same number of clock cycles, though, isn't it? You're using some extra circuitry during the SUB, but during the XOR, that circuitry is just sitting idle anyway, so it's still six of one/half a dozen of the other.

XOR can do everything in 1 cycle (which is hopefully far, far less than the clock). SUB-if done the simple way-has to take n cycles where n is the number of bits subtracted.

That's just not true. You think subtracting/adding 64-bit numbers actually take 64 cycles?

There is sequential implementation of ripple carry adder that uses clock and register, this will add 1-bit per cycle, but no body uses this for obvious reason, it's just a toy example for education. A normal ripple carry adder will have some delay in propagation time before the output is valid, but that is much less a clock cycle. You can also design a customized adder circuit for 4-bit 8-bit 16-bit etc separately that would greatly minimizes the propagation delay to only 2 or 3 levels of gates, instead of n gates like in the ripple carry adder.

Re: XOR'ing a register with itself is the idiom for zeroing it out. Why not sub?

#146
post #47

Relatedly, there's a steganographic opportunity to hide info in machine code by using "XOR rax,rax" for a "zero" and "SUB rax,rax" for a "one" in your executable. Shouldn't be too hard to add a compiler feature to allow you to specify the string you want encoded into its output.

You can do better. X86 has both "op [mem], reg" and "op reg, [mem]" variants of most instructions, where "[mem]" can be a register too. So you have two ways to encode "xor eax, eax", differing by which of the operands is in the "possible memory operand" slot, the source or the destination.

This one would be a fun challenge in a ctf, or maybe more appropriate for a puzzle hunt – most people would look at the dissassembly and not at the actual bytes and completely miss the binary encoding

Re: XOR'ing a register with itself is the idiom for zeroing it out. Why not sub?

#147
post #112

Earlier quoted context omitted.

In a clockless cpu design you'd indeed expect xor to be faster. But in a regular CPU with a clock you either waste a bit of xor performance by making xor and sub both take the same number of ticks, or you speed up the clock enough that the speed difference between xor and sub justifies sub being at least a full tick slower The former just seems way more practical

Even if they take the same number of ticks, shouldn't xor fundamentally needing less work also mean it can be performed while drawing less power/heating less, which is just as much an improvement in the long run?

That wasn’t much of a concern in the 70s and 80s.

Re: XOR'ing a register with itself is the idiom for zeroing it out. Why not sub?

#148

Earlier quoted context omitted.

On modern ones, x86 has quite a history and the idiom might carry on from an even older machine. Edit: Looked at comments, seems like x86 and the major 8bit cpu's had the same speed, pondering in this might be a remnant from the 4-bit ALU times.

Nope. In any ALU the speed is determined by the slowest operation, so XOR is never faster. It does not matter which is the width of the ALU, all that matters is that an ALU does many kinds of operations, including XOR and subtraction, where the operation done by an ALU is selected by some control bits. I have explained in another comment that the only CPUs where XOR can be faster than subtraction are the so-called su…

> In any ALU the speed is determined by the slowest operation, so XOR is never faster.

On a 386, a reg/reg ADD is 2 cycles. An r32 IMUL is "9-38" cycles.

If what you stated were true, you'd be locking XOR's speed to that of DIV. (Or you do not consider MUL/DIV "arithmetic", or something.)

https://www2.math.uni-wuppertal.de/~fpf/Uebungen/GdR-SS02/op...

> I have explained in another comment that the only CPUs where XOR can be faster than subtraction are the so-called superpipelined CPUs. Superpipelined CPUs have been made only after 1990 and there were very few such CPUs.

(And I'm choosing 386 to avoid it being "a superpipelined CPU".)

Re: XOR'ing a register with itself is the idiom for zeroing it out. Why not sub?

#149

XOR is a simple logic-gate operation. SUB would have to be an ALU operation. A one-bit adder (which is subtraction in reverse) makes signals pass through two gates. See https://en.wikipedia.org/wiki/Adder_(electronics) You need the 2 gates for adding/subtracting because you care about carry. So if you're adding/subtracting 8 bits, 16 bits, or more, you're connecting multiples of these together, and that carry has to…

That's all true, but on any modern x86 processor both the single pair of gates for the xor and the 10 or so for a carry-bypass 64 bit wide subtraction both happen with a single clock cycle of latency so from a programmer's perspective they're the same in that sense. There's still an energy difference but its tiny compared to what even the register file and bypass network for the operation use, let along the OoO struc…

The question isn't whether they both take a clock cycle, but rather whether any future implementation of the ISA might ostensibly find some sort of performance advantage, even if none do right now. From that standpoint, xor seems like a safer bet.

Re: XOR'ing a register with itself is the idiom for zeroing it out. Why not sub?

#150

Earlier quoted context omitted.

XOR can do everything in 1 cycle (which is hopefully far, far less than the clock). SUB-if done the simple way-has to take n cycles where n is the number of bits subtracted.

That's just not true. You think subtracting/adding 64-bit numbers actually take 64 cycles? There is sequential implementation of ripple carry adder that uses clock and register, this will add 1-bit per cycle, but no body uses this for obvious reason, it's just a toy example for education. A normal ripple carry adder will have some delay in propagation time before the output is valid, but that is much less a clock cyc…

Right. In other words, the clock cycle is already made to be long enough to allow a word-sized SUB to settle. An XOR-with-self surely settles faster, but it still has to wait for that same clock cycle before proceeding.
Post reply on HN