Rendered at 23:37:32 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
kazinator 3 days ago [-]
> The question here is: can the compiler hoist the final division operation above assignment to x?
A division calculation not a visible effect. Therefore the question doesn't even m ake sense: it's asking: can we see something that is not a visible effect (division) before or after a visible effect (modification of a volatile object).
uecker 3 days ago [-]
But a division that traps because you divide by zero would prevent previous observable behavior from happening when hoisted above it. So it is not about the division itself but about the indirect effect on other behavior that can be observed.
Compilers already understand this: For example, they would not hoist a potentially trapping operation such as a division above a function call. The issue is that they did not apply this rule also to volatile accesses, but LLVM now does because this was fixed.
kazinator 3 days ago [-]
but "previous observable behavior" just means whatever actually happened, not that it was required to be previous.
uecker 3 days ago [-]
I do not understand what you mean by "whatever actually happened". The point is that before compilers were fixed previous volatile stores were not safe from compiler optimizations (GCC still has this bug, but now fixed on LLVM) or that even previous function calls were not safe (fixed in MSVC and GCC).
wahern 3 days ago [-]
What if x is a register to toggle divide-by-zero exceptions? Normally this is none of the compilers business, but the whole point of volatile is a mechanism to express stuff like that, so it needs minimal semantics, e.g. no time traveling (compiler barrier), to be fit for purpose.
kazinator 3 days ago [-]
x being a register connected to the processor's division machinery doesn't speak to the fact that division isn't a language-defined visible effect, and so the implementation is not required to schedule it in a certain way in relation to the store to x.
wahern 3 days ago [-]
Hmmmmm. It's late, but FWIW I think this comment from Martin from last year might be relevant: https://news.ycombinator.com/item?id=40838721 And I'm guessing the note that was added is fn#150 in C23. Volatile accesses aren't general memory barriers, but I think Martin's point is the potential for UB behavior means the compiler has to preserve the sequence order of the potentially UB operation relative to the volatile access.
kazinator 1 days ago [-]
The potential for undefined behavior means ... the potential for a situation to emerge where no further requirements apply.
If you regard undefined behavior as having ordering requirements, it's game over for optimization (not to mention the current understanding of undefined behavior). Any time you have bona fide observable effects near calculation, you are hamstrung, because the potential for UB is in every nook and cranny, around every corner.
I think it's perfectly fine for the division by zero to occur before the output is produced and flushed, in that example. The UB of a bad division can time travel because a division is assumed not to have UB and can be reordered before a visible effect to which it is not related (the effect doesn't require the result of the division, nor does the division require an output tied to the production of the visible effect).
What these people are looking for is a safe calculation mode in which a bad division does something that is well-defined, like raise an exception that can be caught. That is then deemed visible, and must not be reordered w.r.t. other visible effects. And while it does hamper optimization, it is a translation mode that can be enabled or disabled around a block of code. If the programmer is certain there is no bad division, or else don't care about that being ordered w.r.t. a visible effect, then they declare a low safety level. (Which, this still being C, could be the default level).
As long as C standardization thinking remains obtuse or hostile w.r.t. this type of programmer-controlled safety level concept, they will be forever confounded by dilemmas caused by wanting intuitive outcomes that logically require undefined behavior to be partially defined.
uecker 1 days ago [-]
Both the C and C++ standardization committees have decided that even when there is undefined behavior the program is partially defined. But nobody is confounded by this.
kazinator 20 hours ago [-]
Suppose we require it that the output flush must take place before the behavior becomes undefined by a bad division. That doesn't buy much because the visible effect is only that some bytes are passed from the stdout stream to the host environment. The undefined behavior is allowed to bring down that entire environment though; so our requirements don't add up to the certainty that the output has been transmitted out of that host system to some output device (display, serial or network, ...), or committed to durable storage. (The latter could be wiped out by the UB, even if so).
Basically, standardization groups should be dissolved long before they devolve into stuff like this, where they are just creating churn for the sake of their own continuation.
uecker 18 hours ago [-]
We don't need to suppose this, as this is what is already required in C. You are right that when I/O is performed it is up to the host system what happens, and it is also possible that on UB the whole system crashes if it is poor. But this is outside the jurisdiction of the C language.
What it is important though is that where C spec can be complemented by other specification that add reasonable requirements about the host system, e.g. that it does not crash then a program misbehaves or that I/O is performed according to certain rules, certain safety properties can then be ensured. This would not be possible without UB being restricted in this way. As compilers are now being fixed, this is not churn but a real world improvement people have asked for.
2 days ago [-]
codedokode 3 days ago [-]
I wish they added some simple form of generics so that one could, for example, create types like "list of files", "list of processes" without duplicating the code.
Also, something for RAII, so that one could automatically deallocate allocated memory.
Someone 3 days ago [-]
How does that help reduce undefined behavior in C?
I thought this would be about adding a switch by which to undo every silent code removal operation because the compiler concluded a section includes undefined behavior in some obscure case, like overflow. And doing so silently. So when you recompile on a new compiler, on embedded, your code now fails. Can't do that Dave.
How did we ever get here??
As a user that's so outrageous it must be a deliberate conspiracy against C. I'm sure it isn't but it certainly helps it's demise, as the feeling the tool is in the hands of imbeciles that mistake the trees for he forest. C now requires you to be an expert on undefined behavior, and the psychology regarding the matter, while previously C would be forgiving.
So I thought the article would be about --forgive --generous, or --embedded while there should have been --petty in the first place.
A division calculation not a visible effect. Therefore the question doesn't even m ake sense: it's asking: can we see something that is not a visible effect (division) before or after a visible effect (modification of a volatile object).
Compilers already understand this: For example, they would not hoist a potentially trapping operation such as a division above a function call. The issue is that they did not apply this rule also to volatile accesses, but LLVM now does because this was fixed.
If you regard undefined behavior as having ordering requirements, it's game over for optimization (not to mention the current understanding of undefined behavior). Any time you have bona fide observable effects near calculation, you are hamstrung, because the potential for UB is in every nook and cranny, around every corner.
I think it's perfectly fine for the division by zero to occur before the output is produced and flushed, in that example. The UB of a bad division can time travel because a division is assumed not to have UB and can be reordered before a visible effect to which it is not related (the effect doesn't require the result of the division, nor does the division require an output tied to the production of the visible effect).
What these people are looking for is a safe calculation mode in which a bad division does something that is well-defined, like raise an exception that can be caught. That is then deemed visible, and must not be reordered w.r.t. other visible effects. And while it does hamper optimization, it is a translation mode that can be enabled or disabled around a block of code. If the programmer is certain there is no bad division, or else don't care about that being ordered w.r.t. a visible effect, then they declare a low safety level. (Which, this still being C, could be the default level).
As long as C standardization thinking remains obtuse or hostile w.r.t. this type of programmer-controlled safety level concept, they will be forever confounded by dilemmas caused by wanting intuitive outcomes that logically require undefined behavior to be partially defined.
Basically, standardization groups should be dissolved long before they devolve into stuff like this, where they are just creating churn for the sake of their own continuation.
What it is important though is that where C spec can be complemented by other specification that add reasonable requirements about the host system, e.g. that it does not crash then a program misbehaves or that I/O is performed according to certain rules, certain safety properties can then be ensured. This would not be possible without UB being restricted in this way. As compilers are now being fixed, this is not churn but a real world improvement people have asked for.
Also, something for RAII, so that one could automatically deallocate allocated memory.
Also:
- for ‘generics’: X macros (https://en.wikipedia.org/wiki/X_macro, if desired in combination with _Generic (https://en.cppreference.com/c/language/generic), or use C++, if desired with linting that rejects use of some features.
- for RAII: I think ‘defer’ is in the pipeline. Not quite the same, but useful anyways. gcc and clang already support it (with not-so-nice syntax; see https://gcc.gnu.org/onlinedocs/gcc-8.1.0/gcc/Common-Variable..., look for cleanup)
How did we ever get here??
As a user that's so outrageous it must be a deliberate conspiracy against C. I'm sure it isn't but it certainly helps it's demise, as the feeling the tool is in the hands of imbeciles that mistake the trees for he forest. C now requires you to be an expert on undefined behavior, and the psychology regarding the matter, while previously C would be forgiving.
So I thought the article would be about --forgive --generous, or --embedded while there should have been --petty in the first place.