X86 instructions are variable-length: 1-15 bytes. And you do not know how long any instruction is until you read almost every byte of it (there is no length byte up front - it is just a list of prefixes). Thus to make a 4-wide decoder you need to ingest up to 60 bytes, and your decoder cannot start on instruction #2 till it finds the end of # 1. (Or guess, and rewind as needed). Much harder and a rather long dependency chain for decoder #4 to even get started.
Now say you want to go wider: 8-wide.
Arm64: copy and paste same decoders again. Done
X86: Now you need to ingest 120 bytes, and find even more boundaries, each depending on the previous - an even longer dependency chain. Decoder #8 cannot start till #1 - #7 are almost done. A mess nobody has managed to solve yet.
What an x86 processor does is fetch an entire cacheline of instructions at once. Working out which byte corresponds to instructions is a straightforward state machine--and state machines can be parallelized as reduction tree, because function application is associative! (This is usually described as "x86 processors start decoding, in parallel, every byte as if it were the start of a new instruction.) My guess for x86 is that resolving instruction length takes a single cycle, a single stage of the instruction decode pipeline.
It's actually hard to come up with x86 instructions that are 15 bytes long. Your typical general-purpose instruction is going to max out around 4 bytes (REX + other prefix + opcode + ModR/M byte), and even that's a little long. A "complex" memory instruction that moves a 4-byte constant into a register + constant offset will manage 12 bytes, but that's really the limit of practical instruction. So there's no point building a decoder capable of ingesting that many extra-long instructions a cycle because it is so synthetic a use case.
Sure, you could design a better compressed ISA for decoding than x86, but the impact of its complexity on processors is vastly oversold. Just look at how much die space the x86 decoders actually take up to see how little of an impact it has.
And how many instructions fit into it? Are you going to feed an 8-wide decoder with a single 64B cacheline? Especially with no promises that any instruction starts or ends on that boundary?
> It's actually hard to come up with x86 instructions that are 15 bytes long.
Something as simple and trivial as loading a 64-bit immediate into a register is a 10-byte instruction in x86... So for you to feed an 8-wide decoder even with such simple things as that, you're already at TWO cache lines ingested per cycle (80 bytes)... good luck with that...
I hear intel is hiring. Their current top of the line decoder is IIRC 5-wide. I am sure they’d like to hear your ideas as to how easy it is to decode x86 8-way
It will only take a 10 byte instruction if you can't zero- or sign-extend from a 32-bit immediate. Of course, if you were on ARM or most RISC processors, such an immediate likely requires at least two instructions anyways.
From paging through an objdump output of a large program, I'd hazard that average instruction size is about 4 bytes, maybe a touch shy of that. After all, instructions like "add rax, rbx" is going to be 3 bytes (two if you're only using 32-bit registers!).
> Their current top of the line decoder is IIRC 5-wide.
Looking at wikichip for the block diagram [1], that's 5 instructions per 16-byte window.
[1] https://en.wikichip.org/wiki/intel/microarchitectures/skylak...
“
As with previous microarchitectures, the pre-decoder has a throughput of 6 macro-ops per cycle or until all 16 bytes are consumed, whichever happens first. Note that the predecoder will not load a new 16-byte block until the previous block has been fully exhausted. For example, suppose a new chunk was loaded, resulting in 7 instructions. In the first cycle, 6 instructions will be processed and a whole second cycle will be wasted for that last instruction. This will produce the much lower throughput of 3.5 instructions per cycle which is considerably less than optimal. Likewise, if the 16-byte block resulted in just 4 instructions with 1 byte of the 5th instruction received, the first 4 instructions will be processed in the first cycle and a second cycle will be required for the last instruction. This will produce an average throughput of 2.5 instructions per cycle.
“
Of course internally the CPU has some huge number of registers. But it matters how many registers the machine code can speak of, and arm64 has 32 while x86 has 16. Consequently, x86 machine code has more cases of data flow obscured by spills to the stack. In a very sophisticated cpu, that might mean more decoding work to try to reconstruct the original data flow, or more likely (what’ll probably happen in most cpus), those spills will limit how wide the cpu can go.
I don’t know what limits you more in practice - the smaller number of registers or the variable length instructions. X86 throws at least one more limiter just for good measure: the TSO memory model, which causes memory effects to have dependencies between them in cases where they wouldn’t on arm.
Apple's M1 supports TSO (on ARM, obviously).