7 ms·
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 nee
by nixos 10y ago
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 any phones from before 2014?
- pjmlp 10y agoJIT compilers are able to take advantage of them, because you don't get a binary set in stone that has to run everywhere. This is the main reason why Apple is now pushing for LLVM bitcode, Android still uses dex even when AOT compiling and WP uses MDIL with AOT compilation at the store. So regardless of what an OEM decides for their mobile device, in theory, it is possible to make the best use of the chosen CPU. This is actually quite common in the mainframes, with AS/400 (now IBM i) being one of the most well known ones.
- wahern 10y agoThe AS/400 is more like an AOT than a JIT compiler. When I hear JIT I think opportunistically compiling portions of a program, but falling back to an interpreter. The way AS/400 works, IIUC, is that the compiler compiles to an intermediate byte code, which has remained stable for decades. When the program is first loaded, the entire program is compiled to the native architecture, cached, and then executed like any other binary. The reason why JIT environments aren't competitive generally with AOT compiling is because of all the instrumentation necessary. A JIT environment is usually composed of an interpreter[1] which, using various heuristics, decides to compile segments of code to native code. But the logic for deciding what segments to compile, when to reuse a cached chunk, etc is complex, especially the pieces that keep track of data dependencies. Also, each chunk requires instrumentation code for transitioning from and back into the interpreter.[2] For this and other reasons JIT'd environments aren't competitive with AOT environments except for simple programs or programs with very high code and data locality (i.e. spending most of the time inside a single compiled chunk, such as a small loop). Large or complex programs don't JIT very well. Even if the vast majority of the execution time is spent within a very small portion of the program, if data dependencies or executions paths are complex (which is usually the case) all the work spent managing the JIT'd segments can quickly add up. Programs that would JIT well also tend to be programs that vectorize well, and if they vectorize well AOT compilers also benefit and so JIT compilers are still playing catch-up. (One benefit (albeit only short term) for JIT compilers is that some performance optimizations are easier to add to JIT compilers because you don't have to worry about ABIs and other baggage; you iterate the compiler implementation faster.) I increasingly hear the term JIT used in the context of GPU programming, where programs are generated and compiled dynamically for execution on SIMD cores. But that's much more like AOT compilation. The implementation stack and code generation rules are basically identical to a traditional AOT compiler and very little like the JIT environments for Java or JavaScript. The only similarity is that compilation happens at run-time, but you can analogize that with, for example, dynamically generating, compiling, and invoking C code. Which, actually, isn't uncommon. It's how Perl's Inline::C works, and how TinyCC is often used. [1] Mike Pall has said that the secret to a fast JIT environment is a fast interpreter. [2] So, for example, calling into a module using the Lua C API is faster in PUC Lua than LuaJIT. LuaJIT has to spill more complex state, whereas the PUC Lua interpreter is just calling a C function pointer--the spilling is much simpler and has already been AOT compiled.
- pjmlp 10y agoI used the term JIT, because many tend to associate AOT to native compilation when it happens before the binary is shipped to the customers. Regarding AS/400, doesn't the documentation refer to it as kernel JIT? Thanks for the explanation.
- mike_hearn 10y agoI would disagree with almost everything you said here. JIT compilers can beat equivalent AOT compilers by about 20%, or at least, that's the kind of loss you get in HotSpot from not doing profile guided compilation and doing it all AOT instead. So that point seems wrong. If you're comparing Java and C++ well, that is affected by many things and you can quite easily construct microbenchmarks where Java beats C++. In real programs though, it's usually either a tie or the other way around, mostly because Java has a quite low density memory layout (currently). It turns out that the "hot spot" theory is mostly correct, in that most programs do spend most of their time in small parts of the program, and large quantities of code may only ever be executed once or twice. That's why you can write Java programs that are competitive with or beat C++ programs (see the TechEmpower server benchmarks for some examples of this happening) despite the fact that not all the code is compiled. JIT compilers tend to be better at unguided vectorisation than AOT compilers because they know what CPU features are available to them at compile time. Again, you may be confusing multiple unrelated factors together: in practice, most C++ programs will probably use vectorisation more heavily than Java programs do because C++ has features that let the programmer explicitly invoke them so the compiler doesn't have to figure out where the instructions can be used by itself. But that is unrelated to when the compiler runs: you can add vector intrinsics to Java and there's a project doing exactly that.
- gpderetta 10y ago> I would disagree with almost everything you said here. reality seems to agree with the parent. Case in point there are no production level JIT compilers for C/C++. The fact that a java AOT compiler is not competitive with HotSpot might have more to do to the maturity of HotSpot and the amenability of Java to AOT compilation. > JIT compilers tend to be better at unguided vectorisation than AOT compilers because they know what CPU features are available to them at compile time. runtime dispatching to specialized functions makes this a moot point point. I'm not aware of the state of the art, but last I heard HotSpot wasn't particularly great at vectorization.
- roel_v 10y agoCompilers have flags to use eg avx instructions. If you run such a binary on a cpu that doesn't support it, your program will simply crash. It's possible but cumbersome to detect cpu features and take different code paths based on that.
- veddan 10y agoThere are some methods to get around this. For example, there's an ELF extension called STT_GNU_IFUNC. It allows a symbol to be resolved at load time using a custom resolver function. This avoids the problem of figuring out which code-path to use on every invocation. For example, you could have a function void hash(char *out, const char *in); with two different possible implementations: a slow one using common instructions, and a fast one using exotic instructions. You can then can have a resolver like this: void fast_hash(char *out, const char *in); void slow_hash(char *out, const char *in); void (*resolve_hasher(void))(char *, const char *) { if (cpuSupportsFancyInstructions()) { return &fast_hash; } else { return &slow_hash; } }
- mschuster91 10y agoI'm a bit skeptical about the performance, especially with often-called functions. Normally, asm would do call slow_hash at every place where slow_hash is invoked, but now it has to check at every invocation a pointer with the address of the function. Of course the loader could walk through all uses of the pointer to slow_hash and replace them by fast_hash on loading, but that won't work for selfmodifying (packed, or RE-protected) code.
- chkras 10y agoThat's why you macro and inline your code. call's jxx's are expensive, hense CMOV ^_^
- deleted 10y ago[deleted]
- gpderetta 10y agoIn principle the compiler can inline the specialized hash function and specialize its caller instead (recursively). GCC is supposed to do that, but I hear that the optimiziation is still a bit unreliable.
- pwdisswordfish 10y agoGCC introduced __attribute__((ifunc(...))) precisely for this use case: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attribute...
- Rebelgecko 10y agoIIRC, 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 don't need different binaries tuned for every possible combination of available instructions. I don't think any compilers do this automatically, but most of the time generating assembly for the lowest common denominator is fast enough.
- revelation 10y agoThe 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 http://www.agner.org/optimize/blog/read.php?i=49#49
- yrro 10y agoWow... And I thought Microsoft used questionable business practices...
- brainfire 10y agoThe PC release of No Man's Sky apparently was built to use SSE4.1 instructions, leading to unexpected problems for users with older processors that would otherwise have been fine. We have in the past received different binaries from vendors to use depending on which processors we're using.
- Animats 10y agoThat was the curse of MIPS. Code was sort of portable, but the optimal code for each CPU implementation was different. Programs would ship with multiple binaries compiled with different options.
- mikeash 10y agoIn 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.
- peteri 10y agoCertainly the Borland 8087 emulation code used to have an interrupt call followed by the 8087 opcodes, the interrupt call got replaced at run time by NOPs if there was a co-processor present.
- SixSigma 10y agoLook forward to Win7 not working on the next gen of Intel processors, at MS request.
- krylon 10y agoWhy would Microsoft want to stop people running their system on newer CPUs?
- SixSigma 10y agoHmm, I got that slightly backwards, Win10 will not work on older CPUs > Going forward, as new silicon generations are introduced, they will require the latest Windows platform at that time for support. This enables us to focus on deep integration between Windows and the silicon, while maintaining maximum reliability and compatibility with previous generations of platform and silicon. For example, Windows 10 will be the only supported Windows platform on Intel’s upcoming “Kaby Lake” silicon, Qualcomm’s upcoming “8996” silicon, and AMD’s upcoming “Bristol Ridge” silicon. https://blogs.windows.com/windowsexperience/2016/01/15/windows-10-embracing-silicon-innovation/ https://blogs.windows.com/windowsexperience/2016/01/15/windo...
- caf 10y agoI can't see that surviving first contact with large corporate / government customers saying "No, we're going to stay on Windows 7 Enterprise for the next little while thanks - and by the way, we need to buy new PCs sometimes".
- krylon 10y ago> Hmm, I got that slightly backwards, Win10 will not work on older CPUs That makes more sense. I remember that years ago, there was this tongue-in-cheek conspiracy theory floating around that Microsoft and Intel had some kind of deal that new versions of Windows would pretty much require one to get a new computer so it would run decently, in order to boost Intel's sales. Maybe there was something to it? ;-) OTOH: "For example, Windows 10 will be the only supported Windows platform on Intel’s upcoming “Kaby Lake” silicon" - that does indeed sound like one could run into trouble trying to run older versions of Windows on new chips... I wonder if there are hard technical reasons for this. After all, both Intel and Microsoft had put a lot of hard work into preserving backwards compatibility.
- CyberDildonics 10y agoWindows 8 won't work on some of the early AMD 64 bit processors because they don't support 128 bit atomic compare and swap.
- rasz_pl 10y agoYes, usually it happens because someone is not experienced enough. Good example is No Man’s Sky crashing on Phenom CPUs because they hard linked Havoc compiled with SSE4.1 and _do not_ detect CPU type.
- ajuc 10y agoYou can already do that. In gcc you can pass arguments like -march=pentium3 , and the code will use features of that cpu and won't run on pentium2, etc. Gentoo linux uses this to slightly optimize binaries - because every gentoo user specifies these flags systemwide, customizes them for herhis cpu, and the whole system is recompiled taking them into account. Performance boost is negligible from my experience (but I used gentoo 10 years ago, maybe it changed).