4 ms·
Take the example with writing to NULL. Ignoring the situation means writing to NULL anyway or not writing to the address, not removing the if statement. Making
by quelsolaar 7y ago
Take the example with writing to NULL. Ignoring the situation means writing to NULL anyway or not writing to the address, not removing the if statement. Making assumptions about the value of a pointer based on UB is not ignoring the situation, that's trying to take advantage of the situation.
- aw1621107 7y ago> Making assumptions about the value of a pointer based on UB is not ignoring the situation But it is? Out of all situations, you ignore those that invoke UB. It's technically correct, but can produce bad results. What you want is to ignore the undefined behavior itself, and to treat the code as conforming. That's an equally valid interpretation, and the only way to definitively get one interpretation over the other is for the standard to change that wording to say so.
- acqq 7y ago> The only way to definitively get one interpretation over the other is for the standard to change that wording to say so. No. If the interpretation of the standard results in a behavior that makes the result obviously worse, nobody should favor such an interpretation. Even if the behavior is "undefined" as per standard, that doesn't mean that any compiler should "define" its own interpretation and implementation in a way to make every resulting program worse. They obviously "can" and obviously are already doing this, but even that doesn't make such an approach good or desired by any user of that compiler.
- aw1621107 7y ago> If the interpretation of the standard results in a behavior that makes the result obviously worse, nobody should favor such an interpretation. This would be true if everyone actually agreed that the more aggressive interpretation is obviously worse. Since said interpretation is claimed to result in better performance, though, it's not very clear-cut. That's the nature of ambiguity; either you rely on convention to establish a canonical behavior, which doesn't appear to be likely to happen any time soon and isn't guaranteed to remain the same over time, or you change the wording to no longer be ambiguous.
- DougBTX 7y ago> to make every resulting program worse. That's the crux of the issue though, isn't it. I'd like to think that the compiler developers are not programming at random, and that the optimisations do improve the compilation of code that only uses defined behaviour. If the only defined value for a condition is effectively: if (false) { ... } surely removing the branch is the best thing to do, regardless of what is inside the braces? If a language makes it easy to accidentally write if (false) when the programmer was trying to test some real condition, then surely that's a problem with the language, not the compiler.
- acqq 7y agoBut it's not "if (false)" that was originally written. It's the problem only when the language lawyers search for the "loopholes in the standard" to derive the meaning that is turning if (make sure something bad is not going on) { do_something } to the following: "I clever compiler author devised a way to analyze do_something and I see that you the writer of the code managed to write do_something in a way that can be considered "undefined behavior" according to this standard I'm using, therefore I am "allowed" to also remove the "if (make sure something bad is not going on)" to... nothing! See how fast the resulting code now is!" It's searching for the loopholes to do something directly against the intention of the writer of the code that people have problems with. In that light I fully agree with Linus: https://lkml.org/lkml/2018/6/5/769 https://lkml.org/lkml/2018/6/5/769 "I've said this before, and I'll say it again: a standards paper is just so much toilet paper when it conflicts with reality. It has absolutely _zero_ relevance." "There are competent people on standards bodies. But they aren't _always_ competent, and the translation of intent to English isn't always perfect either. So standards are not some kind of holy book that has to be revered. Standards too need to be questioned."
- bluGill 7y agoI'm glad my compiler searches for those situations and removes the unneeded if. I write a lot of generic code - often I need the if only in some situations and write it trusting the compiler will figure out the situations I don't need it and remove the if. Without this I'd have to spend a lot of effort maintaining hundreds of almost the same copies of my code different only where there is a might or might not be needed if. Not to mention all the likely bugs when someone doesn't select the right version of my code to use.
- steerablesafe 7y ago> Even if the behavior is "undefined" as per standard, that doesn't mean that any compiler should "define" its own interpretation and implementation in a way to make every resulting program worse. The standard offers no interpretation of any undefined behavior, that's literally what meant by undefined. There is no execution of a program that contains UB that could be considered 'valid' by the standard. The compiler has no choice, it has to define its own interpretation.
- eru 7y agoTo make matters worse, undefined behaviour doesn't just exist at runtime. It can also happen at compile time etc. And it is allowed to time travel.
- acqq 7y ago> The standard offers no interpretation of any undefined behavior "The standard" is not compiling my program, the real compiler is doing that, and the authors of that specific implementation should do the right thing, in spite of the standard leaving something "undefined." > The compiler has no choice, it has to define its own interpretation. Correct. And it can define it as to not to be an adversary to the writer of the code.
- ryandrake 7y agoWhat is the “right” thing to do with UB, and who defines what is right, if not the standard? You?
- acqq 7y ago> What is the “right” thing to do with UB It is actually quite clear in most of specific cases, as Linus pointed even 18 years ago: https://gcc.gnu.org/legacy-ml/gcc/2002-01/msg00418.html https://gcc.gnu.org/legacy-ml/gcc/2002-01/msg00418.html "When it comes to optimization, there are things that are "obvious", and that will never generate any discussion at all, because they are faster ways of getting the exact same result, with no question about it. Nobody will ever fault a compiler for evaluating constant expressions at compile-time rather than doing the math at run-time and getting the same result. The two are indistinguishable from a result standpoint. But optimizations that can change the value of an expression are slightly different. They should make sense. You should be able to _explain_ them." "If you cannot explain the results, you end up having to argue about word-weaseling in the standard, and people _will_ disagree on what the damn thing means. Because standards aren't mathematically exact things."
- eru 7y agoWhat does 'removing a statement' even mean? C statements aren't translated one-for-one to machine code, so you can't just talk about 'removing a statement'. In any case, lots of undefined behaviour is in the standard exactly so that the compiler can take advantage of the situation. That's mostly so that the optimizer's job is simpler. Eg the compiler can assume that you are checking for null yourself already, so reasoning-by-contradiction it can assume that when an execution path looks like it might access a null pointer, that it means this path is dead code. (And there's lots of dead code around.) Similarly, the compiler is allowed to assume that loops that have no side-effects terminate. (Or rather, non-terminating loops without side-effects are undefined behaviour. So you can't rely on them to implement a busy wait.) See "C Compilers Disprove Fermat’s Last Theorem" https://blog.regehr.org/archives/140 https://blog.regehr.org/archives/140 for more about this.
- mark-r 7y agoThe whole point of the null pointer example is that "you are checking for null yourself" doesn't work in that situation, because the compiler optimizes out the check. It becomes impossible to do the right thing.
- eru 7y agoNo, the compiler doesn't optimize out proper null-checks. Eg the compiler leaves this one in: if(ptr) *ptr = 2; But if you do something like this: int* next = ptr + 1; if(ptr) { *ptr = 2; ptr = next; } Then the compiler 'knows' that you must have already checked the ptr for null, and can elide the redundant check.
- mark-r 7y agoSo what you're saying is that this is guaranteed to work: if(ptr) ptr->action1(); if(ptr) ptr->action2(); While this might blow up: if(ptr) { ptr->action1(); ptr->action2(); } That's the only if statement in all of C that works that way, and violates the principle of least surprise in a huge way.