3 ms·
But it's buggy in a way that doesn't destroy the meaning of the whole program. The Sledgehammer Principle is not enough; UB is a nuclear holocaust for a progra
by gavinhoward 3y ago
But it's buggy in a way that doesn't destroy the meaning of the whole program.
The Sledgehammer Principle is not enough; UB is a nuclear holocaust for a program.
So I'll take the buggy behavior that slows my code and only uses a sledgehammer over the buggy behavior that treats my program as a suggestion.
- UncleMeat 3y agoIt is true that the damage is more unbounded when you trigger undefined behavior. I mentioned this in my comment. It is real, but I believe that people largely overstate the degree with which these two things are different (nasal demons).
- gavinhoward 3y agoNot really. https://web.archive.org/web/20210508125532/https://twitter.com/m13253/status/1371615680068526081 https://web.archive.org/web/20210508125532/https://twitter.c... https://godbolt.org/z/1q1bjn https://godbolt.org/z/1q1bjn https://news.ycombinator.com/item?id=26498233 https://news.ycombinator.com/item?id=26498233 Nasal demons was an analogy, not something to take literally.
- UncleMeat 3y agoOh come on, this is exactly what I'm talking about. We've seen these before. The "format your hard drive" one is instead "completely fuck up your control flow and jump between functions" and it is purposefully constructed. People could have just focused on that - it was already intense enough. And you can happily get fucked up control flow with good old fashioned modular arithmetic errors. C and C++ are both nightmare languages and the connection between UB and the as-if rule are a mess. But incorrect programs can still blast off into incredible harm without the optimizer's help. And the emphasis on UB because it is so fun to construct weird scenarios has led to a world where people make strange and ill defined demands for how the compiler should react in these situations.
- gavinhoward 3y agoIt may be purposefully constructed for the example, but it is also easily constructed. This is only possible because the compiler deletes code. Yes, you can get messed up control flow from modular arithmetic code, but the compiler is not going to delete that code if it happens to overflow. And just because it's purposefully constructed doesn't mean it won't happen in real code, which is much more complicated and liable to triggering such problems.
- UncleMeat 3y agoThe compiler deletes code all the time in well formed programs. You typically want this. I do agree that the "but I put the check there" argument is real and that it absolutely breaks the principle of least surprise that you could look at your source and see a check that is important yet doesn't actually end up in the binary. But basically nobody has provided a rigorous conception of what a compiler shouldn't do w.r.t. undefined behavior. "Just don't delete my code" doesn't work since that's a big portion of what makes compiler optimization valuable. "Don't ever optimize based on UB assumptions" doesn't work, since that means that literally any function without a body available to the current translation unit can havoc all memory (or worse, even another function running concurrently).
- gavinhoward 3y ago> "Don't ever optimize based on UB assumptions" doesn't work Why not? Why is that somehow worse than code that breaks everywhere, all the time? Correctness should always come first. Sometimes performance is part of correctness (real-time), but it is not most of the time. When performance is not a part of correctness, why would optimizing for performance even be a possibility? And having done real-time work, I can tell you that the output of the compiler is scrutinized heavily in that domain. If the compiler is optimizing according to UB, changes would be made. But to answer your question, this is what I would do: If a programmer did not opt-in to the UB, don't optimize according to the assumption of lack of that particular UB. Programmers do not opt-in to signed overflow as UB, so it should never be used as an optimizing assumption. Programmers do opt-in to UB for data races by sharing memory across threads or signal handlers, so compilers can optimize against that. Programmers do not opt-in to non-terminating loops as UB, so compilers should not optimize to that. Programmers do opt-in to strict aliasing by using pointers to different types, or by using the `restrict` keyword, so the compiler can optimize on that. You may complain that if I use signed types, I'm opting-in to that UB. I disagree, but even if you are right, I'm explicitly opting-out by using unsigned types. And yet, you'll still complain about that; you started this discussion by complaining about developers using unsigned types because it would be slower, even though you acknowledged that the bugs wouldn't be as bad. In other words, you want performance at any cost, so much so that you'll complain when people use mitigations against compiler optimizations that can raze code like Rome razed Carthage. So you and I will never see eye-to-eye because you firmly believe that performance is everything, and I firmly believe that correctness is everything. I usually try to be diplomatic on HN (and often fail), but this is one time I can confidently say: you are dead wrong. By the way, I have public code that uses unsigned types as much as possible, and it still holds its own against an implementation that uses a heavily optimized library. So you are also wrong that unsigned types make it noticeably slower.