Search code examples
assemblycpu-architecturecpu-registers

Does it make sense for a clean-slate 64-bit instruction set to have 64-bit registers only? No need to care about backwards compat


If we could redesign a CPU that would not need to have 8/16/32 bit registers (only 64 bit) and planned on only running NEW programs (no backports), would it make sense that we'd only have 64 bit registers?

(no al/ah/ax/eax/etc registers)

Not planning on designing a CPU, just curious.


Solution

  • You're right that you certainly wouldn't include high-8 partial registers, and likely wouldn't include 8 and 16-bit operand-size except for load/store and sign/zero-extend. Operations on uint8_t data can be done by zero-extending to 32/64 when needed, often not needing to redo extension between operations, except before stuff like right-shift or division where high garbage would be a problem.


    Have a look at AArch64. Unlike x86 extending to x86-64, they started fresh with the machine-code format and registers to make a clean new design. They have 32 and 64-bit operand-size for most instructions, like most modern 64-bit RISC ISAs.

    With instructions to zero-extend or sign-extend narrow values from registers or memory, and even some addressing modes that use a 32-bit register as an index to a 64-bit pointer, intended to support cases like foo(int *p, int idx) { return p[idx]; } where the index has to be sign-extended to pointer-width somehow.

    Even DEC Alpha, which was aggressively 64-bit, has some support for some common 32-bit operations.

    Of course, it's just a matter of naming conventions whether you do MIPS64 dadd $t0, $t1, $t2 vs. add $t0, $t1, $t2 for a 64-bit vs. 32-bit add, with the operand-size implied by the mnemonic, or AArch64 add x0, x1, x2 vs. add w0, w1, w2 with the operand-size implied by the register names (x0..31 instead of w0..31 as the names for their low halves).

    MIPS64 doesn't have features like AArch64's ldr x0, [x1, w2, uxtw] (indexed addressing mode, zero-extending the low 32 bits of x2, aka w2). So MIPS doesn't need separate names for separate register widths, because it's not mixing widths in the same instruction. It uses a prefix for the mnemonic (d for double-word) to indicate what kind of operation this is. (I'm glossing over the fact that MIPS64 requires 32-bit instructions other than shifts to have their inputs and outputs correctly sign-extended to 64-bit, or something like that.)

    But really it's not fundamentally different, it's not like the low halves are a different register. You could design an asm syntax for MIPS64 where addu $t1w, $t2w, $t3w meant 32-bit, and the same with $t1d meant 64-bit.

    PowerPC uses l (long = 64-bit) vs. w (word = 32-bit) for a lot of mnemonics, so it's similar to MIPS's style.


    One assumption I'm making is that writing a "narrow" register zero-extends into the full register, like AArch64 and x86-64 for 32-bit registers.

    But unlike x86-64 for legacy 8 and 16-bit partial registers, which merge with the old value because those semantics date back to 8086 and 386, before pipelined implementations, and before out-of-order exec was even considered for x86 to make it obvious that false dependencies were bad.

    You'd definitely do implicit zero-extension like AArch64 add w0, w1, w2, and provide bitfield-insert instructions for merging. Or let software do it with AND/OR.