Live data from Hacker News

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

devblogs.microsoft.com

211–220 of 231 posts

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

#211
post #88

Earlier quoted context omitted.

Energy consumption wasn't really a concern when the idiom developed. I don't think people really cared about the energy consumption of instructions until well into the x86-64 era.

Not sure why this is being downvoted, but it’s absolutely correct. For most of the history of computing, people were happy that it worked at all. Being concerned about energy efficiency is a recent byproduct of mobile devices and, even more recently, giant amounts of compute adding up to gigawatts.

This take is anachronistic. Thermal issues were evident by the late 1990's. Of course by that time not many were working in x86 assembly but embedded systems sure cared about power.

People forget embedded predated mobile by a good 20 years.

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

#212
post #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.…

SUB does not have higher latency than XOR on any Intel CPU, when those operations are really performed, e.g. when their operands are distinct registers.

The weird values among those listed by you, i.e. those where the latency is less than 1 clock cycle, are when the operations have not been executed.

There are various special cases that are detected and such operations are not executed in an ALU. For instance, when the operands of XOR/SUB are the same the operation is not done and a null result is produced. On certain CPUs, the cases when one operand is a small constant are also detected and that operation is done by special circuits at the register renamer stage, so such operations do not reach the schedulers for the execution units.

To understand the meaning of the values, we must see the actual loop that has been used for measuring the latency.

In reality, the latency measured between truly dependent instructions cannot be less than 1 clock cycle. If a latency-measuring loop provides a time that when divided by the number of instructions is less than 1, that is because some of those instructions have been skipped. So that XOR-latency measuring loop must have included XORs between identical operands, which were bypassed.

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

#213
post #17

Looking at some random 1989 Zenith 386SX bios written in assembly so purely programmer preferences: 8 'sub al, al', 14 'sub ah, ah', 3 'sub ax, ax' 26 'xor al, al', 43 'xor ah, ah', 3 'xor ax, ax' edit: checked a 2010 bios and not a single 'sub x, x'

Could be used to express 1 bit of information in some non-obvious convention.

The x86-64 ISA provides a lot of alternative encodings for the same instruction or for instructions that are equivalent.

It has already been suggested to use these for steganography, i.e. for embedding a hidden message in a binary executable file, by encoding 1 or more bits in the choice of the instruction encoding among alternatives, for every instruction for which alternatives exist.

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

#214

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.

It all depends on the CPU architecture, if it supports something like out-of-order execution then both parts of the CPU could be in use at the same time to execute different instructions. Realistically any CPU with that level of complexity doesn't care about SUB vs XOR though.

In an OoO CPU it won't even hit an execution unit because it's handled as a dependency chain break.

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

#215
post #10

Once an instruction has an edge, even if only extremely slight, that’s enough to tip the scales and rally everyone to that side. And this, interestingly, is why life on earth uses left-handed amino acids and right-handed sugars .. and why left handed sugar is perfect for diet sodas.

> and why left handed sugar is perfect for diet sodas

If you want to get diarrhea.

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

#216
post #11

Earlier quoted context omitted.

I'm not actually aware of any CPUs that preform a XOR faster than a SUB. And more importantly, they have identical timings on the 8086, which is where this pattern comes from.

I'm studying 4-bit-slice processors from the 1970s. This is all tangent to the x86 discussion. Minicomputer processors! I have two bit-slice machines from TI based on the 74S481 (4-bit slice x 4). Just like with the 74181, all ALU operations go through the same path, there are just extra gates that make the difference between logical or arithmetic. For instance, for each bit in the slice, the carry path is masked out…

Thanks, I suspected there might be something from the minicomputer era.

I've only really looked at a single AM2900 implementation (and it was far from optimal). Guess I need to dig deeper at some point.

> The ONES operation forces all the carry chain to 1 (ignoring operand) (you can do a ONES+1 to get arithmetic 0, but why?)

Forcing all carries to 1 inverts the output.

If I'm understanding the ALU correctly, (the datasheet doesn't show that part) it only implements OR and XOR. When combined with the ability to invert both inputs, AND can be implemented as !(!A OR !B), NAND is (!A OR !B) and so on.

Or maybe the ALU implements NOR and XNOR, and all the carry logic is physically inverted from what the documentation says.

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

#217

Earlier quoted context omitted.

Unfortunately, 6502 can't XOR the accumulator with itself. I don't recall if the Z80 can, and loading an immediate 0 would be most efficient on those anyway.

You're absolutely right, I stand corrected. The 6502 gets by doing immediate load: 2 clock cycles, 2 bytes (frequently followed by single byte register transfer instruction). Out of curiosity I did a quick scan of the MOS 1.20 rom of the BBC micro: LDY #0 (a0 00): 38 hits LDX #0 (a2 00): 28 hits LDA #0 (a9 00): 48 hits

Are you sure you're not an LLM? There is no way anybody writing 6502 would do anything else, because there's no other way to do it.

(You can squeeze in a cheeky Txx instruction afterwards to get a 2-or-more-for-1, if that would be what you need - but this only saves bytes. Every instruction on the 6502 takes 2+ cycles! You could have done repeated immediate loads. The cycle count would be the same and the code would be more general.)

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

#218

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…

> but no body uses this for obvious reason, it's just a toy example for education.

SERV has entered the chat!

It has one upside besides education, and that is that it can be implemented with fewer gates. If you for some reason need parallelism on the core level rather than the bit level, you can cram in more cores with bit-serial ALUs in the same space.

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

#219

Earlier quoted context omitted.

> afaik, xor’ing is faster Even tiny tiny CPUs can do sub in one cycle, so I doubt that. On super-scalar CPUs xor and sub are normally issued to the same execution units so it wouldn't make a difference there either.

On superscalars running xor trick as is would be significantly slower because it implies a data dependency where there isn't one. But all OOO x86's optimize it away internally.

Sub has the same false data dependency.

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

#220
post #4

The obvious answer is that XOR is faster. To do a subtract, you have to propagate the carry bit from the least-significant bit to the most-significant bit. In XOR you don't have to do that because the output of every bit is independent of the other adjacent bits. Probably, there are ALU pipeline designs where you don't pay an explicit penalty. But not all, and so XOR is faster. Surely, someone as awesome as Raymond C…

XOR is faster when you do that alone in an FPGA or in an ASIC. When you do XOR together with many other operations in an ALU (arithmetic-logical unit), the speed is determined by the slowest operation, so the speed of any faster operation does not matter. This means that in almost all CPUs XOR and addition and subtraction have the same speed, despite the fact that XOR could be done faster. In a modern pipelined CPU,…

Superpipelining doesn't work in practice because you can only save the timing slack left over in the pipelined architecture. If you're running the CPU twice as fast but basic operations now take twice as long, all you've done is double the book keeping cost, which is the energy intensive part of a CPU, while having gained a small performance increase in the few cases where a quick 1 cycle instruction finishes faster than a slow 1 cycle instruction.

Energy efficiency is usually better. There are countless ways to translate energy efficiency into higher performance.

Post reply on HN