Live data from Hacker News

Does a compiler use all x86 instructions? (2010)

pepijndevos.nl

61–70 of 198 posts

Re: Does a compiler use all x86 instructions? (2010)

#61
post #42

My question is if compilers use "new" x86 instructions, as then the program won't work at all on old systems. For example, if Intel decided today that CPUs need a new "fast" hashing opcode (I don't know if they actually do), a compiler can't compiles to it, as programs won't work on older computers. Is it like the API cruft in Android, where "new" Lollipop APIs are introduced for 10 years from now, when no one uses a…

IIRC, when a new version of OSX stops working on older hardware, it's usually because Apple started using a "new" version of SSE that's not supported on a given CPU. For handcoded assembly, it's not uncommon for code to check a CPU's capabilities at runtime (e.g. OpenSSL checks at runtime if it can use the really fast AES-NI instructions,. If not, it fall backs to other assembly or C implementations). That way you do…

The Intel compiler (used to?) do runtime CPUID checks. If your CPU came back "GenuineIntel" (and only Intel), it took the fast SSE path.

Theres more here:

http://www.agner.org/optimize/blog/read.php?i=49#49

Re: Does a compiler use all x86 instructions? (2010)

#62
post #28

Earlier quoted context omitted.

Good examples of (essentially-deprecated) instructions include the rep prefixed instructions for string operations (modern library code for string operations typically involve a mixture of SSE, full-word loads and unrolled loops for speed); the "loop" instruction (compilers usually generate explicit loops for flexibility); pretty much all the BCD arithmetic instructions (since programming languages don't typically us…

> the rep prefixed instructions for string operations ... are actually preferred over a hand-written vectorized loop on Ivy Bridge and up (see [1] section 3.7.7, "Enhanced REP MOVSB and STOSB operation (ERMSB)"). It's indicated by a CPUID feature flag bit (edit: grep for "erms" in /proc/cpuinfo to see this). The reason is that microcode knows more about the dcache microachitecture, load/store units, special features…

And by not having a group of hand-optimized special cases, the REP MOVSB and REP STOSB can be inlined saving instruction cache.

Re: Does a compiler use all x86 instructions? (2010)

#63
post #7

Earlier quoted context omitted.

There are also AFIAK a few "deprecated" instructions that are implemented for backward compatibility but do not perform well on modern cores or have much better modern alternatives. These would be things like old MMX instructions, cruft left over from the 16-bit DOS days, etc. X86 is crufty. Of course all old architectures are crufty, and using microcode it's probably possible to keep the cruft from taking up much si…

Since P6, Intel's CPUs have used a RISC like core with a very heavy decoder that translates x86 CISC instructions to run on the internal ISA. With that in mind, do older or lesser used instructions actually perform poorly or are they just the wrong choice but actually preferred for other scenarios?

Old instructions may have a smaller encoding, which means it can be hard to tell whether you should use them if they are otherwise slow. The compactness of the x86 instruction encoding is one of the architecture's chief strengths (and weaknesses).

Re: Does a compiler use all x86 instructions? (2010)

#64

There are instructions that would almost never be useful. See Linus's rant on cmov http://yarchive.net/comp/linux/cmov.html The tl;dr is that it would only be useful if you are trying to optimize the size of a binary.

I didn't read Linus's rant on CMOV, but whenever you see a CPU with CMOV, it is because the hardware has very good branch prediction, and the compiler has intimate knowledge of how the branch prediction hardware works.

Then the compiler works hard on determining if branches are highly predictable. Is the branch part of closing a loop? Predict that you will stay in the loop. Is the branch checking for an exception condition? Predict that the exception is rare. OTOH, there are some branches that are, in fact, "flakey" on a dynamic execution basis. Deciding where to push a particular particle of data based on it's value inside an innermost processing loop, for instance.

So... the compiler identifies "flakey" branches, it emits code to compute both branches of the if, and CMOVs the desired result at the end. That allows the instruction issue pipeline to avoid seeing a branch at all, thus avoiding polluting the branch cache with a flakey branch, and avoiding a whole bunch of pipeline flushes in the back end. At the cost of using extra back-end resources on throw-away work.

CMOV is in X86 for a reason. On Pentium Pro and later, it is a win if your compiler has good branch analysis.

Re: Does a compiler use all x86 instructions? (2010)

#65
post #36

Certainly the BCD instructions are not used?

Well, be sure to check out a COBOL compiler before you write it off. But it's certainly rare.

BCD arithmetic is an artifact of history. The 4004 was targeting desktop calculators. And IIRC all of the microprocessors in the 8008/8080 era had some support for BCD.

Re: Does a compiler use all x86 instructions? (2010)

#66
post #7

Earlier quoted context omitted.

There are also AFIAK a few "deprecated" instructions that are implemented for backward compatibility but do not perform well on modern cores or have much better modern alternatives. These would be things like old MMX instructions, cruft left over from the 16-bit DOS days, etc. X86 is crufty. Of course all old architectures are crufty, and using microcode it's probably possible to keep the cruft from taking up much si…

Since P6, Intel's CPUs have used a RISC like core with a very heavy decoder that translates x86 CISC instructions to run on the internal ISA. With that in mind, do older or lesser used instructions actually perform poorly or are they just the wrong choice but actually preferred for other scenarios?

> Since P6, Intel's CPUs have used a RISC like core with a very heavy decoder that translates x86 CISC

This get repeated often, but it is actually wrong. First of all RISC is a property of the ISA, not the microarchitecture: CISCs have been breaking complex instructions in micro instructions well before the RISC/CISC separation were even conceived.

In fact modern CISCs try to not to break instructions until they reach the execution units so that less resources need to be spent tracking them (uop fusion). Some even try to fuse multiple instructions (macro op fusion, many RISCs do it as well).

Re: Does a compiler use all x86 instructions? (2010)

#67

The article assumes that no software in bin is written natively in asm or has asm blocks or linked objects... which seems a bit out there.

My thought was that most binary distributions probably use very conservative configuration that will generate code that compatible with very old processors, and that you would therefore not see much use of modern instructions in /bin. This is one of the selling points of compile-yourself distributions like Arch/Gentoo: you know what processor you're running on, so you can take full advantage of its features.

Re: Does a compiler use all x86 instructions? (2010)

#68
post #2

It doesn't, because there are lots of special-purpose x86 instructions that would be more trouble than they're worth to teach a compiler about. For example, instructions for accessing particular CPU features that the C and C++ languages have no concept of (cryptographic acceleration and instructions used for OS kernel code spring to mind). Some of these the compiler might know about via intrinsic functions, but won't…

About LEA: adding to the above correct information, it is also useful because it can be scheduled in parallel with other ALU instructions (through a different "port" in x86 parlance), or at least that's how it used to be (I haven't looked at most recent x86 architectures). Thus compilers can use LEA to perform some arithmetic operations and generate code that will effectively run faster.

