RISC-V is designed with great care taken to weight all decisions as not to hamper any scope of implementation, from the lowest power microcontrollers to the fastest supercomputers, and everything in between.
This is unlike e.g. ARMv8/aarch64, which imposes too much complexity (>700 instructions) to small implementations, and hampers all implementations due to low code density.
In small implementations where performance isn't paramount but 64bit is required (for e.g. addressing, as is the case in many specialized tiny cores embedded in larger SoCs), bad code density increases ROM and/or RAM requirements as code takes more space, thus area and power. If 64bit is not needed, aarch32 can be used, which has much higher density, yet as of recent B and Zc extensions, RISC-V is better still, and can be tailored to the requirements, down to as simple as ~42 instructions and 16 registers (rv32e).
In large implementations, such as Apple's M1/M2, aarch64's awful code density imposes a large L1$ code cache in order to keep the pipelines fed. Large L1$ are very costly, as latency, area and power go up quickly with cache size, and maximum clock drops as size increases. Keep in mind that, in current fab nodes, SRAM is far more costly than logic
In contrast, RISC-V offers industry-leading code density through a form of variable instruction size that takes great care to not complicate decoder parallelism. There are already multiple large scale RISC-V cores announced with 8-wide decode (like Apple M1/M2).
RISC-V was consistently designed so that the simplest implementations remained as simple as possible. When design tradeoffs meant more complexity for more performant designs, any possible detriment was determined to be unimportant.
Notoriously, this is the whole "big cores can fuse instructions; that's got to be free for them, right?" Though the V extension also has a couple of fun ones with re-using v0 for masking (oh a big core can just keep track of whether it's a mask or not and switch which set of registers it's renaming from) and vsetvl (yeah let's make a big core speculate what effect it'll have on subsequent instructions)
> aarch64's awful code density
All the code density comparisons I've seen have been static. Do you know of any dynamic comparisons? I suspect 10% less dense code than RISC-V C (but still denser than x86-64) isn't the primary reason for having 3x more L1I than I think any RISC-V or x86 design currently has...
And even then, ARM for instance decided it was worth spending 50% of L1I cache area on a MOP cache for various reasons, a key one being that their implementation shaves off a cycle in branch mispredicts. If there's no predecode info stored in cache, I can easily see a predecode stage adding a cycle to the pipeline depth over assuming fixed-length. And if it is stored in SRAM you lose some of the codesize benefits...
For something very simple, an in-order 32-bit processor with nothing but simple integer features, RISC-V does appear to be a bit simpler to implement than the current embedded ARM instruction sets. Less state, fewer and more regular instructions. In a large machine throwing away some tens of thousands of transistors on that is an irrelevance. But in a 10 cent microcontroller it is not. As 32-bit and 64-bit computing displaces the 8-bit embedded world, I think RISC-V has a small technical advantage in that area at least.
With all due respect, this is complete nonsense. You keep making this claim, yet have never produced any credible evidence other than a single bogus «ls -l /bin/bash» ages ago for three – x86, aarch64 and RISC-V (was it RISC-V 64 or 32?) – Linux distributions where the code was most likely compiled with «-O2 -g». If you want to substantiate your claim, you had better provide an objdump output for the _text/.text section that will reveal the actual (i.e. pure) code size footprint.
Let's pick this apart. The same random C++ code compiled with the same GCC v12.2.0 for 1) aarch64 (a generic 64-bit aarch64), 2) RISC-V (a generic 64-bit target), and let's also throw 3) POWER64 and 4) MIPS64 in for giggles:
– aarch64: https://godbolt.org/z/r4WPPj8nG
– RISC-V: https://godbolt.org/z/sr6nb8sTc
– ppc64: https://godbolt.org/z/PMrhe5W8z
– mips64: https://godbolt.org/z/sdKjncEfq
If we filter out empty lines, labels, assembly pseudo-instructions and directives, we get the following results (in the ascending order) as the number of actual instructions for each ISA:
aarch64:
$ rg -cv '(^ *\.)|(^$)|(^[ A-Za-z0-9\(\)_]*:)' /tmp/meta.arm64-v8a.S
814
RISC-V 64-bit:
$ rg -cv '(^ *\.)|(^[ A-Za-z0-9\(\)_]*:)' /tmp/meta.risc-v.S
860
ppc64:
$ rg -cv '(^ *\.)|(^$)|(^[ A-Za-z0-9\(\)_]*:)' /tmp/meta.ppc64.S
945
mips64:
$ rg -cv '(^ *\.)|(^$)|(^[ A-Za-z0-9\(\)_]*:)' /tmp/meta.mips64.S
1030
aarch64 has, in fact, the lowest instruction footprint and came out at the top, with RISC-V trailing behind (but being comparable nevertheless). Surprisingly, ppc64 did more than 10% worse than aarch64, and mips64 came in last (unsurprisingly).Conclusion: aarch64 does not possess the «awful code density» and is comparable to or better than that of the RISC-V 64-bit architecture.
Even if your comparison was valid, it is affected by the quality of the compiler - current compilers are not very good at exploiting RISC-V compressed instructions and generally have not had as much attention as on older architectures.
Also, RISC-V compressed instructions are RISC-V 32-bit, therefore a comparison with 64-bit architectures is not applicable.
No, both 32-bit and 64-bit RISC-V have compressed instructions, and they're nearly identical (IIRC, there's one compressed instruction which is different).
Wait WHAT? I strongly suspect you're confusing me with somebody else I am not.
Please use `size` from binutils, if you're trying to compare code size of binaries.
-----------------
For the above code segment, though I selected the compile-to-binary option, and the program failed to compile. So I don't know what's up with that.
On a relatively short chunk of C++ code, you can see the compressed (16-bit) instructions in the binary output:
https://godbolt.org/z/jzjzrrfMP
Some instructions, such as the ones with a larger immediate load, are 32-bit.
Same version of GCC / G++ as the above.
The actual code size of the ARM64 is bigger:
https://godbolt.org/z/Wq7jKz5rY
I'm not sure why all the NOP instructions are in there, but even discounting that, all the generated machine instructions are 32-bit. Is there another option for GCC that will help? Or am I mistaken in thinking there is a Thumb2 equivalent for this target?
Edit: Answer: No. A64 is fixed-length:
https://developer.arm.com/documentation/den0024/a/An-Introdu...
The piece of C++ code I have uploaded to GodBolt was floated on HN a few years ago as an instance of the template metaprograaming that crashed one C++ compiler and resulted in an OOM process kill for another (for the record, it was clang++ and g++ although I can't remember which one did what). Both compilers have been fixed since then, but the code is still interesting due to it being: a) terse, and b) resulting in a unusually large number of [mostly useless] numeric computations that a C++ compiler yields by virtue of the template expansion. So I have retained a copy of the mischievous code. The template expansion is common in C++ template metaprogramming, and is extensively used in C++ scientific libraries (e.g. BLASS as well as in others).
When it comes to the generated code footprint comparison, the numeric computations are a good metric to assess.
cross g++ version 8.4.0 for aarch64: 13712 bytes
native g++ version 11.3.0-3 for RV64GC: 13968 bytes
... but that includes a bunch of startup stuff, so that's not a good measure.
Modifying the code to generate binary on godbolt:
ARM64, 3816 bytes: https://godbolt.org/z/vn3de5nEc
RV64GC, 3226 bytes: https://godbolt.org/z/4ovMTaxo9
So an 18% increase in code size for ARM 64-bit vs RISC-V 64-bit. As you can see in the generated code, there are 16-bit compressed instructions by default with the RV64GC binary.
I have also noted that compressed and uncompressed instructions are unevenly interleaved in the generated code, and occur in random sequences, i.e. addresses 0x10a60 ÷ 0x10a72 follow the 16-32-16-16-16-16-32-16 bit sequence whereas most other instructions follow 16-32 bit or 16-32-32 / 16-16-32 bit sequences. I wonder what performance penalty for the instruction decoding unit in hi-perf RISC-V cores is going to be for compressed vs uncompressed instructions code especially if the instruction being decoded spills over into the next L1 i-cache line (or, worse, into another memory page). RISC architectures are generally averse to misaligned memory access.
Compressed instructions are available for 32-bit (RV32) and 64-bit (RV64). The compressed instructions differ slightly between the two though. See my other comment in this thread.
Also, according to https://wiki.riscv.org/display/HOME/Specification+Status, code size extensions are in a review state slated for a ratification in Q1 2023, with «ABI issues being worked» mentioned in the notes. My interpretation is that the compressed instructions extension is still work in progress, and is a moot point to discuss at this very moment.
The compressed instructions extension (the C extension) has been finished and standardized for a long time. In fact, all mainstream desktop Linux distributions which have ported to RISC-V have standardized on RV64GC (aka RV64IMAFDC, with the extensions I, M, A, F, D, C), that is, they require compressed instructions.
C was ratified years ago, but I suspect you might have heard about Zc (about to be ratified, specific to 32bit, particularly helpful to microcontrollers) or B (ratified recently, bit manipulation, happens to help code density), and their effect in code density.
As an example Apple designed their processors from scratch, they do not use any core designs provided by the ARM holdings corporation. Yet they still have to pay ARM a lot of money because they use their instruction set.
The instruction set is a very sticky thing. All the software gets written for a particular instruction set, and people like to keep using previously written software. Thus, software stickiness causes CPU ISA stickiness which gives companies that control that particular ISA a lot of power. And they of course monetize that power by charging extra for their CPUs.
With an open ISA, anybody can make processors that use the same instructions, which means that your software should work on any CPU designed for that particular ISA. Thus, all the talented hardware designers in the entire world can work to make RISC-V processors without asking for permission. They are free to charge for their designs or make them open. And if you write RISC-V code, it can execute on any of those processors or any future ones anyone decides to develop. (There is a bit of complication here in that the RISC-V ISA includes several different combinations for instructions for different use cases, but they are all open).
Is this true of Apple? They co-founded ARM, I thought it was believed they had a perpetual license.
This is great but if one or all the parties in a commercial war decide that your product cannot cross a border, an open source design won't save your business.
Patterson, I believe, has said that folks like Intel intentionally do this, add rather pointless new patented instructions so that they are always "embracing and extending" (my words) their own designs to prevent true compatibility; and thus cloning by others. And that that's one big reason why it was worth creating RISC-V.