Live data from Hacker News

The x86 architecture is the weirdo, part 2

devblogs.microsoft.com

141–150 of 170 posts

Re: The x86 architecture is the weirdo, part 2

#141
post #38

Earlier quoted context omitted.

> CPU architectures implicitly have something I call the "architectural weirdness budget" The point of designing a new architecture is you have a point to make (generally, "doing X will lead to faster execution"). So by definition you are adding unfamiliar architectural weirdness, else why get involved. The big problem is that the pervasiveness of the C model has fossilized design decisions of the PDP-11 that still h…

What exactly do you mean by the "C model"? Computers have followed the Von Neumann architecture for quite a long time. That PDP-11 is fundamentally the same as a modern computer, albeit millions of times slower. And I'm curious as well what "new paradigms" you have in mind.

Harvard architecture machines are still not just being used but still being designed these days.

Examples of non-pdp-11 things are discussed in various comments in this thread.

Re: The x86 architecture is the weirdo, part 2

#142
post #38

Earlier quoted context omitted.

> CPU architectures implicitly have something I call the "architectural weirdness budget" The point of designing a new architecture is you have a point to make (generally, "doing X will lead to faster execution"). So by definition you are adding unfamiliar architectural weirdness, else why get involved. The big problem is that the pervasiveness of the C model has fossilized design decisions of the PDP-11 that still h…

> the pervasiveness of the C model has fossilized design decisions of the PDP-11 This is tangential and somewhat pedantic, but I want to point out an oft-repeated mischaracterization. C is essentially a refinement of B with some additions [1], and B was written for the PDP-7, which was a very different machine. For example, the increment and decrement operators in C were inherited from B; they could not have been mod…

>[1] Notably, the char type, since the PDP-11 was byte addressable but the PDP-7 was not.

"char" is a useful type for the program's problem domain regardless of whether the machine is byte-addressed or not. Pascal was developed on machines that were not byte-addressable but always had a char type. This is easily visible in the PACK and UNPACK standard functions and the PACKED keyword for arrays and records. It is slow to do access to a random char from an array packed several to a word, so there are standard functions to copy everything from a packed array to an array that uses one word per item for easier processing, and back again.

If anything, you need the compiler to support the char type more if you don't have a byte-addressed machine because it's such a pain for the programmer to deal with themselves.

Standard Algol 60 didn't have "char" but some manufacturers added it non-standard. Algol 68 had char. These ran on primarily word-addressed machines too, as was the fashion in those days.

Re: The x86 architecture is the weirdo, part 2

#143
post #90

Earlier quoted context omitted.

What does ARM and RISC-V spend their weirdness budget on?

For traditional 32-bit ARM, off the top of my head: - Every instruction being conditional (all instructions have a four-bit condition field, with one of the 16 possible modes being "always"); - The barrel shifter, which can be used on nearly every data processing instruction; - The program counter being one of the general-purpose registers (and on the original ARM, the same register also containing the flags), so tha…

"always" is not the weird one -- that's just the same as everyone else. "Never" is weird, immediately spending 1/16th of the opcode space (256 million instructions) on NO-OPs.

PC being a general-purpose register was historically not uncommon. PDP-11 and VAX both did it and they were kinda popular at one time.

Load/store multiple was also fairly common with, for example, both 68000 and VAX having it. IBM 360 also, though using a register range rather than a bitmap -- a less general solution, but good enough, and much easier to make go fast.

Re: The x86 architecture is the weirdo, part 2

#144
post #90

Earlier quoted context omitted.

For traditional 32-bit ARM, off the top of my head: - Every instruction being conditional (all instructions have a four-bit condition field, with one of the 16 possible modes being "always"); - The barrel shifter, which can be used on nearly every data processing instruction; - The program counter being one of the general-purpose registers (and on the original ARM, the same register also containing the flags), so tha…

Loading and storing multiple registers is by no means weird: the MC68000 family has the movem.(b|w|l) instructions which do exactly that, and it's one of the best things since sliced bread because performance can be gained when used cleverly. Being able to manipulate the program counter in the ARM processors directly is just being honest, simple and straightforward, rather then having it always done implicitly. Seems…

Being able to manipulate the program counter directly plays hell with a superscalar and especially OoO processor where you want to be able to predict what the program counter does very accurately so the instruction fetch and decode can run far ahead of the execution.

There are four kinds of instructions that play hell with pipeline and OoO design:

- instructions that might cause traps, dependent on the values processed

- instructions that you don't know whether they will change the control flow

- instructions that you don't know where the control flow is going to go to

- instructions where you don't know how long they will take to execute

RISC-V, for example, bans the first category entirely other than load/store, and carefully separates the other three so any one instruction only had at most one of those problems.

ARM load multiple has all of those problems. At least you can examine the register mask at instruction decode time and know whether it will change the PC or not and tag the instruction in the pipeline as being a Jump or not. Imagine if there was a version that took the bitmap from a register instead of being hard-coded...

Load/store multiple don't increase performance much if at all on a CPU with an instruction cache and/or an instruction prefetch buffer. On an original 68000 or ARM without any cache, sure, a series of load or store instructions requires interleaving reading the opcodes with reading or writing the data, while load/store multiple eliminates the opcode reads. An instruction cache also eliminates them, leaving only the code size benefits. But load/store multiple is a perfect candidate for using a simple runtime function instead, at least if you have lightweight function call/return as RISC designs usually do.

Re: The x86 architecture is the weirdo, part 2

#145
post #87
post #14

Earlier quoted context omitted.

