4 ms·
The point he is making is that trying to optimize C without any UB at all is basically impossible, even basic stuff like register allocation. If you're willing
by Jweb_Guru 5y ago
The point he is making is that trying to optimize C without any UB at all is basically impossible, even basic stuff like register allocation. If you're willing to forego that much performance just for the nebulous goal of producing "platform-specific UB" (which in practice can be just as damaging as the "regular" kind of UB in most cases) to simplify reasoning about your program, do you really need to be writing in C? Or would another, memory-safe language suffice? For that matter, if you really only care about what happens on the platform you're on, why not write in assembler and avoid having to worry about an optimizer at all? After all, "platform-specific" UB will otherwise vary across systems, leading to the same kind of unexpected behavior when you port your program (e.g. weak memory bugs in code designed for x86-64 machines that only surface on ARM).
(And to be clear--I think the article is being incredibly diplomatic in saying "there isn't enough research" into compilers that don't optimize based on UB. It would be absolutely shocking if a non-UB-based compiler were able to come anywhere close to modern C compilers on either real-world programs or toy benchmarks, because almost every avenue of optimization would be closed to them. When any out of bounds write from anywhere in your code, including another thread, can modify a variable on the stack, it's not even usually possible to coalesce or rearrange writes, or remove redundant reads! These sorts of things are the bread and butter of compiler optimizations. Hell, are we even allowed to inline function calls? It might break out of bounds writes to the saved stack pointer!).
- eximius 5y agoUB that just says "this is an axiom the compiler makes" is fine. Ignoring it as a possibility is fine. Lighting your program on fire when it knows you've done it instead of just telling you or doing something consistent is not. Anyway, if this is basically just "C is bad", whatever, I'm not using C for anything. I'd use Zig or Rust.
- Jweb_Guru 5y ago> UB that just says "this is an axiom the compiler makes" is fine. That's literally what UB is. If the compiler makes any optimizations based on those axioms, and the axioms are violated, you'll end up with unexpected behaviors ("lighting your program on fire") that are crazy hard to debug. So saying "the compiler can have UB, but it just shouldn't make weird optimizations based on that" is a little hard to justify, or else you get absurd results like what I outlined in the post above. A lot of beginning C programmers do find it strange that they can't index past the end of a variable and expect to always hit the next variable on the stack, for example, but experienced C programmers aren't. Being able to rearrange, reuse, and register allocate variable slots is one of the most fundamental optimizations out there. Now, imagine if compilers were never allowed to reorder writes and loads because another thread might potentially update them... pretty crazy, right? Well, a lot of the "benign data races" people yell at compilers for screwing with, are caused by exactly that kind of reasoning, assuming that writes in one thread will always be seen in the same order by other threads without any explicit synchronization or ordering primitives. And that's not even necessarily a "messing with what the hardware does" thing, because there's hardware out there that will reorder ordinary loads and stores for you!
- UncleMeat 5y agoNo compiler has some branch in it that says "if your program contains UB, maximally aggravate the developer." Compilers don't make your programs wipe your hard drive. It is literally just that the logic of the compiler may produce incorrect results based on assumptions it makes about your program.
- throwuxiytayq 5y ago> It would be absolutely shocking if a non-UB-based compiler were able to come anywhere close to modern C compilers on either real-world programs or toy benchmarks, because almost every avenue of optimization would be closed to them. Modern C# has next to no undefined behavior, you can disregard the managed features and write low level code with it, and it JIT-compiles nearly as efficiently as C. Unity has an LLVM-based compiler for a subset of C# that emits extremely clean native assembly. These days there's no real reason for languages to be as ugly as C.
- Jweb_Guru 5y ago> Modern C# has next to no undefined behavior, you can disregard the managed features and write low level code with it, and it JIT-compiles nearly as efficiently as C. The calculus for memory-safe languages is completely different. They are not allowed to perform arbitrary, unchecked, out-of-bounds writes, so optimizations assuming those writes don't exist are sound without UB. If you mix them with an unsafe language or component that can use raw pointers, that part of the language has a lot of the same UB that C does, or else it would invalidate optimizations in the managed part of the language. If you've ever seen C++ code that needs to interact with a managed runtime, you're well aware of how careful the unmanaged part has to be to avoid treading on runtime invariants. Anyway I'm not defending C here. I'm defending UB. It exists in the language for a reason, which is that it's not meaningfully possible to optimize programs without it. If people don't want to deal with it, they should use another language that isn't C. That doesn't mean that the amount of UB in C couldn't be greatly reduced, but the article is responding to people who claim that a modern optimizing C compiler could reasonably avoid non-"platform specific" UB altogether.