So far, nearly all RISC-V is in the embedded space where everything is compiled from scratch, and a change to the ISA wouldn't have a huge impact.
Far more important to get it right for RISC-V phones/laptops/servers, where code will be distributed in binary form and expected to maintain forward and back CPU compatibility for 10+years.
Well that's only sunk cost if they assumed from the start that they were going to change the design to RISC-V AND drop the C extension. In that case, it was a rather risky plan from the start - assuming they can shift the industry like that. I'm guessing RISC-V was a change of direction for them and this would make things easier short term.
The time to 'get it right' has already passed IMO. If a hard ISA compatibility break happens at this stage, who is going to trust that it won't happen again?
The question is really about how far do they want to go beyond just disallowing page-crossing / cacheline-crossing instructions.
Personally, I always thought the C extension would have been much easier to implement if it had certain rules about it. Imagine looking at a random location in memory, how can you tell where instructions begin and end? You can't.
I want to see variable length instructions, but a requirement for instruction alignment.
Ie. every aligned 64 bit word of RAM contain one of these:
[64 bit instruction]
[32 bit instruction][32 bit instruction]
[16 bit instruction][16 bit instruction][32 bit instruction]
[32 bit instruction][16 bit instruction][16 bit instruction]
[16 bit instruction][16 bit instruction][16 bit instruction][16 bit instruction]
That should make decode far simpler, but put a little more pressure on compilers (instructions will frequently need to be reordered to align - but a review of compiler generated code is that that frequently isn't an issue)
I don't think we have real data on it, but I suspect that the negative impact of this would effect 16/32/48/64 way more than just 32/64.
Since if you have a 16 bit instruction, you know that it must be followed by another 16 bit instruction. Therefore, that 2nd instruction doesn't need the identifying bits. Or, more precisely, within a 32 bit slot, the 2^32 instructions possible need to be divided - and one way to do that is 2^31+2^30 possible 32 bit instructions, and 2^15 * 2^15 16 bit instructions. Now, the 16 bit instructions are only taking 25%, not 75% of the instruction space.
0000 -- 64-bit instruction that uses 60 bits
1100 -- two 32-bit instructions (each 30-bits)
1111 -- four 16-bit instructions (each 15 bits)
10xx -- one 32-bit instruction then two 16-bit instructions (xx are used by first instruction)
01xx -- two 16-bit instructions then one 32-bit instruction (xx are used by first instruction)
Alternatively, you view them as VLIW instruction sets. This has the additional potential advantage of some explicitly parallel instructions when convenient.Even with decent instruction scheduling, you are still going to end up with a bunch of instruction slots filled with nops.
And it will be even worse if you take the next step to make it VLIW and require static scheduling within a packet.
In the absolute worst-case scenario where you are blindly jumping to the 16-bit instruction in the 4th position, you only fetch 2-3 unnecessary instructions. Decoders do get a lot more interesting on the performance end as each one will decode between 1 and 4 instructions, but this gets offset by the realization that 64-bit instructions will be used by things like SIMD/vector where you already execute fewer instructions overall.
The move to 64-bit groups also means you can increase cache size without blowing out your latency.
VLIW doesn't mean strictly static scheduling. Even Itanic was just decoding into a traditional backend by the time it retired. You would view it more as optional parallelism hints when marked.
I'd also note that it matches up with VLIW rather well. 64-bit instructions will tend to be SIMD instructions or very long jumps. Both of these are fine without VLIW.
Two 32-bit instructions make it a lot easier to find parallelism and they have lots of room to mark exactly when they are VLIW and when they are not. One 32-bit with two 16-bit still gives the 32-bit room to mark if it's VLIW, so you can turn it off on the worst cases.
The only point where it potentially becomes hard is four 16-bit instructions, but you can either lose a bit of density switching to the 32+16+16 format to not be parallel or you can use all 4 together and make sure they're parallel (or add another marker bit, but that seems like its own problem).
I'd rather have an extra nop or two before jump targets than blindly throw 1-3 instructions worth of decoding bandwidth on jumps (which are often hot)
If you don't want to decode the extra instructions, you don't have to. If the last 2 bits of the jump are zero, you need the whole instruction block. If the last bit is zero, jump to the 35th bit and begin decoding while looking at the first nibble to see if it's a single 32-bit instruction or two 16-bit instructions. And finally, if it ends with a 1, it's the last instruction and must be the last 15 bits.
All that said, if you're using a uop cache and aligning it with I-cache, you're already going to just decode all the things and move on knowing that there's a decent chance you jump back to them later anyway.
Presumably your high performance core has at least three of these 64bit wide decoders, for a frontend that takes a 64bit aligned 192bit block every cycle and decodes three 64bit instructions, six 32bit instructions, twelve 16bit instructions, or some combination of all sizes every cycle.
If you implement unaligned jump targets, then the decoders still need to fetch 64bit aligned blocks to get the length bits. For every unaligned jump, that's upto a third of your instruction decode slots site idle for the first cycle. This might mean the difference between executing a tight loop in one cycle or two.
A similar thing applies to a low gate count version of the core, a design where your instruction decoder targets one 32bit or 16bit instruction per cycle (and a 64bit instruction every second cycle). On unaligned jumps, such a decoder still needs to load the first 32bits of the instruction first to check the length decoding, and waste an entire cycle on every single branch.
Allowing unaligned jump targets might keep a few NOPs out of icache (depending on how good the instruction scheduler is), but it costs you cycles in tight branchy code.
Knowing compiler authors, if you have this style of ISA and even it does support unaligned jump targets, they are still going to default to inserting NOPs to align every single jump target, just because the performance is notably better on aligned jump targets and they have no idea if this branch target is hot or cold.
So my argument is that you might as well enforce jump target alignment of 64 bits anyway. Allow all implementations gain the small wins from assuming that all targets are 64bit aligned, and use the 2 extra bits to make your relative jump instructions have four times as much range.
[jmp nop nop], [addi xxx]
OR
[xxx jmp], [nop nop addi]
OR
[xxx jmp], [unused, addi]
All of these tie up your entire decoder, but some tie it up with potentially useful information. That seems superior to me.
For conditional jumps (which are pretty common), the extra instructions in the packet will be executed whenever the branch isn't taken.
And instruction scheduling can actually do some optimisation here. If you have a loop with an unconditional jump at the end and an unaligned target, you can do partial loop unrolling, for example:
With [xxx, inst_1, inst_2], [inst3]...(loop body) ...[jmp to inst_1, nop, nop], you can repack the final jump packet as [inst_1, inst_2, jump to inst_3]
This partial loop unrolling actually is much better for performance than not wasting I-cache as it has reduces the number of instruction decoder packets per iteration by one. Compilers will implement this anyway, even if you do support mid-packet jump targets.
Finally, compilers already tend to put nops after jumps and returns on current ISAs, because they want certain jump targets (function entry points, jump table entries) to be aligned to cache lines.
Can't you pad it with nops up to the alignment boundary?
Even if there's not an explicit nop instruction in 16-bit and 32-bit variants (I don't know) there's surely something you can find that will have no side effects.
If you can make a compiler fill those NOP slots with useful instructions, then all the better.
It adds complexity for humans writing assembly code by hand, but that is a tiny minority of code now.
If the 16-bit instruction isn't 32-bit aligned, then the following instruction will be 32-bit aligned with no padding.
So, the question is equivalently: "Why would you ever want to add padding after a 16-bit instruction in order to force the next instruction to not be 32-bit aligned?"
Okay, for 1. you'd have to do two fetches to get stuff into the I-cache (but not if it's part of the same cache line, option 2.) and of course you're going to reduce instruction density because you're using up cache, but there's nothing you can do about that, but at least it would allow n-bit instructions to be genuinely n-bits long which is a big advantage.
That this hasn't been done before to my knowledge is proof that it's a rotten idea, but can the experts here please explain why – thanks
I think this is the big downside. You're effectively taking information which will always be needed at the same time, and storing it in two different places.
There is never a need for one piece of information without the other, so why not store it together.
Why not? as I said, so you can have full-length instructions!
And you can store it together in the same fetchable unit - the cache line (my option 2)
Start compressed instructions <size>
Compressed instructions
End compressed instructions <size>
Wouldn't that be to Huffman encode the instructions? Fixed table, but still, would save a lot of bits on the common instructions surely...
So it's been done, and is not a big deal.
The major question is how well RISC-V will manage disputes over this sort of thing without some group such as Qualcomm deciding to just release their version anyway.
To me, this looks like a reasonable argument over design decisions, where there are clear advantages and disadvantages to either side. It's basically a trade-off between code size and front-end complexity. Can you detail where exactly you see the purism thing being an issue?
I see now what you mean by posing this as a conflict between ISA purists (only provide load/store all other instructions have register or immediate operands, only provide one store and one load instruction, add compressed instructions to combat binary bloat) and ISA pragmatists (add new special-case instructions with memory operands and useful addressing modes).
Qualcomm must resolve this within RISC-V International somehow. Going its own way would designate those products as non-conformant to RV spec, not passing test suites, not allowed to carry RISC-V logo or claim "RISC-V compatible", etc. With all the software headaches that would result in. Or 3rd party vendors avoiding such Qualcomm products.
So this comes down to "convince majority of RISC-V members Qualcomm's proposal is better". Or failing that, just deal with it.
Whatever happens, chances are slim that backward compatibility with existing implementations & software would be broken @ this point. So creating some kind of alternative profile seems like the most sane option?
If Qualcomm’s offering is performant (in its dollars, power and speed mix) and Qualcomm keeps it open enough (I think this is at the moment, as they are using an instruction set that anybody can copy), would Qualcomm’s customers care about that? If so, would Qualcomm?
The largest possible concern I see is that customers would have to be convinced that Qualcomm can deliver good compilers that don’t inadvertently spit out instructions not supported by their somewhat off-beat hardware.
Second, the software ecosystem is huge, far more then compiles. And given how everybody today uses open source, making all that available for Qualcomm seems like a losing effort.
Is Qualcomm gone pay to make Android Qualcomm-RISC-V ready. Are they gone provide advanced verification suits. Formal analysis and all that stuff?
Qualcomm absolutely have the market power in the Android space to redefine a new open ISA if they want to though.
Only if Google allows them. Which is unlikely.
Only if they use the op space reserved for custom extensions.
If they don't and instead step all over space that belongs to C, then they would indeed not be RISC-V.
Which is what I understand they're doing. Thus could not be called RISC-V.
RISC-V has already been deployed widely, including 64-bit. Various medium performance cores are out there being used or are being interceded soon.
There are also various companies making high performance RISC-V designs, not a single one of them has suggested that RISC-V design isn't gone work well for their effort. In fact quite the opposite.
And then Qualcomm shows up making
> group such as Qualcomm deciding to just release their version anyway.
They are free to do so. The can even call it 'RISC-V' as long as the base ISA is ok. But its unlikely to be a standard.
It would be, very, very, very hard for them to make all the distros, compilers, and other tools available for their distribution. And Google isn't gone make Android available for Qualcomm specifically unless they get paid a lot.
There is a reason Qualcomm want to be the new standard, they know they can't finance all the software work themselves.
The reality here is not that RISC-V can't be competitive, but rather that Qualcomm doesn't want to invest lots of money in changing their designs to be 'RISC-V native' so they simply propose RISC-V to be almost exactly like AArch64. This seem to me to simply be a pretty transparent Qualcomm short term money saving effort that nobody else asked for.
Qualcomm's proposal to add complex addressing modes to RISC-V is a design that has been tested by time and is known to work. Apple (and now ARM, with X4) are using this ISA design to deliver enthusiast-level performance in a thermal envelope of a compact handheld device. It is not at all obvious to me that RISC-V, which requires the CPU to perform additional work to bundle operations for efficient execution, is capable of the same feat.
RISC-V compressed instructions are provably WAY less complex to decode than x86 and only a bit more complex than ARM64. Once you get past slicing apart the instructions, RISC-V decoders are much more simple than ARM64 because the stuff they are decoding is way less complex.
I also agree that RISC-V decoders are simpler but only because the base ISA itself is very limited. Once you add functionality like FP, atomics, Zb extension, vectors etc… there is not that much difference. And the need to do fusion for address computation adds another layer of complexity on top.
https://chipsandcheese.com/2021/07/13/arm-or-x86-isa-doesnt-...
Addressing modes have never been mentioned as a limiting factor. Its not clear at all that addressing modes are a game changer for performance.
You can also argue that any other unique feature of ARM or x86 is the 'magical pile' that allows for much higher performance. The more reasonable assumption to me is that its simply about how much is invested to make it happen. I think based on its design, less investment into RISC-V will lead to a higher performance core compared to ARM because of that complexity.
Qualcomm motivation here seems pretty clear, and I don't believe its actually because they have pure technical merit at the heart of their desires.
So should I really believe the company that has clear financial motivation to push their line?
What's the evidence that the existing RISC-V approach is not competitive, and thus that Qualcomm's changes are necessary?
It would seem to take RISC-V closer to AArch64 in approach?
Qualcomm's proposal is all instructions are aligned to their size. Initially that means everything is a 32-bit instruction, now with a lot more green-field encoding space to play with (so less need to have larger instructions). 64-bit instructions would be introduced (aligned on a 64-bit boundary) when needed with the expectation they'd be used for rare operations and 48-bit instructions wouldn't happen.
The SiFive (and original RISC-V architects view) is RISC-V is meant to be a variable length instruction set and a mix of 16/32/48 provides better static code size along with better dynamic code size meaning smaller icaches needed, smaller buffers in fetch units etc.
Interesting that the architecture that was meant to be a 'purer' RISC implementation than ARM is pushing towards the more CISC style variable length instructions. In a sense Qualcomm are trying to keep it closer to the RISC ideal!
What does 'rare' mean here, does it mean rare in execution, or rarely appears in code? (The difference being that something might only appear once in your code but be part of your hot loop so be executed any number of times)
If they are rare in execution, what is their value over composing them of 32-bit instructions, where the (rare) overhead of doing so would be typically a amortised away?
(The only thing I can think of that 64 bit instruction seem suited to is some kind of internal CPU management instructions, but context switches etc. are relatively rare & very expensive anyway so... I don't know)
> There are several 48-bit instruction possibilities.
> 1. PC-relative long jump
> 2. GP-relative addressing to support large small data area, effectively giving GP-relative access to entire data address space of most programs
> 3. Load upper 32-bits of 64-bit constants or addresses
> 4. Or lower 32-bits of 64-bit constants or addresses
> 5. And with 32-bit mask
> 6. More effective ins/ext of 64-bit bit fields
Another thing thats offten discussed is moving the vtype and setvl into each vector instructions, I'm not sure if that requries 48 or 64 bit instructions.
> 1. PC-relative long jump
My understanding is that these are rare
> 2. GP-relative addressing to support large small data area, effectively giving GP-relative access to entire data address space of most programs
What is 'GP' here? but "...access to entire data address space of most programs" In this case you are just going to be bouncing all over the address space, substantially missing any level of cache much of the time, surely?. Maybe you get a little extra code density but you aren't going to get any extra speed to speak of.
> 3. Load upper 32-bits of 64-bit constants or addresses
> 4. Or lower 32-bits of 64-bit constants or addresses
> 5. And with 32-bit mask
Well yeah, but how common is this? I understand the alpha architecture team looked at this and found it uncommon which is why they were okay with less-than-32-bit constants. If it really speeded things up you might build a specific cache to store constants (a kind of larger, stupider, register set). It would seem a simpler solution.
I'm not sure what you mean with 6, and I'm not familiar with vtype/setvl
Depends on how many bits you had to start with. On Power ISA they aren't common either, but when they happen you need up to seven instructions (lis, ori, rldicl, oris, ori, then for branches mtctr/b(c)ctr) to specify the new address or larger value. Most other RISCs are similar when full 64-bit values must be specified. This is a significant savings.
Often one vsetvl stays valid for multiple/most/all instructions, but sometimes there's a need to toggle it for a single instruction and then toggle it back. With 48-bit or 64-bit instructions, such temporary changes could be encoded in the operation instruction itself.
Additionally, masked instructions always mask by v0, which could be expanded to allow any register (and perhaps built-in negation) by more instruction bits too.
You could have a lot more registers.
The first example, I'm not sure you'd want a full 64bit encoding space. You still aren't going to be able to load a 64bit immediate directly so I'd rather see an instruction that uses the next instruction as the immediate. But then 50% of the time you're still going to be padding this to 64bit alignment, so it's unclear to me that this is a benefit over 2 lots of the same but with 32bit immediates.
The second option is interesting. But if you've got 256 addressable registers say, what use are the 32 and 16 bit instructions that can only address a tiny proportion of those registers.
On the flip side, the ability to have more registers is very good for SIMD/GPU applications.
But if you have a processor that also uses 16 bit instructions those extra registers become unusable. Thumb can't encode all registers in all instructions so you have the high registers that are significantly less useful than the low registers.
X86 is the same, never really done 64bit ASM so I don't know if they improved that.
So then you may aswell just divide up the registers so you've got 16 general purpose registers and 16 registers for simd or whatever.
I suppose the os could reserve registers for itself to save swapping in and out quite so often.
Register windows for applications/functions/threads.
Or maybe something radically different, like get rid of the stack, and treat them conceptually like a list?
Very revealing, thanks, this had never occurred to me
https://www.talospace.com/2021/04/prefixed-instructions-and-...
The initial idea of RISC-V was pretty much, a variable length RISC isa, but sane and easy to decode. That is not the x86, "we need to add yet another prefix".
The compressed 'C' extention is designed to re-use a lot of the existing decode infrastructure. On RV32 the C instructions are a strict subset of the full length instructions, so at least on RV32 it is very light weight, and it adds barely any logic to a core. It's almost always worth it to turn on C extensions versus making the cache bigger or trying to speed up main memory.
In my experience I-cache pressure is real, especially on lightweight implementations that don't have multiple levels of cache hierarchy and huge amounts of associativity to reduce the impact of an instruction cache miss.
I have played with both C and non-C variants, and also played with compiler tuning that saves code size versus 'performance' (which includes loop unrolling and thus more I cache misses). Generally smaller code size is better for power and system complexity, while keeping performance at par. Of course if you aren't as restricted on power or complexity (as is the case on a high end CPU), the calculus is different.
This kind of simplicity to me embodies the heart of RISC. If your CPU is hitting cache lines more often, you don't have to speculate as deep, don't have to re-order as much, thus less logic, less complexity, less power, higher clock rates and fewer side channels.
On the other hand, I suppose if you are already committed to deep speculation and out of order, compressed instructions might extract a disproportionate cost, maybe less so in decode and more so in precise exception handling and in tricks like register renaming.
Yeah I'd tend to agree, in particular x86 variable length encoding is a lot more complex than the RISC-V encoding!
What I'm really getting at is CISC and RISC aren't well-defined things and it's interesting seeing how the design of RISC-V is getting pulled in different directions.
> so at least on RV32 it is very light weight, and it adds barely any logic to a core. It's almost always worth it to turn on C extensions versus making the cache bigger or trying to speed up main memory.
Definitely, and the Qualcomm proposals are that things should stay that way for RV32/low end in general. It's high-end RV64 they care about.
> On the other hand, I suppose if you are already committed to deep speculation and out of order, compressed instructions might extract a disproportionate cost, maybe less so in decode and more so in precise exception handling and in tricks like register renaming.
This is the root of it. It's easy enough to do a study demonstrating changes in static code size, also easy enough to build a low-end RISC-V processor and examine the trade-offs. It's all a lot more complex at the higher-end especially as high-end RISC-V cores are far from mature.
Is this an exaggeration, or were there ever such ISAs?
IMO it's futile effort that unnecesarily taxes the big codes.
This really just seems to be Qualcomm wanting it to be more like ARM so they can use their existing cores. That seem pretty clear from what they are proposing.
Does Qualcomm have a concrete proposal for how their version of compressed instructions would work, or is the idea more or less just "the C extensions but 32-bit instructions must be 32-bit aligned"? Have they published details somewhere?
It adds new addressing modes, and things like load/store-pair instructions.
(I would mind that future though.)
Not an issue; Qualcomm is a member of RISC-V, thus it has signed the agreement. It has legalese designed to prevent this and further entire categories of legal issues.
I know there's the big discussion about macro-op fusion. But in hindsight, I think a big motivator for C -- implicit or not -- was the fact that on the very low-end microcontroller or in the softcore (FPGA) world, you typically have disproportionately low amounts of SRAM available versus compute fabric. Those were the initial deployment targets (and initial successful deployments!) for RISC-V, since you need tons of extra features for "Application Class" designs. These cores often have a short pipeline and are completely in-order, so their cost and verification effort are much lower. These are (very likely) not going to implement macro fusion, at least on the medium-low end. So, increasing the effective size of the I-cache through smaller opcodes is often a straight win to increase IPC. On the other hand, Application Class designs today are typically OoO, so they achieve high IPC while still hiding miss latencies pretty effectively; smaller instructions are still good but the benefits they provide aren't as prominent. And it does use a ridiculous amount of opcode space, yes.
Another big issue for Application Class systems IIRC -- unrelated to all this -- is that I don't think hardware implementing C can actually run binaries compiled without it. This means Linux distros cannot compile for the lowest common denominator of "no C extension" and ship it to all users. It is effectively a fundamental ABI break, so they must ship packages for users with C, and a whole different set of packages for users without. That is... really not ideal. In practice having to maintain a whole different ABI for things like this means few people do it, it bitrots, and the quality is overall lower everywhere.
I wonder if they would have just been better off copying one of ARM's design principles from the very start: actual design families akin to the -M, -R, and -A series of ARM processors, created for different actual design spaces. These could actually be allowed to have (potentially large!) incompatibilities between them while still sharing a lot of the base instruction set and privileged e.g. PMP extensions could probably exist among all of them. I'd be happy to have an "Application Class" "-A series" RISC-V processor that could run Linux but didn't have compressed instructions or whatever; it probably has enough cache and OoO capacity to handle it.
I believe this is incorrect? I believe RV{32,64}-with-C is simply a superset of RV{32,64}-without-C. Now I have only implemented RV32I, so I'm not that familiar with the C extension or other extensions for that matter, but in my digging through the various RV specs, I haven't found anything which suggests that implementing C requires breaking code compiled without the use of C.
Do you have any details?
Wish I could use strike-outs here, but oh well.
i̵t̵ ̵t̵u̵r̵n̵s̵ ̵o̵u̵t̵ ̶t̶h̶e̶r̶e̶ ̶a̶r̶e̶ ̶d̶i̶f̶f̶e̶r̶e̶n̶t̶ ̴s̴t̴r̴i̴c̴t̴ ̴t̴h̴r̴o̴u̴g̴h̴ ̴s̴t̴y̴l̴e̴s̴
and the font HN uses doesn't handle any of them well
> The C extension is compatible with all other standard instruction extensions. The C extension allows 16-bit instructions to be freely intermixed with 32-bit instructions, with the latter now able to start on any 16-bit boundary [...]
Consider also a 16-bit aligned 32-bit instruction crossing a page boundary, potentially with different access permissions. This type of bug allowed userspace applications to hang early Cortex-A8 based phones(ARM errata 657417).
(This mostly applies to the high end, but designers on low-end devices may be more SRAM-constrained)
Anyway, all that aside, I personally wouldn't be sad to see the C extensions go away. I'm actively designing a RISC-V core for a small game console, and probably won't implement them, and will spend the time on more DV instead. I don't expect them to have any meaningful benefits for my case and mostly increase frontend complexity.
Not too long ago, Qualcomm bought NUVIA, a designer of high performance arm64 cores that can theoretically compete with Apple cores on perf. Arm pretty much immediately sued saying that the specifics of the licenses that Qualcomm and NUVIA have mean that cores developed under NUVIA's license can't be transferred to Qualcomm's license.[0] Qualcomm obviously disagrees. Whatever happens those cores as they exist today are going to be stuck in litigation for longer than they're relevant.
Qualcomm's proposal smells strongly like they're doing the minimum to strap a RISC-V decoder to the front of these cores. For whatever reason the seem hell bent on only changing the part of the front end that's the 'pure function that converts bit patterns of ops to bit patterns of micro-ops'. Arm64 is only 32bit aligned instructions, so they don't want to support anything else.
At the end of the day, the C extension really isn't that bad to support in a high perf core if you go in wanting to support it. The canonical design (not just for RISC-V but high end designs like Intel and AMD too) is to have I$ lines fill into a shift register, have some hardware on whatever period your alignment boundary is that reports 'if an instruction started here, how long is it', and a second stage (logically, it doesn't have to be an actual clock stage) that looks at all of those reports generates the instruction boundaries and feeds them into the decoders. At this point everything is also marked for validity (ie. did an I$ line not come in because of a TLB permissions failure or something).
[0] - https://www.reuters.com/legal/chips-tech-firm-arm-sues-qualc...
Though, I suspect that's easy problem to fix. The more pressing issue is what happens after the decoders. I understand this is a very wide design, decoding say 10 instructions per cycle.
There might be a single 16bit instruction in the middle of that block 40 bytes, changing the alignment halfway though. To keep the same throughput, Qualcomm now need 20 decoders, one attempting to decode on every 16bit boundary. The extra decoders waste power and die space.
Even worse, they somehow need to collect the first 10 valid instructions from those 20 decoders. I really doubt they have enough slack to do that inside the decode stage, or the next stage, so Qualcomm might find them selves adding an entire extra pipeline stage, (probably before decode, so they can have 20 simpler length decoders feeding into 10 full decoders on the next) just to deal with possible misaligned instructions.
I don't know how flexible their design is, it's quite possible adding an entire extra pipeline stage is a big deal. Much bigger than just rewriting the instruction decoders to 32bit RISC-V.
> I don't know how flexible their design is, it's quite possible adding an entire extra pipeline stage is a big deal. Much bigger than just rewriting the instruction decoders to 32bit RISC-V.
I'm sure it is legitimately simpler for them. I'm not sure we should bend over backwards and bring down the rest of the industry because they don't want to do it. Veyron, Tenstorrent were showing off high perf designs with RV-C.
For an 8-wide or 10-wide design, the propagation delays are getting too long to do it in all in single cycle. So you need the extra pipeline stage. The longer pipeline translates to more cycles wasted on branch mispredits.
RISC-V code is only about 6-14% denser than Aarch64 [1], I'm really not sure the extra complexity is worth it. Especially since Aarch64 still ends up with a lower instruction count, so it will be faster whenever you are decode limited instead of icache limited.
> Adding complexity to the I$ hasn't even made sense for x86 in two decades
Hang on. Limiting the Icache to only 32bit aligned access actually simplifies it.
And since the NUVIA core was originally an aarch64 core, why wouldn't they optimise for hardcoded 32bit alignment and get a slightly smaller Icache?
Even x86 only reads 16 or 32 byte aligned fields out of the I$, then shifts them. There's not extra I$ complexity. You still have to do that shift at some point, in case you don't jump 32 byte aligned address. You also ideally don't want to only hit peak decode bandwidth starting on aligned 32 byte program counters, so that whole shift register thing is pretty much a requirement. And that's where most of the propagation delays are.
> RISC-V code is only about 6-14% denser than Aarch64 [1], I'm really not sure the extra complexity is worth it. Especially since Aarch64 still ends up with a lower instruction count, so it will be faster whenever you are decode limited instead of icache limited.
There's heavy use of fusion, and fwiw, the M1 also heavily fuses into micro ops too (and I'm sure the AArch64 morph of NUVIA's cores do too).
https://en.wikibooks.org/wiki/MIPS_Assembly/Instruction_Form...
https://valsamaras.medium.com/arm-64-assembly-series-branch-...
And it's not only a way of decreasing code size. It help with security too. If you can have an innocuous looking bit of binary starting at address X that turns into a piece of malware if you dump to instruction X+1 that's a serious problem.
https://mainisusuallyafunction.blogspot.com/2012/11/attackin...
RISC-V, I'm pretty sure, enforces 16 bit alignment and is self synchronizing so it doesn't suffer from this despite being variable length. But if it allowed the PC to be pointed at an instruction with a 1 byte offset then it might be.
As far as I'm aware every RISC ISA that's had any commercial succss does this. HP RISC, SPARC, POWER, MIPS, Arm, RISC-V, etc.
> It help with security too. If you can have an innocuous looking bit of binary starting at address X that turns into a piece of malware if you dump to instruction X+1 that's a serious problem.
JIT spraying attacks work just fine on aligned architectures too, hence why Linux hardened the AArch64 BPF JIT as well: https://linux-kernel.vger.kernel.narkive.com/M0Qk08uz/patch-...
Additionally, MIPS these days has a compressed extension to their ISA too, heavily inspired by RV-C. https://mips.com/products/architectures/nanomips/
And NanoMips requires instructions to be word aligned just like everybody else, it's just that it requires 16 bit alignment rather than 32. Attempting to access an odd PC address will result in an access error according to this:
https://s3-eu-west-1.amazonaws.com/downloads-mips/I7200/I720...
That's the same as RV-C.
I don't have a strong opinion on whether the C extension is a net good or bad for high performance designs, but I do strongly believe that it comes with costs as well as benefits.
https://project-archive.inf.ed.ac.uk/ug4/20191424/ug4_proj.p...
Since then, RISC-V has added a bunch more instructions that ARM/x86 already had which has made RISC-V even smaller relative to them.
That's the point? You can go faster if everything is 32-bit aligned, i.e. you don't have variable length instructions.
Hmm.. At about the same time as the proposal to drop C from RVA, Qualcomm also proposed an instruction-set extension [1] that smells very much of ARM's ISA (at least to my nose). It also has several issues to criticise, IMHO.
[1] https://lists.riscv.org/g/tech-profiles/attachment/332/0/cod...
But you need most of that anyway because you need to handle program counters that aren't 32 byte aligned, so you need to either do it before hitting the decoders, or afterwards when you're throwing the micro-ops into the issue queues (which are probably much wider and therefore more expensive).
That doesn't seem correct. I think adding and dropping C for desktop/server workloads would be relatively easy. Most of what will be run on it is either open source (Linux, Apache et al) or Java/Python/Go/.Net. Either way, I'd expect Oracle or somebody to support both with a single installer. This isn't x86 where there is a lot of binaries with no source, or lots of janky code that assumes x86 that we need backward compatibility. (Note: IIRC RV32A is the "application" profile, not for embedded where hand tuned assembly is a real thing, things are much more fragile there).
That said, just like Linux supported multiple x86 based platforms (PC-98), I'd imagine Debian and others would support non-C processors with distros, so I don't think it would really hurt Qualcomm if it's kept and they don't include it.
> They also say that there's lots of implementations with C in already, so backing out of it now disadvantages those implementations.
Ugh. So we should hold hold onto something, even if it is a bad idea just because other people wasted time on it? That seems like a very crab-bucket mentality. Not saying it should be removed, but the decision should be technical, not based on favoring certain players.
So, if someone forks the arch such that two different distros are required based on HW, its just going to fragment the distro's too because some of them will just pick one or the other profile.
It's not super easy because standard RISC-V without the c extension balloons code size by about 30%. So Qualcomm is proposing a set of custom extensions on top of that to get the code size back down. It's not clear what the patent situation is on those extensions since they're so obviously AArch64 inspired.
Stated in the reverse, removing compressed instructions would increase the kernel size by 50%.
https://people.eecs.berkeley.edu/~krste/papers/EECS-2016-1.p...
p. 51
> each RVC instruction must expand into a single RISC-V instruction. The reasons for this constraint are twofold. Most importantly, it simplifies the implementation and verification of RVC processors: RVC instructions can simply be expanded into their base ISA counterparts during instruction decode, and so the backend of the processor can be largely agnostic to their existence. ... This constraint does, however, preclude some important code size optimizations: notably, load-multiple and store-multiple instructions, a common feature of other compressed RISC ISAs, do not fit this template. ... Given these constraints, the ISA design problem reduces to a simple tradeoff between compression ratio and ease of instruction decode cost. ... The dictionary lookup is costly, offsetting the instruction fetch energy savings. It also adds significant latency to instruction decode, likely reducing performance and further offsetting the energy savings. Finally, the dictionary adds to the architectural state, increasing context switch time and memory usage.
They have never build a core comparable to the fastest current Arm, x86 cores because they haven't been moving up the performance curve for very long. Just five generations at this point.
October 2017: U54, almost A53 competitive despite being single-issue
October 2018: U74 (dual issue), A55 class
October 2019: U84 (OoO), A72 class
June 2021: P550, A76 class
December 2021: P650, A78 class
October 2023: P870, Cortex-X3 class
SiFive can't talk a lot about power consumption because that depends not only on the core design but the entire SoC, the process node, the corner of the process node, the physical design and many other things that are under the control of SiFive's customers, not SiFive.
After all the Vector, AI, and graphics stuff is hashed out I'd like to see a RISC-VI with all the same specs but totally redone instruction encoding. But maybe that's just me.
Not sure what you mean here: opcode space has to have been reserved for the C extension from the start, that part can’t have been an afterthought. It may have been badly designed still, but if so that must be for other reasons (working from a bad code sample is often cited).
> The encoding of constants into the 32-bit instruction word is really ugly and also wastes opcode space.
It kinda has to be to minimise fanout, and with it propagation delays and energy consumption. As a software guy I recoil in horror, but I can’t argue against faster and more efficient decoders. https://www.youtube.com/watch?v=a7EPIelcckk
> I'm also a fan of immediate data (after the opcode), which for RISC-V I would have made come in 16,32,64 bit sizes.
So was I, before I read the RISC-V specs. One possible disadvantage of separate immediate data is wasting instruction space (many constants are so much closer to zero than 127), making alignment issues even worse, and it could increase decoding latency. I would definitely do this for bytecode for a stack machine meant to be decoded by software, but for a register machine I want to instantiate in an FPGA or ASIC, I would think long and hard before making a different choice than RISC-V.
I understand that "afterthought" can be more of a subjective comment on the design than a concrete claim about the order of events, but still I'll quote directly from the RISC-V Instruction Set Manual:
> Given the code size and energy savings of a compressed format, we wanted to build in support for a compressed format to the ISA encoding scheme rather than adding this as an afterthought
Maybe I should weigh in on this issue in the official channels.
[0] https://people.eecs.berkeley.edu/~krste/papers/EECS-2016-1.p...
The analysis from the SiFive folks generally doesn't include that compensation factor: it just involves a straight find-and-replace in the binary.
First, you context switch a lot to and from the Linux kernel, so decreased cache pressure does matter.
Second, if you have proof that loops predominantly consist of 32-bit instructions, prove your case. To my mind, a loop is likely to use fewer registers and likely to have shorter branches and smaller immediate values. all of these seem to favor compressed instructions actually favoring working code even MORE than general code.
As to what you suggested, I have actually started putting something together to possibly send to the RISC-V foundation from my own experience implementing RISC-V designs, but pretty much nobody is asserting that loops are predominantly 32-bit instructions. Tight loops are often already sitting in a uop cache once you get to a core of reasonable size, so compressed vs uncompressed is completely irrelevant. Contrary to what you seem to be hoping for, correct arguments about working set size and performance are very subtle.
The C instructions aren't free in frequency terms, either. You have significant complexity increases in decoders and cache hierarchies to support them. Making that cost add up to 3% is not that hard.
Andrew at SiFive disagrees vehemently[0].
>Qualcomm is definitely not alone
It's also worth noting that they tried to appropriate Rivos's opinion, only to be called out[1].
0. https://lists.riscv.org/g/tech-profiles/topic/slides_on_reta...
1. The variable length instructions (currently 16 bit or 32 bit but 48 bit on the horizon) complicate instruction fetch and decode and in particular this is a problem for high performance RISC-V implementations.
2. The C extension uses 75% of the 32-bit opcode space, this can be put to better use.
They're saying the benefits from the C extension don't outweigh the costs. They're also saying that if you move forward with the C extension in RVA23 now there's no real backing out of it. As the software ecosystem develops removing it once it's baked in just won't be possible. However adding it back in later is more feasible.
SiFive strongly disagree. They believe the C extension is worth the cost and that it doesn't prevent you from building high-performance cores. They also say that there's lots of implementations with C in already, so backing out of it now disadvantages those implementations.
It could end up in being the first major fragmentation in the eco-system Qualcomm go one way and SiFive the other (other companies also sit on one side or the other of this debate but Qualcomm and SiFive are driving it). Indeed the latest proposal from Krste is to do just that with a new 'RVH23' profile..
I wonder how much this development has been driving SiFive's thinking here? Clearly they are under pressure to deliver to their investors so you can see why they want to keep things as they are rather than consider a big change. Good for SiFive, but good for the long-term RISC-V ecosystem?
Edit: If you want the details check out the publicly readable tech-profiles list: https://lists.riscv.org/g/tech-profiles/messages they've got recordings of the last two meetings that discussed the issue and presentations, all available via that message archive.