[HN Gopher] The surprising subtleties of zeroing a register (2012)
       ___________________________________________________________________
        
       The surprising subtleties of zeroing a register (2012)
        
       Author : gtirloni
       Score  : 100 points
       Date   : 2021-11-04 13:49 UTC (9 hours ago)
        
 (HTM) web link (randomascii.wordpress.com)
 (TXT) w3m dump (randomascii.wordpress.com)
        
       | billfruit wrote:
       | What ahout FP registers? Is there special instructions to Zero
       | them?
        
         | woodruffw wrote:
         | For x87, you can use FLDZ to load `+0.0` onto the register
         | stack. My guess is that neither Intel nor AMD has tried
         | particularly hard to optimize that, given x87's legacy status
         | and weird register semantics (see: them being a pseudo-stack).
         | 
         | For SIMD-based FP, you can use VZEROUPPER and VZEROALL if you
         | want to clear all {X,Y,Z}MM state. For a single register, I
         | believe PXOR is still the common idiom, mirroring `XOR EAX,
         | EAX`.
        
           | titzer wrote:
           | > For x87
           | 
           | Should read: for x87, unless you are doing this for fun,
           | shoot yourself in the face and instead use SSE because no one
           | in their right mind should be writing x87 code in 2021!
           | 
           | Please help this architectural misfeature die, which should
           | have happened decades ago.
        
             | gpderetta wrote:
             | For some operations 80 bit accumulators are still useful,
             | so a mix of SSE and x87 might be optimal.
        
             | woodruffw wrote:
             | > Should read: for x87, unless you are doing this for fun,
             | shoot yourself in the face and instead use SSE because no
             | one in their right mind should be writing x87 code in 2021!
             | 
             | My information is pretty old, but IIRC x87 still has
             | specialized instructions for sin/cos/tan that are
             | _sometimes_ more performant than their equivalent
             | implementations in SSE. x87 instructions are also very
             | small, so trig-heavy workloads where I$ is a measurable
             | performance component might _unfortunately_ still be a good
             | fit for x87.
        
               | lifthrasiir wrote:
               | x87 trig instructions are pretty inaccurate even in the
               | supported range mainly because of faulty range reduction
               | [1] and can't be vectorized at all. If you have trig-
               | heavy workloads nowadays you would want SIMD libm, not
               | x87.
               | 
               | [1] http://notabs.org/fpuaccuracy/
        
               | cogman10 wrote:
               | Why can't the trig instructions be vectorized? I've never
               | quiet understood why SSE/AVX didn't add trig functions to
               | finally kill off any argument for using x87.
        
               | adrian_b wrote:
               | Unlike simple operations like a floating-point
               | multiplication, trigonometric functions and the other
               | transcedental functions are too complex, so they must be
               | split into many steps.
               | 
               | If a trigonometric function is encoded as a single
               | instruction, then it must launch a microprogram, to
               | execute the many required steps.
               | 
               | A microprogram cannot execute faster than when the same
               | execution steps would have been encoded as separate
               | instructions, the only advantage of encoding a
               | trigonometric function in a single instruction would be
               | to reduce the program size. Most programs contain few
               | trigonometric functions, so the reduction in program size
               | is not worthwhile.
               | 
               | While a microprogrammed trigonometric function could be
               | as fast as the equivalent sequence of instructions, in
               | reality it is usually much slower.
               | 
               | The reason is that the modern CPUs are optimized for the
               | most frequent instructions and they dedicate a minimum of
               | resources for the seldom used microprogrammed
               | instructions, so these hit various limitations that do
               | not exist for the simple instructions. The
               | microprogrammed instructions usually have some phases
               | whose execution cannot be overlapped in time with other
               | instructions, which leads to lower performance.
        
               | jabl wrote:
               | There exist libraries that provide SIMD versions (on the
               | fast path) of math functions, e.g.
               | https://sourceware.org/glibc/wiki/libmvec
        
               | cogman10 wrote:
               | Gotcha, hence the reason they've added instructions like
               | FMA. Those don't require microprograms but do make things
               | like calculating a taylor series faster. Right?
        
               | Tuna-Fish wrote:
               | > My information is pretty old, but IIRC x87 still has
               | specialized instructions for sin/cos/tan that are
               | sometimes more performant than their equivalent
               | implementations in SSE.
               | 
               | They absolutely are not. If you accept the same, crappy
               | precision, you can do an estimate in just a few cycles
               | instead of the 60+ that the instructions take.
        
               | adrian_b wrote:
               | On recent Intel/AMD CPUs the x87 transcedental functions
               | are usually much slower than their equivalents using
               | SSE/AVX instructions.
               | 
               | For future CPUs, it is expected that the performance gap
               | will increase.
               | 
               | The x87 trigonometric functions require typically between
               | 100 and 200 clock cycles. During that time a recent CPU
               | can execute 200 to 500 instructions, enough to compute
               | many values of a trigonometric function (using a
               | polynomial approximation). When SIMD instructions can be
               | used, several tens of values of a function could be
               | computed during a single x87 instruction.
        
         | monocasa wrote:
         | Last time I checked, x87 (and MMX which uses the same register
         | file) aren't renamed. There's only the architectural register
         | file. At that point these sorts of concerns don't matter
         | because you're not metaprogramming rename hardware through the
         | veneer of the architectural registers.
        
           | gpderetta wrote:
           | The x87 register stack has been renamed on Intel
           | desktop/server OoO cpus since the original Pentium Pro, by
           | special handilng of the fxch instruction.
           | 
           | If anything, AMD improved its handling on recent Zens.
           | 
           | Take a look at Agner manuals.
           | 
           | It is plausible it will be dropped at some point but we are
           | not there yet.
           | 
           | [1] newer atoms, although a
        
             | monocasa wrote:
             | I guess I didn't really consider the manual renaming via
             | fxch the same kind of renaming, and that equally doesn't
             | apply to these sorts of zeroing concerns.
             | 
             | That being said, it looks like my information is a little
             | out of date, and they are renamed fully now and share a PRF
             | with the AVX-512 K registers.
        
               | gpderetta wrote:
               | FXCH has always existed; before PPro, FXCH was a normal
               | FP instruction that was needed to manipulate the physical
               | architectural register stack (as most x87 instructions
               | implicitly operated on the top of stack).
               | 
               | Since PPro there isn't really a register stack anymore.
               | FXCH is not a "real" instruction any more, it has 0
               | latency and uses no execution resources, and it is
               | resolved at the renaming stage and aliases the implicit
               | stack positions to actual fp registers.
        
               | monocasa wrote:
               | According to agner, FXCH didn't actually move values
               | around on Pentium and Pentium MMX processors either
               | despite them being in order cores. There was 'rename
               | hardware' sitting in front of the x87/MMX register file,
               | but there wasn't this distinction of size between
               | architectural and physical register files. Despite
               | renaming, there was still only one physical register for
               | each architectural register at a time (except for a cute
               | bypass technique combined with FXCH).
               | 
               | > The solution to this problem is register renaming. The
               | FXCH instruction does not in reality swap the contents of
               | two registers; it only swaps their names. Instructions
               | that push or pop the register stack also work by
               | renaming. Floating point register renaming has been
               | highly optimized on the Pentiums so that a register may
               | be renamed while in use. Register renaming never causes
               | stalls - it is even possible to rename a register more
               | than once in the same clock cycle, as for example when
               | FLD or FCOMPP is paired with FXCH.
               | 
               | So, like I stated originally, the presence of FXCH and
               | x87 stack doesn't necessarily imply the the same sorts of
               | physical register allocation dependency breaking concerns
               | involved with "how do I zero a register properly". They
               | do now though, as it looks like there's true renaming
               | going on now.
        
           | brucedawson wrote:
           | If x87 and MMX registers don't support renaming then that
           | means that they can't support OOO and speculative execution
           | of these instructions. This is possible but seems unlikely to
           | me.
           | 
           | That is, even though not a lot of x87/MMX code is executed
           | these days (and even less is written) I would be surprised if
           | the OOO/speculative-execution of the processor gets halted
           | whenever these instructions are encountered.
           | 
           | Note that x87 instructions still get used in most 32-bit
           | programs because the x87 registers are the defined way for
           | functions to return floating-point results.
           | 
           | Which is to say, citation needed.
        
             | monocasa wrote:
             | It looks like my information was woefully out of date.
             | They're fully renamed and executed OoO now, and on Intel
             | even share a PRF with AVX-512 K registers interestingly
             | enough.
             | 
             | Thanks for keeping me honest.
        
       | superjan wrote:
       | A plus for the benchmarks. One more complication of xor xx,xx
       | goes unmentioned: xor also (partially) affects the flags
       | register. That should get renamed as well.
        
       | jsnell wrote:
       | (2012)
       | 
       | The most active previous discussion:
       | https://news.ycombinator.com/item?id=19262249
        
         | dang wrote:
         | Thanks! Expanded list:
         | 
         |  _The Surprising Subtleties of Zeroing a Register (2013)_ -
         | https://news.ycombinator.com/item?id=19262249 - Feb 2019 (22
         | comments)
         | 
         |  _The Surprising Subtleties of Zeroing a Register (2012)_ -
         | https://news.ycombinator.com/item?id=11057679 - Feb 2016 (3
         | comments)
         | 
         |  _The Surprising Subtleties of Zeroing a Register_ -
         | https://news.ycombinator.com/item?id=6312266 - Sept 2013 (1
         | comment)
        
       | philh wrote:
       | Author in the old thread
       | (https://news.ycombinator.com/item?id=19262249) says
       | 
       | > An x86-64 CPU has sixteen integer registers, but 100-200
       | physical integer registers. Every time an instruction writes to,
       | say, RAX the renamer chooses an available physical register and
       | does the write to it, recording the fact that RAX is now
       | physical-register #137. This allows the breaking of dependency
       | chains, thus allowing execution parallelism.
       | 
       | I'm curious why they have so many more physical registers than...
       | logical? registers. I have a couple of guesses:
       | 
       | * Physical registers are physically cheaper to add than logical
       | registers.
       | 
       | * Adding logical registers breaks backwards compatibility, or at
       | best means you get no speedup on things written (/compiled) for
       | fewer logical registers. Adding physical registers lets you
       | improve performance without recompiling.
       | 
       | * Adding logical registers increases complexity for people
       | writing assembly and/or compilers. Adding physical registers
       | moves that complexity to people designing CPUs.
       | 
       | Are some of these correct? Other reasons I'm missing?
        
         | guerrilla wrote:
         | > * Adding logical registers breaks backwards compatibility, or
         | at best means you get no speedup on things written (/compiled)
         | for fewer logical registers. Adding physical registers lets you
         | improve performance without recompiling.
         | 
         | > * Adding logical registers increases complexity for people
         | writing assembly and/or compilers. Adding physical registers
         | moves that complexity to people designing CPUs.
         | 
         | These two points are the same thing: compatibility and
         | compatibility is what Intel and AMD have lived on from day one
         | with x86. It's why we still live with this really weird
         | instruction set with all of its historical oddities. Certain
         | features of real mode weren't removed until long into the
         | 64-bit era. Adding things isn't any better: If you wanted to
         | add more add more registers, you'd have to change instruction
         | encoding and the instruction space is finite (actually limited
         | to 15 bytes.) That would be rather disruptive.
        
         | enragedcacti wrote:
         | It can definitely be beneficial to add logical registers. When
         | AMD designed x86-64 they doubled the number of general logical
         | registers up to 16. As other commenters have said, unless you
         | are already making breaking changes, increasing the number of
         | logical registers is probably not worth it.
        
         | captainmuon wrote:
         | I wonder how things like register renaming (or pipelining) are
         | implemented. It would seem difficult even in a high level
         | language, but they do it inside the processor. Is this in
         | microcode that runs on the "actual" processor? Or is it in
         | hardware? Do they write th algorithm in a language like VHDL or
         | Verilog?
        
           | monocasa wrote:
           | Renaming isn't really done in microcode. Microcode is just
           | another source for ops that get renamed. All of the renaming
           | happens in hardware, and boils down to a handful of tables
           | inside the processor.
        
           | brucedawson wrote:
           | Register renaming is implemented in hardware. Because it is
           | used on every instruction it is on the critical path and is
           | probably hand-optimized. Here is some more reading on this
           | topic:
           | 
           | https://en.wikipedia.org/wiki/Register_renaming
        
           | dnautics wrote:
           | All of the above, I believe.
        
         | CalChris wrote:
         | > I'm curious why they have so many more physical registers
         | than... logical? registers
         | 
         | Register renaming allows instructions to be executed out-of-
         | order [1] which allows for more instruction throughput.
         | 
         | This goes back to 1967 and to the IBM 360/91 with its 4
         | floating point registers. That's not many registers but Moore's
         | law was making more transistors available. The problem was how
         | to use these transistors to get more throughput from existing
         | programs without changing the ISA and (potentially) breaking
         | compatibility.
         | 
         | The solution was Tomasulo's algorithm [2] which allowed (few)
         | _architectural_ registers to be renamed to (many) physical
         | registers.                 original         renamed
         | reordered       mov RAX, 1       mov PHYS1, 1      mov PHYS1,
         | 1; mov RAX, [RCX]       add RBX, RAX     add RBX, PHYS1    add
         | RBX, PHYS1       mov RAX, [RCX]   mov RAX, [RCX]
         | 
         | The first and third instructions can be executed at the same
         | time on independent functional units. The third is out-of-order
         | with respect to the second.
         | 
         | [1]
         | https://inst.eecs.berkeley.edu/~cs152/sp20/lectures/L10-Comp...
         | 
         | [2] https://en.wikipedia.org/wiki/Tomasulo_algorithm
        
         | brucedawson wrote:
         | Adding more logical registers is compatibility breaking. And,
         | since you have to encode the register specifier in the
         | instruction it means larger instructions (hence the
         | compatibility breaking) which makes reading and decoding
         | instructions slower.
         | 
         | And, regardless of how many logical registers you have you need
         | to have more physical registers. These are needed for out-of-
         | order (OOO) execution and speculative execution. An OOO super-
         | scalar speculative CPU can have hundreds of instructions in
         | flight and these instructions all need physical registers to
         | work on. If you don't have excess physical registers you can't
         | do OOO or speculative execution.
        
           | CalChris wrote:
           | Adding more logical registers doesn't have to break
           | application compatibility. Intel added x87, MMX ...
           | extensions with their new register sets all without breaking
           | compatibility. They even doubled the integer register set in
           | x86_64 with the REX prefix. New programs could use these
           | features and their register sets without existing programs
           | being broken.
           | 
           | What register renaming allows is to increase the performance
           | of both new and _existing_ programs, which is no mean feat.
           | It allows the CPU scheduler to search for more out-of-order
           | parallelism rather than relying on the compiler to find in-
           | order parallelism.
           | 
           | This binary compatibility doesn't seem very important now,
           | _don 't break userspace_ excepted, but it was then.
           | Compatibility made IBM and Intel hundreds of billions of
           | dollars.
        
             | woodruffw wrote:
             | x87 and MMX's register encodings exist in (mostly) separate
             | parts of the x86 operand encoding map. That's in contrast
             | to GPRs, which have to squeeze into 3 (sometimes 4, with
             | the REX prefix) bits.
             | 
             | That's where the incompatibility comes from -- x86-64
             | required an entirely new prefix to merely double the GPRs;
             | adding a few hundred more would require some _very_
             | substantial changes to the opcode map and all decoders
             | already out there.
        
               | CalChris wrote:
               | When x87+MMX were added, existing programs ran unchanged.
               | When x86-64 doubled the register sets, many existing
               | programs ran unchanged (some features were dropped).
               | Compatibility was largely maintained. That compatibility
               | was what AMD wagged in Intel's face when Intel was trying
               | to pivot to Itanium. Intel had to then take the walk of
               | shame and adopt AMD's approach.
               | 
               | Seriously, Intel took a long view towards this. x87 was a
               | wart on the side of mole and still its unholy marriage
               | with MMX (they shared a register set) allowed existing
               | programs to run while creating a compatibility barrier to
               | competitors. Competitors had to be compatible _and_ bug
               | compatible. The guy tasked with doing this at Transmeta
               | almost had a nervous breakdown, not from compatibility
               | (easy) but from bug compatibility.
               | 
               | IBM 360 programs still run on the Z architecture.
        
               | garaetjjte wrote:
               | x87 instructions weren't dropped, they are still
               | available on x86_64.
        
               | CalChris wrote:
               | Yeah, you're right. _64-Bit Mode Valid_ Thanks.
        
               | woodruffw wrote:
               | I'm not saying it's impossible! They certainly have
               | plenty of space in the EVEX scheme. But extending the
               | GPRs is a much bigger lift, tooling-wise, than is adding
               | a relatively disjoint ISA extension. Even if they can do
               | it while preserving older encodings, it's just another
               | speedbump at a time when Intel is probably anxious to
               | make x86-64 as frictionless as possible.
               | 
               | Besides, register renaming seems to be working splendidly
               | at the uarch level. Why complicate the architectural
               | model when the gains are already present?
        
               | monocasa wrote:
               | > Even if they can do it while preserving older
               | encodings, it's just another speedbump at a time when
               | Intel is probably anxious to make x86-64 as frictionless
               | as possible.
               | 
               | Just wanted to throw out there that it was AMD that came
               | up with x86-64's ISA rather than Intel. Intel was still
               | pushing Itanium hard at the time.
        
               | chasil wrote:
               | The original 8087 prompted the IEE-754 floating point
               | standard, which had a profound impact on the entire field
               | of computer science.
               | 
               | https://news.ycombinator.com/item?id=17767925
               | 
               | https://news.ycombinator.com/item?id=23205225
               | 
               | https://news.ycombinator.com/item?id=23362673
               | 
               | https://news.ycombinator.com/item?id=18107165
        
             | monocasa wrote:
             | Adding those registers each time at the bare minimum broke
             | OS compatibility in order to enable them, and required
             | kernel changes to save and restore the new architectural
             | registers. Adding to the physical register file allows
             | existing code (including kernel space) to take advantage of
             | it. There's been some extensions lately like xsave that try
             | to address that, but they're not fully embraced by major
             | kernels AFAIK.
        
               | CalChris wrote:
               | Tomasulo's register renaming algorithm (1967) comes from
               | an era when you bought the computer and the operating
               | system together. So OS compatibility was their problem.
               | That's not our era but the resulting in-order vs out-of-
               | order war is long over.
               | 
               | Register renaming enabled _out-of-order execution_. The
               | 90s were a competition between in-order compiler based
               | scheduling and out-of-order CPUs. The in-order proponents
               | said that out-of-order was too power hungry, too complex
               | and wouldn 't scale. Well, it did. Even the Itanium which
               | was the great in-order hope, its last microarchitecture,
               | Poulson, had out-of-order execution.
               | 
               | Ultimately, out-of-order won the war but in-order
               | survives for low power low complexity designs; the A53 is
               | in-order. Skylake Server has 180 physical registers with
               | an out-of-order search window of 224.
               | 
               | https://www.primeline-solutions.com/media/wysiwyg/news-
               | press...
        
               | monocasa wrote:
               | In the 1960s, os compat was your problem as the end user
               | too. There wasn't the strict divide between OS code and
               | user code in the same way. The 360/91 that Tomasulo's
               | algorithm originally shipped on didn't even have an MMU
               | to separate your code from the kernel.
               | 
               | Additionally, compiler tech wasn't anywhere near where it
               | was today, and high perf code was written in ASM.
               | Therefore existing code would have to be rewritten to use
               | more registers, but the 360/91 ran existing code just
               | fine (which was very important for the 360/91's main
               | customers).
        
         | cogman10 wrote:
         | I've not seen this in other responses, but an answer to your
         | question is every logical register adds overhead to context
         | switching by the operating system.
         | 
         | The OS has to store and load all registers whenever it decides
         | to switch which thread is processing. 100 more logical
         | registers means 100 more locations the OS has to keep track of.
         | 
         | This is part of the reason why new SIMD instruction sets need
         | OS support before you can start using them.
        
         | kps wrote:
         | > Adding logical registers breaks backwards compatibility
         | 
         | This, plus adding logical registers increases instruction size
         | and therefore decreases the number of instructions that can be
         | fetched with a given memory bandwidth.
        
           | [deleted]
        
         | ridiculous_fish wrote:
         | Don't think of %eax as a real register. Think of it as a tag in
         | a compressed dataflow graph. The compression is performed by
         | the compiler's register allocator, and the decompression is
         | performed by the CPU's register renaming.
         | 
         | A compiler's IR is a directed graph, where nodes are
         | instructions and tagged by an assigned register. It would be
         | pleasant to assign each node a distinct register, but then
         | machine instructions would be unacceptably large. So the
         | compiler's register allocator compresses the graph, by finding
         | nodes that do not interfere and assigning them the same
         | register.
         | 
         | The CPU's register renamer then reinflates this graph, by
         | inspecting the dataflow between instructions. If two
         | instructions share a register tag, but the second instruction
         | has no dependence on the first, then they may be assigned
         | different physical registers.
         | 
         | `xor eax, eax` has no dependence on any instruction, so it can
         | be specially recognized as allocating a new physical register.
         | In this way of thinking, `xor eax, eax` doesn't zero anything,
         | but is like malloc: it produces a fresh place to read/write,
         | that doesn't alias anything else.
        
         | msla wrote:
         | Having more physical registers than logical means the CPU can
         | do optimizations, opcodes don't have to be as big (it takes
         | more bits to encode more registers), compatibility with older
         | binary code is maintained, and CPUs at different price points
         | can have different numbers of physical registers while all
         | being able to run the same binaries.
         | 
         | (I don't know if any manufacturer actually does that last
         | thing, however.)
        
         | dexen wrote:
         | Beyond reasons & limitations explained in sibling posts, having
         | large number of (logical / instruction-level) registers also
         | inflates instruction size, and thus diminishes instruction
         | density, and thus lowers performance - so there is a trade-off
         | between that and large number of registers. Hear me out.
         | 
         | The CPU has limited memory bandwidth; the larger instruction
         | size, the more bytes needs to be loaded from memory to execute
         | the instruction. Same with cache size - the more space an
         | instruction takes, the lower the amount of instructions that is
         | cached. Lastly, there's the complexity & latency of the
         | instruction decoder. This possible performance loss is averted
         | by keeping instructions short and instruction set "dense".
         | 
         | Any instruction that refers to a register needs certain amount
         | of bits in the operand portion to indicate which specific
         | register(s) is to be used [1][2][3]. As example, in case of
         | 8-register x86 the operand generally uses 3 bits just to
         | indicate which register to use. In case of 16 register x86_64,
         | it takes 4 bits. If we wanted to use all 200 physical register,
         | that would require whole 8 bits reserved in the instruction
         | just to indicate the register to use. Certain instructions -
         | data transfer, algebra & bitwise operations, comparisons, etc.
         | - naturally use two or more registers, so multiply that
         | accordingly.
         | 
         | Since using this many registers gives only diminishing return
         | in terms of performance (and also requires very heavy lifting
         | on compiler's part[4]), the trade-off selected is that the
         | compiler uses architecture-defined small number of registers,
         | and the processor at runtime is able to speed up _some_ code
         | using the spare registers for instruction-level execution
         | parallelism.
         | 
         | [Edit]
         | 
         | There's one more common circumstance where large number of
         | registers is undesirable: a change of execution context (thread
         | switch; process switch; interrupt). Typically all
         | _architecturally-visible_ registers are saved to memory on a
         | change of context and new set is loaded for the new context.
         | The more registers there are, the more work is to be done.
         | Since the hardware registers are managed directly by CPU and
         | serve as more of cache than directly accessed register, they
         | don 't need to be stored to memory.
         | 
         | [1] Aside of certain specialized instructions that implicitly
         | use a particular register; for example in x86 many instructions
         | implicitly use the FLAGS register; DIV/IDIV integer division
         | implicitly uses AX and DX registers.
         | 
         | [2] Aside of certain instruction prefixes that influence which
         | register is used; for example in x86 that would be segment
         | register overrides.
         | 
         | [3] Aside of certain architectures where registers were
         | organized in a "file" and available only through a "window" -
         | i.e., an implicit context, implicit register addressing base;
         | instruction operands referred to registers relative to the
         | current window, and the whole window could be shifted by
         | specialized instructions. Typically shifted on function
         | enter/leave and similar. This was more-or-less the whole
         | "hardware registers" being exposed at architecture level,
         | however in a somewhat constrained / instruction-dense way.
         | 
         | [4] Arranging which registers to use, which to spill to memory
         | etc. is non-trivial work for compiler, and the complexity grows
         | super-linearly with the number of registers.
        
         | tenebrisalietum wrote:
         | I think your second point hits it and is the primary benefit to
         | hiding the microarchitecture layer - it can be improved and
         | existing code will benefit from it.
         | 
         | Basically Intel is saying if you had 200 GPRs, you couldn't do
         | better at using the free ones than the CPU scheduler/decoder.
         | 
         | > Adding *architecturally visible* registers increases
         | complexity for people writing assembly and/or compilers.
         | 
         | More registers just makes your code less likely to have to
         | shuffle stuff to and back from RAM - which is where stuff will
         | go if you don't have registers.
         | 
         | It's always faster for a CPU to access registers within itself
         | than have to talk over a bus to a memory. Even when RAM was the
         | same speed as CPUs (8-bit era) you would still save a cycle or
         | two.
        
           | brucedawson wrote:
           | Having more logical/architectural registers is great except
           | for a few costs:
           | 
           | 1) More bits to encode register numbers in instructions.
           | Doubling the number of logical registers costs another two or
           | three bits depending on how many registers are referenced in
           | an instruction
           | 
           | 2) Logical registers have to be saved on context switches
           | 
           | 3) Logical registers have to be saved around function calls.
           | Either the caller or the callee has to save (or not use)
           | registers, and most functions are both callers and callees.
           | That is, if you are not a leaf-node function then every
           | register you use you have to first save to the stack, or else
           | assume that the functions you call will trash it. Thus, more
           | registers have diminishing returns.
           | 
           | 4) No matter how many logical registers you have you _always_
           | want to have an order of magnitude more physical registers,
           | because otherwise you can't implement OOO or speculative
           | execution.
           | 
           | Point #4 is probably the most critical because I think what
           | people are really asking is why are there more physical than
           | logical registers, and OOO/speculative execution is the
           | answer.
        
       | commandlinefan wrote:
       | > 1: add eax, 1
       | 
       | > 2: mov ebx, eax
       | 
       | > 3: xor eax, eax
       | 
       | > 4: add eax, ecx
       | 
       | > Ideally we would like our awesome out-of-order processor to
       | execute instructions 1 and 3 in parallel. There is a literal data
       | dependency between them, but a sufficiently advanced processor
       | could detect that this dependency is artificial.
       | 
       | This doesn't seem right... instruction 2 does need to run after
       | instruction 1 and before instruction 3.
        
         | monocasa wrote:
         | Not if it's renamed. Instruction 2 does need to run after
         | instruction 1, but instruction 3 doesn't need to run after
         | them. xor eax, eax doesn't depend on the previous value of eax
         | (because xoring anything at all with itself is zero), so you
         | can assign it a new physical register file slot and just set it
         | to zero.
        
           | brucedawson wrote:
           | Yep, exactly. The rule of thumb is that whenever a register
           | is written to a new register mapping is created. For
           | instruction #1 eax might be assigned to physical register
           | 103. It maintains that identity for instruction #2. For
           | instruction #3 eax can be assigned to, let's say, physical
           | register 67. Then for instruction #4 it could be assigned
           | physical register 92. The only tricky thing is that
           | instruction #3 also reads from eax, which normally would
           | cause a dependency, but because XOR doesn't depend on the
           | register contents this clever optimization applies.
           | 
           | Here's another variant:
           | 
           | > 1: add eax, 1 > 2: mov ebx, eax > 3: mov eax, 42 > 4: add
           | eax, ecx
           | 
           | Here instruction 3 writes the constant 42 to the register.
           | This might make it clearer why instruction #3 can run in
           | parallel with or before instruction #1.
           | 
           | Note that the results must be _retired_ in order, but
           | execution can happen out of order.
        
       | TheMatten wrote:
       | After reading https://news.ycombinator.com/item?id=29104841 today
       | and wondering why they zero one of registers using XOR on itself,
       | it suddenly makes a lot of sense.
        
         | commandlinefan wrote:
         | I asked why that was when I took assembler in college and the
         | instructor didn't know (he said, "oh, um, it's faster"). I'm
         | glad to finally find the actual correct answer to that
         | question.
        
           | monocasa wrote:
           | To be fair, the choice of xor eax, eax is 'faster' than mov
           | eax, 0. The xor option is only two bytes versus the five of
           | mov, which can have I$ and decode width concerns. And back in
           | the day, five bytes was pretty awkward for up to 32 bit data
           | buses, where you'd need at least two cycles to even read the
           | mov instruction. That's why existing code tended to use xor
           | eax, eax in the first place, even before Intel started adding
           | rename hardware to their processors.
        
             | bonzini wrote:
             | Same for xor ax,ax on 16-bit processors by the way (2 bytes
             | vs 3), and even earlier for the 8-bit 8080 (1 byte vs 2).
             | The xor or sub trick dates to the late 70s.
        
       ___________________________________________________________________
       (page generated 2021-11-04 23:01 UTC)