ARM’s entire gross profits are about half of AMD’s R&D budget, but ARM cores have soundly beat x86 in IPC for years now (since around A78) and the most recent generations seem to be beating them in total performance, perf/watt, and core size.
We now have all three of the big ARM-based cores (Apple, ARM, and Qualcomm) beating x86. Apple you could maybe write off as unlimited money, but all three isn’t just coincidence.
If that weren’t enough, ARM designers are releasing new cores every year instead of every other year meaning they are doing around twice as many layout and validations despite the massively lower budget.
Before I get the “ARM only makes cores” excuses, I’d note that ARM announced that they’ve been working on their own server chips and that work is obviously having to fit within their same (comparatively tiny) budget.
It seems fairly obvious. Spring legacy garbage drives up complexity and cost (eg, ARM reduced A-series decoder size by 75% when they dropped 32-bit mode which was still way less complex than x86). This complexity drives up development time and cost. It also drives up validation cost and time.
There’s also a physical cost. Large, high frequency uop caches and cache controllers are better than just decoders on x86, but worse than not needing them at all is better still. Likewise, you hear crazy stuff like the x86 overly-strict memory model not mattering because you can speculate it away. That speculation means more complexity, more power, and more area.
Once you’re done with enough of these work-around, you get a chip that is technically as fast, but it cost more to design, costs more to validate, costs more to fab, costs more to buy, costs more to operate, and carries an opportunity cost from taking so much longer to get to market.
Interesting. Got a source for that?
It was one of the biggest features of A715 going by ARM’s slides.
I think that's arguable, honestly. Or if not it hinges on quibbling over "classic".
There is a lot of code density advantage to special-case CISCy instructions: multiply+add and multiplex are obvious one in the compute world, which need to take three inputs and don't fit within classic ALU pipeline models. (You either need to wire 50% more register wires into every ALU and add an extra read port for the register store, or have a decode stage that recognizes the "special" instructions to route them to a special unit -- very "Complex" for a RISC instruction).
But also just x86 CALL/RET, which combine arithmetic on the stack pointer, computation of a return address and store/load of the results to memory, are a big win (well, where not disallowed due to spectre/meltdown mitigations). ARM32 has its ldm/stm instructions which are big code size advantages too. Hardware-managed stack management a-la SPARC and ia64 was also a win for similar reasons, and still exists in a few areas (Xtensa has a similar register window design and is a dominant player in the DSP space).
The idea of making all access to registers and memory be cleanly symmetric is obviously good for a very constrained chip (and its designers). But actual code in the real world makes very asymmetric use of those resources to conform to oddball but very common use cases like "C function call" or "Matrix Inversion" and aiming your hardware at that isn't necessarily "bad" design either.
That says nothing about whether you should opt for something that is more similar in nature to classic RISC or classic CISC.
If you are doing many-core (NOT multi-core) than RISC make obvious sense.
If you are doing in-order pipelined execution, than RISC makes-sense.
But if you are doing superscalar out-of-order where you have multiple execution units and you crack instructions anyway, why not have CISC so that the you have more micro-ops to reorder and optimise? It seems like it would give the schedulers more flexibility to keep the pipelines fed.
With most infrastructure now open-source I think the penalty for introducing a new ISA is a lot less burdensome. If you port LLVM, GCC, and the JVM and most businesses could use it in production immediately without needing emulation that helped doom the Itanium.
In the end, though, I don’t see CISC making any significant come back, other than perhaps in embedded where code size is definitely important and speeds are generally lower and multi-cycle execution is ok. But it feels like we already have all the ISAs we need to cover that space pretty well already.
* From the RISC side, adhere to a load-store architecture with lots of registers
* From the CISC side, have compressed variable length encoding and fused instructions for common complex operations (e.g. multiply-add, compare-and-branch)
Likewise, RISC-V Compressed only provides compressed encodings of a subset of RISC-V instructions, so binaries that make use of RISC-V Compressed will be variable-length encoded, mixing 16-bit and 32-bit encoded instructions.
It was only RISC ISAs introduced between 1985 (Arm, MIPS, SPARC) and 1992 (DEC Alpha) that had fixed-length 4 byte instructions -- a mere eight years out of the 60 year history of RISC designs.
In any case, I think it's weird that we've experienced semantic drift to the point that architectures with instruction sets that are too large and complex to be considered reduced get referred to as RISC simply because they're load-store architectures with lots of registers.
I don't think newer things labelled as "RISC" have significantly (if at all) exceeded original 1985 ARM for complexity of an individual instruction, or 1990 IBM RS/6000 for sheer number of instructions, so unless you have a newer example this "semantic drift" happened 35-40 years ago in the first 5-10 years after publication of the first RISC papers.
Arm64 of course hits both axes.
While I think having a small number of instructions (at least optionally) is desirable for a number of reasons I don't see that as defining RISC. RISC is, I think, about instructions being uniform enough to not complicate the execution pipeline. If you keep the instructions to reading two registers and writing a result to one register, preferably after 1 clock cycle, then I don't think there is any number of different operations available on those 2 operands that would make something be not RISC any more.
Some people think it's ok to read 3 input registers -- again, Arm since 1985. And everyone in floating point, since `±A*B±C` is the fundamental and most common operation in floating point algorithms and IEEE 754-2008 mandates that the operation be done without intermediate rounding.
There are some manufacturers who have apparently thought "RISC" is a good marketing thing and put the label on things that I don't consider to be RISC. For example Microchip claims 8 bit PIC is RISC. It certainly isn't -- it's a classic accumulator architecture (as are Intel 8051 and 8080 and MOS 6602) with Acc-Mem and Mem-Acc instructions. Calling memory "registers" doesn't change that On the other hand Atmel AVR is clearly RISC.
Also TI market the MSP430 as RISC. It's a lovely little minimalist 16 bit ISA very similar to PDP-11 and, like PDP-11, all the 2-operand arithmetic operations can be done memory-to-memory which is a definite RISC no-no. Original M68000 - also PDP-11 like -- has a better claim to being RISC as (like 8086) it at least kept everything except MOV{B,W,L} down to reg-mem.
But, no, PIC and MSP430 are not RISC despite their manufacturers saying they are.
I wonder if anyone has actually measured what the code size savings from this look like for typical programs, that would be an interesting read. RISC trope is to expose a "link register" and expect the programmer to manage storage for a return address, but if call/ret manage this for you auto-magically you're at least saving some space whenever dealing with non-leaf functions.
On arm64, all instructions are four bytes. The BL and BX to effect the branching is 8 bytes of instruction already. Plus non-leaf functions need to push and pop the return address via some means (which generally depends on what the surrounding code is doing, so isn't a fixed cost).
Obviously making that work requires not just the parallel dispatch for all the individual bits, but a stack engine in front of the cache that can remember what it was doing. Not free. But it's 100% a big win in cache footprint.
True for `ret`, I'm not convinced it's true for `call` on typical amd64 code. The vast majority I see are 5 bytes for a regular call, with a significant number of 6 bytes e.g. `call 0xa4b4b(%rip)` or 7 bytes if relative to a hi register. And a few 2 bytes if indirect via a lo register e.g. `call %rax` or 3 for e.g. `call *%r8`.
But mostly 5 bytes, while virtually all calls on arm64 and riscv64 are 4 bytes with an occasional call needing an extra `adrp` or `lui/auipc` to give ±2 GB range.
But in any case, it is indisputable that on average, for real-world programs, fixed-length 4 byte arm64 matches 1-15 byte variable-length amd64 in code density and both are significantly beaten by two length riscv64.
All you have to do to verify this is to just pop into the same OS and version e.g. Ubuntu 24.04 LTS on each ISA in Docker and run `size` on the contents of `/bin`, `/usr/bin` etc.
(I cheated a bit and used the total size of the binary, as binutils isn't available out of the box in the ubuntu container. But it shouldn't be too different from text+bss+data.)
$ podman run --platform=linux/amd64 ubuntu:latest ls -l /usr/bin | awk '{sum += $5} END {print sum}'
22629493
$ podman run --platform=linux/arm64 ubuntu:latest ls -l /usr/bin | awk '{sum += $5} END {print sum}'
29173962
$ podman run --platform=linux/riscv64 ubuntu:latest ls -l /usr/bin | awk '{sum += $5} END {print sum}'
22677127
One can see that amd64 and riscv64 are actually very close, with in fact a slight edge to amd64. Both are far ahead of arm64 though.
Please use `size`, it does matter.
It would literally change your conclusion here. RISC-V is denser than amd64; It's not even close.