Live data from Hacker News

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

devblogs.microsoft.com

201–210 of 231 posts

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

#202

Earlier quoted context omitted.

The question is why one idiom won over the other, which happened a long time ago. Because as the article notes on "any modern x86 processor" both xor r, r and sub r, r are handled by the frontend and have essentially no cost.

Because of encoding size of the machine code, not because of any runtime cost

> It encodes to the same number of bytes

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

#203
post #11
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…

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 if logical, but used if arithmetic.

* The XOR operation (logical) is accomplished with A+B but no bits carry. If carry is not masked, you get arithmetic ADD.

* The ZERO or CLEAR operation is (A+A without carry). With carry, A+A is a shift-left.

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

* In the simpler 74181 (4 years earlier) there are 16 operations with 48 logical/arithmetic outcomes. Pick 12 or so for your instruction set. There are some weirdos.

The crazy thing here is that in the TM990/1481 implementation, the microinstruction clock is 15 MHz, and each has a field for number of micro-wait states. This is faster than the '481s max!

Theoretically, if 66ns is sufficient to settle the ALU, a logical operation doesn't need a micro-wait-state. While arithmetic needs one, only because of carry-look-ahead. If I/O buses are activated, then micro-instructions account for setup/hold times. I could be wrong about the details, but that field is there!

It's the only architecture I know of with short and long microinstructions! (The others are like a fixed 4-stage cycle: input valid, ALU valid, store)

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

#204
Afaik xor reg,reg is optimized by the cpu as zero-out reg; sub reg, reg is quite more difficult to optimize this way; this seems to be quite important in modern cpus, where cisc is translated to micro-ops; in superscalar archs, this is probably optimized away instead of causing a stall.

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

#205

Earlier quoted context omitted.

> 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…

386 is a microprogrammed CPU where a multiplication is dome by a long sequence of microinstructions, including a loop that is executed a variable number of times, hence its long and variable execution time. A register-register operation required 2 microinstructions, presumably for an ALU operation and for writing back into the register file. Unlike the later 80486 which had execution pipelines that allowed consecutiv…

Most of mul/div was implemented in hardware since the 80186 (and the more or less compatible NEC V30 too). The microcode only loaded the operands into internal ALU registers, and did some final adjustment at the end. But it was still done as a sequence of single bit shifts with add/sub, taking one clock cycle per bit.

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

#206

Earlier quoted context omitted.

It would probably run really fast, considering that Itanium's downfall was the difficulty in compiling. (Including translating x86 instructions into Itanium instructions)

Not really. Itanium was a result of some people at Intel being obsessed by LINPACK benchmarks and forgetting everything else. It sucked for random memory access, and hence everything that's not floating-point number-crunching. Compiler can't hide memory access latency because it's fundamentally unpredictable. VLIW does magic for floating-point latency (which is predictable), but - As transistors got smaller, FP perfo…

Short loops and lots of math is what I'm talking about; the kind of thing that hand-written assembly language helps with on even modern hardware.

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

#207

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…

[deleted]

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

#208
post #85
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…

Yea, that’s what immediately went through my head, too. XOR is ALWAYS going to be single cycle because it’s bit-parallel.

And SUB is also always a single cycle on any practically useful architecture since the 70s. Theoretical archs where SUB might be slower than XOR don't matter.

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

#209

Earlier quoted context omitted.

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…

> For general-purpose computers, there have never been "4-bit ALU times". Well, consider minicomputers made from bit-slices. Those would be 4-bit ALUs with CLA. What drives me crazy about the 8-bit era is the lack of orthogonality. We're having this whole discussion because they didn't have a ZERO or ONES opcode. In 1972's 74181 chip those were just cases among 48 modes.

The minicomputers made with bit-slices had 16-bit ALUs or 32-bit ALUs.

Those 16-bit or 32-bit ALUs were made from 2-bit, 4-bit or 8-bit slices, but this did not matter for the programmer, and it did not matter even for the micro-programmer who implemented the instruction set architecture by writing microcode.

The size of the slices mattered a little for the schematic designer who had to draw the corresponding slices and their interconnections an it mattered a lot for the PCB designer, because each RALU slice (RALU = registers + ALU) was a separate integrated circuit package.

Intel made 2-bit RALU slices (the Intel 3000 series), AMD made 4-bit RALU slices (the 2900 series), which were the most successful on the market. There were a few other 4-bit RALU slices, e.g. the faster ECL 10800 series from Motorola, Later, there were a few 8-bit RALU slices, e.g. from Fairchild and from TI, but by that time the monolithic processors became quickly dominant, so the bit-sliced designs were abandoned.

The width of the slices mattered for cost, size and power consumption, but it did not matter for the architecture of the processor, because the slices were made to be chained into ALUs of any width that was a multiple of the slice width.

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

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

should set Z too
Post reply on HN