You keep saying this about RISC-V and it keeps not being true. Instruction sequences that fuse are standardized. RISC-V defines various profiles (like "Unix server") which mandate a minimum set of extensions. Extensions beyond the mandated ones will be detected at runtime, just like on x86.

Fusion isn't cheap and I'd say it's part of the weirdness bucket to rely on fusion instead of making a combined instruction.

Modern x86 and ARM both rely on fusing a compare instruction with a following conditional branch -- something RISC-V doesn't have to do as conditional branches already incorporate the compare and there are no condition codes.

So if fusion is a weirdness it's a nearly universal one.

The good thing about fusion is the program works fine if you don't do it, so low end minimal area CPUs such as microcontrollers can just not bother.

Re: The x86 architecture is the weirdo, part 2

#146
post #14

Earlier quoted context omitted.

You keep saying this about RISC-V and it keeps not being true. Instruction sequences that fuse are standardized. RISC-V defines various profiles (like "Unix server") which mandate a minimum set of extensions. Extensions beyond the mandated ones will be detected at runtime, just like on x86.

> Instruction sequences that fuse are standardized They are? Care to point to a ratified RISC-V standard that lists said instruction sequences?

Modern x86 and ARM both rely on fusing a compare instruction with a following conditional branch.

That's not in the spec. It's just something high end implementations do, and low end ones don't.

Re: The x86 architecture is the weirdo, part 2

#147

Earlier quoted context omitted.

As a former microarchitect, the contortions designs take on is due to a combination of factors, usually stemming from routing/timing problems in the design or backwards compatibility, and every now and then, compiler team requests. For example, endian-ness looks like weirdness to a programmer, but not to a chip designer that needs to read in lower-order bytes first to decode an instruction quickly. Why not just chang…

That's because it is insane: how come most processors are big endian, how did they manage just fine?

First off, most processors are not BE. Arm and X86 are LE and cover "most processors".

Second, processors like the Motorola 68000 were 32-bit, so there was no need to read just the first byte. Refer to my point about instruction fetch. Like I said, if you have an ISA that came from byte-sized opcodes, and then expanded later, you need to fetch bytes first. However, if your opcodes are 32-bit, then the code is bigger. (In the old days, CISC code size was a selling point compared to RISC, this opcode compression was one of the reasons.)

This is what you learn in first-year computer architecture. However the world has changed a lot since the 80's. Single bytes aren't fetched in x86, 256-byte cachelines are, and the decode happens on that.

Re: The x86 architecture is the weirdo, part 2

#148

Earlier quoted context omitted.

just one example of weird insanity from that table: slli rd, rs1, {1,2,3} add rd, rd, rs2 Fused into a load effective address ...this is so insane. Whoever thought that this is okay and good, has, in my opinion, severe psychological and psychiatric problems and would do well to seek professional help. If this gets "fused" into a lea, why just not implement a hex code for lea? I'm just completely at a loss as to how m…

The bitmanip extension has an LEA equivalent, so it looks like they have backtracked on this one.

The Bitmanip "LEA" instructions were added primarily for sh1add.uw, sh1add.uw, and sh3add.uw which not only shift and add but also zero-extend the lower 32 bits of the rs1 register to 64 bits before shifting and adding them.

Thus they are replacing not two instructions but three. This addition was indicated because of the number of critical loops in legacy software that, against C recommendations, tries to "optimise" code by using "unsigned" for loop counters and array indexes instead of the natural types int, long, size_t, ptrdiff_t. This can indeed be an optimisation on amd64 and arm64, but it is a pessimisation on RISC-V, MIPS, Alpha, PowerPC.

One codebase that uses "unsigned" in this way is CoreMark and they explicitly prohibit fixing the variable type. But it's also common in SPEC and in much code optimised for x86 and ARM in general, where using "int" pessimises the code. If they used long, unsigned long, or the size_t or ptrdiff_t typedefs the code would run well everywhere.

While the .uw instructions were being added, it was very low cost to add the versions using all the bits of rs1 at the same time.

So, in the context of this discussion, having 32 bit operations sign extend the results to 64 bits is a weirdness. More ISAs do is than zero-extension, but the most common in the market zero-extend. Note that at the time RISC-V was designed arm64 was not yet announced, so only amd64 did zero-extension.

Re: The x86 architecture is the weirdo, part 2

#149
post #10

Earlier quoted context omitted.

As far as I am aware, RISC-V spends it on not being weird (which is itself weird).

The hoops they had to go through to get PIC address calculations to work make it quite weird. Because `auipc` adds an offset from its `pc`, the corresponding `add` or `lw` relocation needs refer back to that instruction rather than the symbol it's actually looking for. The poor ELF specification ends up quite tortured by this, IMO.

That affects ELF relocations but not the code.

Arm64 is even worse! There is almost exactly the same instruction, but it also zeroes the low bits of the target address, so as you relocate code you also have to change the offset in the 2nd instruction even if the distance between the reference and the target stays the same.

Re: The x86 architecture is the weirdo, part 2

#150
post #64

Earlier quoted context omitted.

So which language has handled ILP, OOO, delay slots, cache hints, etc. particularly well? Anything that's ergonomic and actually used (i.e. not awkward and academic)?

But that's my point: the dominance of a C monoculture has relegated alternatives to being "awkward and academic". When you're in a C straightjacket it's hard to take advantages of other architectures like transputer, connection machine, or cell. Ever run bsd unix on a Cray? It was dog slow because the CPU assumed very deep pipelining and always had to continually resynchronize because of the frequent branches in the…

[deleted]
Post reply on HN