6 ms·
I have often wondered, do we really need any general-purpose registers to be visible in the ISA? Aren't they an implementation detail, just like caching? IMHO,
by bluetomcat 11y ago
I have often wondered, do we really need any general-purpose registers to be visible in the ISA? Aren't they an implementation detail, just like caching?
IMHO, a memory-to-memory architecture would make for a much simpler ISA and allow much easier code generation (no register allocation needed).
- panic 11y agoOne problem would be instruction size. On a system with (e.g.) 32 registers, each register can be addressed with 5 bits, while a memory address is 32-64 bits.
- bluetomcat 11y agoThere could be a scheme for encoding large immediates in a compact way, just like ARM does. Besides, I don't think that instruction size or instruction cache pressure is the bottleneck nowadays.
- cesarb 11y agoThe RISC-V designers implemented a compressed instruction extension (the most common instructions can be represented in half the size), and found a performance gain. So yes, instruction size matters. From the 1.9 draft of the RISC-V compressed instructions specification: "Waterman shows that RVC fetches 25%-30% fewer instruction bits, which reduces instruction cache misses by 20%-25%, or roughly the same performance impact as doubling the instruction cache size." Larger caches are slower, so just doubling the cache size doesn't necessarily recover the performance lost by larger instructions.
- userbinator 11y agoimplemented a compressed instruction extension (the most common instructions can be represented in half the size), and found a performance gain. ...which makes me wonder why they didn't just make that the standard. x86 is dense and that's partly where its performance comes from.
- cesarb 11y agoThey planned for it since the beginning. While the base RISC-V encoding uses only 32-bit instructions, it's actually a variable-length encoding, where the least significant bits define the instruction length. If the least significant bits are 00, 01, or 10, it's a 16-bit instruction. The base RISC-V was designed to be minimal; for instance, hardware multiply and divide is the M extension. I expect that, once the C (compressed instructions) extension is standardized, most high-performance implementations will include it, since the extra area cost should be minimal compared to the out-of-order machinery; small in-order implementations where area is at a premium and performance is not so important will continue to use only the base 32-bit instructions. There's even a variant, RV32E, which omits half the registers to make the core even smaller.
- _chris_ 11y agox86 is NOT dense - it averages nearly 4 bytes per instruction on workloads such as SPEC. And not everybody wants to go through the design effort to support variable-length encoding (it's non-trivial and can potentially increase pipeline latency to support it). It very well could become a de facto RISC-V standard, but that remains to be seen.
- __s 11y agoIn the paper they discuss how x86 isn't even dense, & barely competes with ARM's constant width instructions
- marcosdumay 11y agoWell, if you really want to push it, you can have a smaller, faster L1 cache, like with 32 words or so, and a set of special instructions, shorter, but only capable of addressing this small memory space... Instruction fetching and parsing is an important bidimensional bottleneck that does restrict your architecture. It's not a bottleneck for run-time performance because it's predictable and optimized at design time.
- meanduck 11y agoMemory can addressed by taking offset from RSP (or [ 0x10 ] eg).
- bluetomcat 11y agoHere is how I implemented a small instruction set for the VM of my experimental language, without using GPRs. Basically, instruction operands are divided into 4 classes: - G (global): the operand refers to the global memory space and contains an offset and size. - A (automatic): the operand refers to the current stack frame. The offset is relative to the current base pointer. - I (immediate) - T (temporary): this is where intermediate results from computations are stored. For example, the result of `a + b` from `(a + b) * c` would be stored in the T memory space. Upon the end of a statement in the HLL, all values in the temporary space are discarded. Reference: https://github.com/bbu/quaint-lang/blob/master/src/exec.c https://github.com/bbu/quaint-lang/blob/master/src/exec.c
- meanduck 11y ago> - A (automatic): the operand refers to the current stack frame. The offset is relative to the current base pointer. Did you mean stack pointer ? base pointer gets set once where as stack pointer changes between every function call. > Here is how I implemented a small instruction set for the VM of my experimental language, without using GPRs. stack pointer is still necessery unless some memory region is predefined as stack pointer. 'A class' can be made more space efficient by dividing stack into 16 chunks and then instruction would only need 4bit to address.
- meanduck 11y agoAccording to the thesis recent intel microarchs caches the top several words of the stack[1]. Thesis references intel optmization manual but does not say where in the manual. [1] pg 12, para 5.
- dkopi 11y agoThe Tradeoff is probably run-time vs compile time optimization.
- ndesaulniers 11y agoI don't understand this comment, maybe because I can't picture what a "memory-to-memory architecture" would look like. You MUST worry about registers due to the exponential increase in latencies between registers and larger memory communicating with the ALU. Also, limited are the amount of execution units. If all memory accesses cost the same in terms of latency, then yes, registers would be an implementation detail.
- bluetomcat 11y agoEliminating GP registers from the ISA doesn't preclude any optimizations which could be done dynamically in the microarchitecture. The CPU would still be able to deal with registers and caches internally, it's just that they won't be visible to the programmer. Think of how x86-64's 16 architectural registers are dynamically renamed to more than 120 physical registers.
- ndesaulniers 11y agoThat certainly is an interesting idea to think about. I would think it's hard to beat the compiler though (when it knows how many GP registers there are). Consider register pressure. If the compiler assumes there are infinite GP registers (because they're hidden now, no longer an implementation detail), it can generate code that spends more time spilling and reloading registers than computing. The compiler has seen the code before; it knows how many variables it will need to work with an how to schedule available resources. The processor (relatively) simply is executing a stream of instructions. It would have to look pretty far ahead (VLIW) or have sufficient execution units (superscalar) to have high throughput and not get bogged down spilling. But, if the finite amount of resources, like number of processing units or GPRs become very high relative to the amount of symbolic variables, then I think the register names could be hidden.
- chrisseaton 11y ago> when it knows how many GP registers there are But compilers don't know this! They know how many registers there are in the ISA, but not how many there really are in the physical architecture. Physical architectures map ISA register names into register file locations. It's an abstraction! You can really use a register more than once at the same time because they will be renamed. Your parent is suggesting that we read and write from memory as normal, and just map memory locations into the register file, rather than ISA register names. Registers may be more of an abstraction than you realise. (Maybe compilers do know about how large the register rename buffer really is on certain physical chips and take this into account when allocating them. Sounds like something Intel's C compiler might do. I'm not an expert.)
- conceit 11y agoA Counter Machine with only one register would be an abstraction that doesn't need register annotation. It's turing complete with only two instructions. Chaching is not a transparent implementation detail of the processor, i.e. compilers and assembler programmers do take caching into account, as far as I can tell from reading HN. Register utilization is a concern as well. Register renaming is probably just a implementation detail, I wouldn't really know.
- cesarb 11y agoAs far as I know, a L1 cache hit takes around 2-3 cycles. On a memory-to-memory architecture, every instruction would take that hit, multiple times, and that's if they hit; unless you use a slower fully associative cache, you can have cache line conflicts which reduce the hit rate. With registers, you can have a long sequence of operations without ever hitting the cache.