Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Is there a reason for giving registers a different name depending on its value's length (D0 = 64-bit, S0 = 32-bit)?

I can never remember which names are mapped to the same register. I've had that issue on x86 and I'll have that issue on ARM.

I loved how Motorola 68000 made it simple. Each command had a dot-letter suffix indicating the length: move.b d0, d1



For 64-bit Arm FPU registers, the mapping is extremely simple: Q0 is the 128-bit vector register; D0 is the bottom 64 bits of it; S0 is the bottom 32 bits; H0 is the bottom 16 bits; B0 is the bottom 8 bits. Similarly Q1/D1/S1/H1/B1 are all the same underlying register.

For 32-bit Arm, unfortunately, things are different: D0 is the bottom half of Q0, and D1 the top half; similarly S0 is the bottom half of D0 and S1 is the top half (and so Q0 is S0 S1 S2 S3, with S3 its most significant 32 bits). This is why "just indicate the length on the insn" wouldn't have worked for 32-bit: there is more than one 32-bit register in each 64-bit register (and some kind of 'high/low' notation would have been annoying when you really do want to just think of it as a collection of 32-bit registers sometimes, especially if your hardware doesn't even have double-precision support!). The 64-bit transition fixed up the awkward overlaid mapping, but retains the notation for the benefit of all the people who were already familiar with the Arm notational conventions for things.


Thanks! I had a closed look at them and the naming is starting to make sense: Q=quad, D=double, S=single, H=half, B=byte (except the article claims that V=128bit and there's no mention of H or B)

For general purpose registers: X=long (64-bit), W=word (32-bit)


It's Qn when you're dealing with the register as a single 128-bit quantity (currently only the SHA256 and SHA512 insns need to do this, I think, judging from a quick search through the architecture reference manual), and Vn when you're dealing with the register as a vector of smaller units (eg "ADD V10.4S, V8.4S, V9.4S" does a vector-addition of V8 and V9 into V10, treating each 128-bit V register as a vector of 4 Single-precision (32-bit) floats): you write vector arguments as Vn.2D, Vn.4S, Vn.8H or Vn.16B. Hn is used for 16-bit floating point arithmetic insns. It looks like nothing's using Bn yet, but the manual defines the notation.


You can ldr and str those registers too, if you're implementing a NEON memcpy for example.


On 68k, that move writes 8 bits; it doesn’t really write to a 8-bit register. I make that difference because, on many (I think) other systems, such a byte move sign extends the least significant byte in d0 to the width of the target register and writes the full register.

Also, on 68k you can only address the least significant word or byte of a register that way. Some CPUs have, say, 8 8-bit registers that can alternatively be treated as 4 16-bit registers (https://en.wikipedia.org/wiki/Intel_8080#Registers). On such a CPU, one can write to the top half of a register in a single instruction by writing to the 8-bit register that shares its storage (some RISC CPUs support that by having a separate instruction “load top half of register”)


My guess is that it's some effort to maintain some kind of compatibility with ARM32. Back when I used to write ARM asm, it was the ARM 26/32 days, and it was rXX for all the register named in the APCS.

It doesn't seem to be as confusing as the x86 situation anyway: none of the registers are _really_ special purpose on ARM.

Here's a link to the details in the ARM64 APCS: https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aap...




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: