3 ms·
This is a feature of optimizing compilers and isn't what people mean when they say it maps to hardware.
by nwmcsween 8y ago
This is a feature of optimizing compilers and isn't what people mean when they say it maps to hardware.
- notacoward 8y agoWith optimization turned off, each line of C does map to a fairly obvious sequence of native instructions, including instructions that are actually poking at memory-mapped hardware registers. No hidden constructors/destructors, no operator overloading, no exceptions, no "built in" data types that require thousands of lines of hidden library code mapping to what the hardware actually offers. That doesn't cover things like memory ordering and barriers, but it's still a lot closer than any other language in which software is actually written (so e.g. assembler or LLVM IR don't count). I believe Rust has the same property, but only a tiny fraction as much software is written in it. Ditto for Zig and Nim. C++ has all of the paradigm-breakers mentioned above, Go has its own hidden magic mostly related to goroutines, Java doesn't even try, and anything functional doesn't even occupy the same conceptual space. As a mental exercise, try taking any random piece of generated assembler code and map it back to the corresponding higher-level-language code. I can usually do this pretty easily for C code, though switch/case statements present some challenges and heavy use of macros in any language can defeat the effort. Even calls to compiler-internal functions (e.g. memcpy-like functions for structure assignment) will be easy to interpret. Again, you can probably do the same for Rust/Nim/Zig. Attempting the same for C++ or Go will lead to all sorts of random places other than the original source line, and for just about anything else good luck even finding that generated assembler in the first place. When you can map easily back and forth between the language and CPU instructions, you're as close to the hardware as you're going to get. This is something few programmers - even in kernels and such - actually need, but C offers it and most other languages don't.
- jcranmer 8y ago> When you can map easily back and forth between the language and CPU instructions, you're as close to the hardware as you're going to get. This is something few programmers - even in kernels and such - actually need, but C offers it and most other languages don't. If you think this is true, try building a native assembly to C decompiler. There are a lot of warts that you realize C gives you absolutely no way to describe. For example, C has no concept of unwinding, which both C++ and Rust do. (Admittedly, much of unwinding is handled by library functions, but one piece of the unwinding puzzle is do-something-in-this-function-when-unwinding, which can't be handled by library functions). There's no support for SIMD vector units in C. And getting floating point edge case support is tricky: there's a difference between a * b + c and fma(a, b, c), and that's before you get into trickiness like floating point exceptions, rounding mode, and signaling NaN. The C language only has an easy bijective mapping to the actual CPU assembly if you focus on the easy scalar integer, general-purpose subset of both. Throw in floating point support, control flow [1], and vectors, and the mapping is much more difficult. Trying to support the actual hardware memory model basically kills the ability to map CPU assembly back to C/C++ [2]. The mapping just isn't as good as most people think it is, not if you actually care to start exploring the space of what can be done. [1] I didn't mention this earlier, but try taking the address of blocks (this is a GNU extension, not standard C) while not doing anything else with them and see how well-preserved they are even at -O0. Spoiler alert: they're not. [2] Virtually all of the effort to actually make hardware features invented in the past 20 years available in standard programming language is concentrated in the C++ working group, not the C working group.
- nwmcsween 8y agoNot having access to SIMD vector units IMO is a feature. Let the compiler figure out if SIMD is useful and have a language that can be expressive enough to allow a compiler to do SIMD if needed. The other 1% use case of SIMD could be a separate extension.
- notacoward 8y agoOK, so how does any of that get easier with any other language? In what other language do SIMD instructions, floating-point edge cases, or fused multiply-add map neatly back into HLL constructs? The unwinding stuff is an even clearer example of other languages - notably those with exceptions - being further from the hardware. I was already aware of all of those things when I said "as close as you can get" and they only reinforce the point. Thanks for the help.