Not exploiting the UB for optimizations in ways that break lots of code is actually trivially easy. We have been doing it for 30+ years and we just have to stop adding new optimizations that break, and possibly revert some of the ones that are already in there.
Here lies madness. What optimizations are not allowed exactly? Strict aliasing? Assuming dereferenced pointers are not null? Assuming all array accesses are always in bound? Assuming that integers don't wrap? Assuming that allocated memory is not accessed after a free? Reusing function frame slots of variables with non overlapping lifetimes? Not laying down local variables in the most obvious way? Caching and reordering non-volatile memory accesses? Accessing local variables of functions that have returned?
I'm sure you will say obviously not allowed to the first few and obviously allowed to the last few, but at some point all of these optimizations (and more) did break some code as some point.
Nah. You're looking at it the wrong way. You are saying madness lies in going there. We don't have to "go there". We can simply not leave there. Not doing something is easy. In fact, it's my secret super-power. If you want, I'd be happy to teach you.
If what you want is a straightforward translation of C into machine code, there is always -O0.
git co <previous-hash>
You're welcome!
It's not the only language of this sort, but C also has a lot of code written in it, which is a major advantage.
Not really; it's structurally nothing like assembly, which is as close as you can get to the hardware without just handcoding machine code.
Now, C -- for a structured language with static typing -- provides very little in terms of safety features that get in the way of direct hardware manipulation, whereas lots of languages with superficially similar structure aim to be safe in some ways by default which makes some hardware manipulation more difficult, or at least more verbose, than it would be in C. So, in some ways it gets less in the way of freedom to manipulate the hardware than some other structured programming languages.
But it's not particularly close to the hardware at all.
https://en.wikipedia.org/wiki/P-code_machine
Linoleum was another:
http://anynowhere.com/bb/layout/html/frameset.html
The closest in C land was C-- by Jones et al which still looks heavy compared to p-code:
Here, assembly is touching the hardware. If you write your code in assembly, it's not portable to other architectures, even hypothetically.
>provides very little in terms of safety features that get in the way of direct hardware manipulation
Exactly. So it's a heck of a lot closer to the hardware than most 3GLs.
Not really true in the context of the optimizations done by modern C compilers (e.g. autovectorization).
It's really common for bugs to only appear when compiling with optimization.
Reasonably, when those bugs appear, people go fix them.
My guess is in LLVM these optimization are turned off for debug builds. But who wants to be running debug builds all the time?
1. Vary between compiler versions.
2. Vary between compiler implementations.
3. Often contain somewhat useless warnings (Wparentheses) that can change with changes in 1. or 2.
There are certainly safer alternatives, but not faster ones.
I'd probably agree though, in most cases users don't need the fastest builds (most OS tools). It would be interesting actually to be able to enabled optimized versions of functions as a runtime flag. But I've never seen that done.
IIRC, the Linux kernel has a rule that as a kernel developer, you are not allowed to break userspace, even if you think userspace is wrong. This is an apologia for why compiler writers think they should not have to be held to similar standards.
The argument is always something along the lines that UB in the C standard means they get to do whatever they want, including such nonsense as eliminating entire loops because the last access is out of bounds or going down an if-else-branch other than what the if says.
That this argumentation is specious in the extreme is shown by the fact that C compilers worked fine (and with less code breakages) before there even was a C standard, so all behavior was "UB".
The unstated assumption is that the optimizations enabled by this are absolutely necessary. That overstates the importance of compiler optimizations in general, and certainly the importance of these particular optimizations. (see Proebsting's Law)[2].
There may be code-bases where these extra optimizations are beneficial or even essential, fine, let's add a -Ogoforit or -Oletsgocrazy flag.
Rebuttal here: What every compiler writer should know about undefined behavior or "Optimization" based on undefined behavior hurts performance[1]
Short summary is that this trend towards exploiting UB breaks a lot of code that has been working for decades, and the fixes required to bring them in-line with the new regime imposed in order to enable these new optimizations makes it slower than the old code without those optimization. Yay.
[1] http://www.complang.tuwien.ac.at/kps2015/proceedings/KPS_201...
[2] http://proebsting.cs.arizona.edu 'I may be best known for "Proebsting's Law", which asserts that compiler optimizations have yielded annual performance gains an order of magnitude worse than hardware performance gains. The law probably would have gone unnoticed had it not been for the protests by those receiving funds to do compiler optimization research.'