3 ms·
One thing I never understood about compilers: Doesn't all CPUs offer a different set of opcodes? How can a compiler then work for different CPUs? For example,
by TekMol 4y ago
One thing I never understood about compilers:
Doesn't all CPUs offer a different set of opcodes? How can a compiler then work for different CPUs?
For example, this Wikipedia page is listing the CPUs that support instructions for fast SHA calculations:
https://en.wikipedia.org/wiki/Intel_SHA_extensions https://en.wikipedia.org/wiki/Intel_SHA_extensions
How could a compiler ever use those, when not all CPUs support them?
- vibbix 4y agoI'd imagine a combination of runtime extension checking, and baseline extension support. I.e. when you pass -mavx2 or -march=native to your Cxx compiler, it sets the baseline minimum for what op codes are used.
- jgalt212 4y agoNotes on how to get list of available targets GCC supports: https://stackoverflow.com/questions/47299458/how-can-i-determine-what-architectures-gcc-supports https://stackoverflow.com/questions/47299458/how-can-i-deter...
- arsome 4y agoTypically compilers have a "frontend" which turns code into a syntax tree and different "backends" for each architecture they need to support. For example, clang is the C front-end for LLVM while Emscripten is a backend that allows it to output Javascript. Though for highly specialized calculations like that. they're often written into popular libraries like OpenSSL with inline assembly for all relevant architectures.
- bratao 4y agoYou can set the level of instructions to use. There are some levels that are compatible enough with modern CPUs ( something like all x64 CPUS) that will work for most of people. https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
- Slartie 4y agoCPUs offer feature flags that can be queried to learn about the availability of a particular extension. If a compiler wants to generate code that uses an extension if available, but falls back to an explicit (but slower) implementation if that's not the case, it can simply generate a branch instruction in front of the opcodes using the extension which checks for the feature flag first, branching into the fallback implementation if the extension isn't available.
- IshKebab 4y agoThat's true, though in practice - at least in C, C++, Rust, Go etc. - the compiler won't do that automatically. You need to write code to do it yourself (which very few people do for obvious reasons).
- eesmith 4y agoSimple. A compiler doesn't generate code that works on all different CPUs. It generates code that works on 1) one CPU, or 2) a family of CPUs, or 3) (less commonly) multiple families of CPUs. But not all CPUs. I think you can see how 1) works. Plus, improved versions of a CPU are often backwards compatible, so if the program works on an older Intel x86-64 CPU than it very likely works on a newer one, or on an AMD x86-64 CPU or other compatible CPU. 2) works because programs can query the processor to see which additional features are supported. In your SHA extension example, https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sha-extensions.html https://www.intel.com/content/www/us/en/developer/articles/t... says: > Availability of the Intel® SHA Extensions on a particular processor can be determined by checking the SHA CPUID bit in CPUID.(EAX=07H, ECX=0):EBX.SHA [bit 29]. It gives an example C function with inline assembly which uses the "cpuid" instruction to get that information: int CheckForIntelShaExtensions() { int a, b, c, d; // Look for CPUID.7.0.EBX[29] // EAX = 7, ECX = 0 a = 7; c = 0; asm volatile ("cpuid" :"=a"(a), "=b"(b), "=c"(c), "=d"(d) :"a"(a), "c"(c) ); // Intel® SHA Extensions feature bit is EBX[29] return ((b >> 29) & 1); } A compiler (or programmer) can have one code path execute if CheckForIntelShaExtensions() is true, and another if it's false. 3) works in collaboration with the operating system. For example, on macOS the compiler can compile once for x86-64 and again for its "M" series chips and bundle the results into a single binary. The OS knows the CPU and can select the right compiled code to start. Do note that Nuitka doesn't do any of this. It compiles Python to C code, then lets the C compiler handle the above.
- fulafel 4y agoThe bar is much lower than this question implies for compilers. People don't expect them to take advantage of those SHA instructions automatically. Or even SIMD instructions - in the 25 years we've had SIMD instructions in consumer CPUs, compiler generated code usually can't exploit it ("autovectorize") well. (Though largely it's because we haven't updated our programming languages to be conductive to this...)
- jnwatson 4y agoSIMD instructions are so uncommonly used that when you see one in a binary, it is probably an inline memcpy.
- jackmott42 4y agoI mean, its normal to use the instructions for floating point math even when its not actually vectorized, so they are in there all the time. And a 64bit target will tend to enable SSE2 which will get you quite a bit of auto-vectorization. and anything in .NET land since .NET Core will have a ton of SIMD in it
- fulafel 4y agoCompilers do emit the instructions regularly, but they rarely manage to speed up the code much.
- pkkm 4y agoUsually you compile for the lowest common denominator. For example, the version of GCC I use defaults to -march=x86-64, which is a common subset of instructions that should be supported by a 64-bit x86 CPU. You can override this with an argument like -march=sandybridge or -march=native (the latter enables all instructions your current machine can handle). In principle, you could distribute many different binaries optimized for different CPUs. However, the more common approach in high-performance software is to compile most of the program for a generic instruction set and write several versions of the hottest parts. Then you detect the supported CPU features at runtime and dispatch appropriately.
- pabs3 4y agoI helped write a document for Debian about that, there are various ways you can select different instructions at runtime or build time etc: https://wiki.debian.org/InstructionSelection https://wiki.debian.org/InstructionSelection