Re: Does a compiler use all x86 instructions? (2010)

#69
post #36

Certainly the BCD instructions are not used?

IIRC, Turbo Pascal and its descendants (including Delphi and FreePascal) have support BCD arithmetic at the language level (but it might be part of stdlib), so I'd imagine that either the compiler outputs BCD instructions or BCD instructions are inlined in the library implementation.

EDIT: Although its possible that they use something else behind the scenes and just convert to/from BCD representation. That seems intuitively likely to be inefficient, but if keeping BCD ops efficient hasn't been a focus of CPU development, its possibly not the ideal way to do things under the hood, even when at the application language level it is presented as doing BCD.

Re: Does a compiler use all x86 instructions? (2010)

#70
post #42

My question is if compilers use "new" x86 instructions, as then the program won't work at all on old systems. For example, if Intel decided today that CPUs need a new "fast" hashing opcode (I don't know if they actually do), a compiler can't compiles to it, as programs won't work on older computers. Is it like the API cruft in Android, where "new" Lollipop APIs are introduced for 10 years from now, when no one uses a…

In addition to what others have said, sometimes stuff gets added in a backwards compatible way. For example, Intel added two new instruction prefixes for their hardware lock elision extension. Actually, they reused existing instruction prefixes which were valid but did nothing on older CPUs when used on the instructions where the HLE prefixes apply. The semantics are such that "do nothing" is valid, but CPUs which understand the new prefix can do better.

Another alternative is to use the new instructions regardless, then trap illegal instructions and emulate them. This used to be a common way to handle floating point instructions, back in the days when FPU hardware wasn't universal. CPUs with FPUs would run the instructions natively, and CPUs without them would emulate them in software. This was, of course, unbelievably slow, but it worked.

But for the most part, you just generate different code for different CPU capabilities and dispatch, as the other comments describe.

Post reply on HN