Live data from Hacker News

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

devblogs.microsoft.com

91–100 of 231 posts

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

#91
post #14
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…

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.

Right, like a “set reg to zero” instruction. One byte. Just encodes the operation and the reg to zero. I’m surprised we didn’t have it on those old processors. Maybe the thinking was that it was already there: xor reg,reg.

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

#92
post #27

Earlier quoted context omitted.

Quite a few architectures have a dedicated 0 register.

Yep. The XOR trick - relying on special use of opcode rather than special register - is probably related to limited number of (general purpose) registers in typical '70 era CPU design (8080, 6502, Z80, 8086).

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.

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

#93
post #20
post #9

Earlier quoted context omitted.

His point is that in x86 there is no performance difference but everyone except his colleague/friend uses xor, while sub actually leaves cleaner flags behind. So he suspects its some kind of social convention selected at random and then propagated via spurious arguments in support (or that it “looks cooler” as a bit of a term of art). It could also be as a result of most people working in assembly being aware of the…

I think an even more likely explanation would be that x86 assembly programmers often were, or learned from other-architecture assembly programmers. Maybe there's a place where it makes more sense and it can be so attributed. 6502 and 68k being first places I would look at.

6502 doesn't even have register-to-register ALU operations, there's no alternative to LDA #0.

8080/Z80 is probably where XOR A got a lead over SUB A, but they are also the same number of cycles.

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

#94

Earlier quoted context omitted.

That comment is not very useful without pointing to realworld CPUs where SUB is more expensive than XOR ;) E.g. on Z80 and 6502 both have the same cycle count.

The 6502 doesn't support XOR A or SUB A, and in fact doesn't have a SUB opcode at all, only SBC (subtract with carry, requiring an extra opcode to set the carry flag beforehand).

I was handwaving over the details, SBC is identical to SUB when the carry flag is clear, so it's understandable why the 6502 designers didn't waste an instruction slot.

EOR and SBC still have the same cycle counts though.

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

#95

Earlier quoted context omitted.

That comment is not very useful without pointing to realworld CPUs where SUB is more expensive than XOR ;) E.g. on Z80 and 6502 both have the same cycle count.

Harvard Mark I? Not sure why people think programming started with Z80.

The article is about x86, and x86 assembly is mostly a superset of 8080 (which is why machine language numbers registers as AX/CX/DX/BX, matching roughly the function of A/BC/DE/HL on the 8080—in particular with respect to BX and HL being last).

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

#96
post #91
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.

Right, like a “set reg to zero” instruction. One byte. Just encodes the operation and the reg to zero. I’m surprised we didn’t have it on those old processors. Maybe the thinking was that it was already there: xor reg,reg.

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 the most recent incantation of the instruction set.

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

#97
post #8
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 and SUB have had identical cycle counts and latencies since the 8088. That's because you can "look ahead" when doing carries in binary. It's just a matter of how much floorspace on the chip you want to use. https://en.wikipedia.org/wiki/Carry-lookahead_adder The only minor difference between the two on x86, really, is SUB sets OF and CF according to the result while XOR always clears them.

OF/CF/AF are always cleared anyway by SUB r,r. So there's absolutely no difference.

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

#98
post #91
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.

Right, like a “set reg to zero” instruction. One byte. Just encodes the operation and the reg to zero. I’m surprised we didn’t have it on those old processors. Maybe the thinking was that it was already there: xor reg,reg.

Instruction slots are extremely valuable in 8-bit instruction sets. The Z80 has some free slots left in the ED-prefixed instruction subset, but being prefix-instructions means they could at best run at half speed of one-byte instructions (8 vs 4 clock cycles).

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

#99

Earlier quoted context omitted.

Yep. The XOR trick - relying on special use of opcode rather than special register - is probably related to limited number of (general purpose) registers in typical '70 era CPU design (8080, 6502, Z80, 8086).

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.

The Z80 can do either LD A,0 or SUB A or XOR A, but the LD is slower due to the extra memory cycle to load the second byte of the instruction.

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

#100
post #27

Earlier quoted context omitted.

Quite a few architectures have a dedicated 0 register.

Yep. The XOR trick - relying on special use of opcode rather than special register - is probably related to limited number of (general purpose) registers in typical '70 era CPU design (8080, 6502, Z80, 8086).

And [as mentioned in the article] even modern x86 implementations have a zero register. So you have this weird special opcode that (when called with identical source and destination) only triggers register renaming
Post reply on HN