Live data from Hacker News

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

devblogs.microsoft.com

121–130 of 231 posts

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

#121
post #79
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.

This sounds like a Paged Out article ;)

https://www.cs.columbia.edu/~angelos/Papers/hydan.pdf

Here's some more prior art

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

#122
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 ripple through all the rest of the gates one-by-one. It can't be paralellized without extra circuitry, which increases your costs in other ways.

Without the AND gate needed for carry, all the XORs can fire off at the same time. If you added the extra circuitry for a parallelizable add/subtract to make it as fast as XOR, your actual parallel XOR would consume less power.

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

#123
post #34
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.

You still need to explain why this case creates a positive feedback loop rather than a negative one. I mean left/right fuel intakes in cars and male/female ratios somehow tend to balance at 50/50.

As someone with a right side fuel intake, that’s certainly isn’t true in the US. Left side fuel intake dominates completely and when the 8 pump station I prefer is busy, I only ever see left hand intake cars being fueled from the “wrong” side.

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

#124
post #96

Earlier quoted context omitted.

One byte instructions, with 8 registers as in the 8086, waste 8 opcodes which is 3% of the total. There are just five: "INC reg", "DEC reg", "PUSH reg", "POP reg", "XCHG AX, reg" (which is 7 wasted opcodes instead of 8, because "XCHG AX, AX" doubles as NOP). One-byte INC/DEC was dropped with x86-64, and PUSH/POP are almost obsolete in APX due to its addition of PUSH2/POP2, leaving only the least useful of the five in…

I’m not sure I understand what you mean by “waste 8 opcodes.”

special mov 0 instruction times 8 registers. The opcode space, especially 1 byte opcode space, is precious so encoding redundant operations is wasteful.

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

#125
post #102

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.

XOR A absolutely works on Z80 and it's of course faster and shorter than loading a zero value with LD A,0. LD A,0 is encoded to 2 bytes while XOR A is encoded as a single opcode. XOR A has the additional benefit to also clear all the flags to 0. Sub A will clear the accumulator, but it will always set the N flag on Z80.

Ah, thanks, I couldn't recall off the top of my head.

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

#126
post #81

Earlier quoted context omitted.

> XOR is just a particular case of subtraction, it is not a different kind of operation. It's different in that there's no carry propagation.

That is not a property specific to XOR. Whenever you do addition/subtraction modulo some power of two, the carry does not propagate over the boundaries that correspond to the size of the modulus. For instance, you can make the 128-bit register XMM1 to be zero in one of the following ways: PXOR XMM1, XMM1 ; Subtraction modulo 2^1 PSUBB XMM1, XMM1 ; Subtraction modulo 2^8 PSUBW XMM1, XMM1 ; Subtraction modulo 2^16 PSUB…

And in practice it is very likely that XOR and the variously sized vector ADDs and SUBs are implemented exactly by the same ALU circuitry, parameterized by a bitmasks of the carry lines to enable (none for XOR, all except the vector size boundaries for the vector operations).

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

#127

Back in the stone ages XOR ing was just 1 byte of opcode. Habbits stick. In effect XORing is no longer faster since a long time.

The article’s point is about why XOR is preferred over SUB, both being one byte.

MOV is right out.

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

#128
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.

That could be a style metric, too. Time spent reversing MS-DOS viruses in my youth showed me assembler programmers very clearly have styles to their code. It's too weak for definitive attribution but it was interesting to see "rhymes" between, for example, the viruses written by The Dark Avenger.

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

#129

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…

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.

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

#130
post #84

Earlier quoted context omitted.

Those aren't the only resources. I could imagine XOR takes less energy because using it might activate less circuitry than SUB.

I'm not aware of any stories in the historical record of "real programmers" optimizing for power use, only for speed or code size.

For a few years I worked in the team that wrote software for an embedded audio DSP. The power draw to do something was normally more important than the speed. Eg when decoding MP3 or SBC you probably had enough MIPS to keep up with the stream rate, so the main thing the customers cared about was battery life. Mostly the techniques to optimize for speed were the same as those for power. But I remember being told that add/sub used less power than multiply even though both were single cycle. And that for loops with fewer than 16 instructions used less power because there was a simple 16 instruction program memory cache that saved the energy required to fetch instructions from RAM or ROM. (The RAM and ROM access was generally single cycle too).

Nowadays, I expect optimizations that minimize energy consumption are an important target for LLM hosts.

Post reply on HN