5 ms·
"Ignoring the situation" is not the same as do whatever you want.
by quelsolaar 7y ago
"Ignoring the situation" is not the same as do whatever you want.
- aw1621107 7y agoAnd "do whatever you want" isn't really what compilers are doing either. Out of all possible program executions, they are ignoring the situations where undefined behavior is invoked. That (arguably) matches what the standard says, does it not?
- quelsolaar 7y agoTake 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."
- 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.