News for hackers. Stuff that matters.
Re:My favourite C++ footgun by armchairhacker on Tuesday June 22, 2021 @08:02PM (#27599004)
I always wonder if compilers actually do weird optimizations taking advantage of semantics like those.

Another example is when undefined behavior occurs, like (iirc) signed integer overflow. Compilers can technically do anything they want, including weird things. But how often does that actually happen?

Re:My favourite C++ footgun by rocqua on Wednesday June 23, 2021 @05:23AM (#27602492)
If you write something like

    int a;
    ...
    int b = a
    if (a + 1 > b){
       foo();
    }
Then compilers will use the fact that integer overflow is undefined behavior. This allows the compiler to assume the `if` statement will always be true. And hence it removes this check.

This isn't just the compiler removing a check the programmer wanted. This could occur because of constants, where other constants make the check meaningful. It could also occur after in-lining a specific function call.

Really, the only case where this optimization would be bad is if the author was trying to detect overflow. Sadly, detecting if something would cause undefined behavior in C code is hard. Because you cannot try the thing.

Re:My favourite C++ footgun by SAI_Peregrinus on Wednesday June 23, 2021 @09:15AM (#27604180)
Others have mentioned that compilers take advantage of these things for optimization, but haven't mentioned that compilers will still often do unexpected things when encountering UB with optimizations off.

Turning optimizations on doesn't suddenly undefine any behavior, it just changes what the compiler does when encountering some of those behaviors (among other, non-UB based optimizations). A non-optimizing compiler and an optimizing compiler both have to conform to the standard.

Re:My favourite C++ footgun by blake1 on Tuesday June 22, 2021 @09:14PM (#27599545)
Different architectures have their own function passing convention. It’s convenient to evaluate left-to-right, pushing as you go, if that matches the arch. And vice versa.
Re:My favourite C++ footgun by tsimionescu on Wednesday June 23, 2021 @02:27AM (#27601418)
That would explain why you want it implementation-defined, but leaving it indeterminate means that the same piece of code is allowed to have different evaluation orders each time it is encountered. This is much stranger, and very specific to C and C++, I don't think there is any other language that doesn't specify the order of execution.
Re:My favourite C++ footgun by vnorilo on Wednesday June 23, 2021 @04:53AM (#27602316)
If you think about how it interacts with inlining, it's easy to see how optimizers like this freedom. Suppose there's just one call that may throw and the others are pure functions. The compiler could bunch the pure ones together to optimize register and stack allocation.
Re:My favourite C++ footgun by atq2119 on Wednesday June 23, 2021 @04:57AM (#27602341)
This is the correct answer, plus add loop unrolling etc.

It is simply not possible to write an optimizing compiler in which the evaluation order is truly indeterminate yet always the same.

Re:My favourite C++ footgun by tsimionescu on Wednesday June 23, 2021 @05:52AM (#27602646)
I was claiming it could be left implementation defined instead of unspecified. That way, code for particular platforms could be evaluated in different order, but it would always be deterministic for a particular compiler.
Re:My favourite C++ footgun by vnorilo on Wednesday June 23, 2021 @06:34AM (#27602896)
The optimized evaluation order would depend on the situation near at the call site, not the architecture or platform.
Re:My favourite C++ footgun by tsimionescu on Wednesday June 23, 2021 @07:18AM (#27603148)
The GP that my post was replying to was claiming it was arch dependent, and I was pointing out that, if it had been, they would have likely not left it unspecified. So, I think you and I are in agreement.
Re:My favourite C++ footgun by tsimionescu on Wednesday June 23, 2021 @05:48AM (#27602626)
I would be truly shocked if this optimization ever mattered in practice. It would mean that rewriting code from

  {
    auto& a = <expression>
    auto& b = <expression>
    foo(a, b);
  } 
To

  foo(<expression>, <expression>);
Could be an optimization.

Edit: modified the code a bit to make the two samples more similar.

Re:My favourite C++ footgun by gpderetta on Wednesday June 23, 2021 @06:20AM (#27602815)
During the standardization of C++17, a proposal specifying the left-to-right order of evaluation of parameters (and other operations) came up and the committee got very close to accepting it, but in the end there were concrete examples in actual code that was worse of because of the proposal, so it was unfortunately weakened at the last minute.
Re:My favourite C++ footgun by simiones on Wednesday June 23, 2021 @10:50AM (#27605187)
Looking at the proposal in question [0], it seems very sad that the alternate, not recommended, choice was made instead [1] , especially since the reasoning seems to have not been put down in writing anywhere. The paper does mention that the difference in performance was <4%, with both improvements and worsening being seen by the VC++ implementers.

[0] http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0145r3....

[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p040...

Re:My favourite C++ footgun by vnorilo on Wednesday June 23, 2021 @06:38AM (#27602929)
Why shocked? Certainly a micro-optimization, but order of things often matters if the compiler cannot prove it's safe to speculate about the execution order. Maybe the surprising thing here is the theme of TFA, that the order of argument evaluation is not defined.
Re:My favourite C++ footgun by simiones on Wednesday June 23, 2021 @10:55AM (#27605252)
Given the out of order nature of processors, and the relatively limited scope for complex expressions given as function arguments, I would not expect this to matter in real-world programs. It seems this was also the opinion of the people drafting the new evaluation order changes [0] in C++17:

> We do not believe that such a nondeterminism brings any substantial added optimization benefit, but it does perpetuate the confusion and hazards around order of evaluations in function calls.It perpetuates unnecessary confusion around brace-initialization vs. direct initialization using parenthesis.

> We found that some entries in the benchmark suite ran slower, others ran faster compared to the scenario where the evaluation of the argument list is left unspecified. The variation is between -4% and +4%. It is worth noting that these results are for the worst case scenario where the optimizers have not yet been updated to be aware of, and take advantage of the new evaluation rules and they are blindly forced to evaluate function calls from left to right. It is clear that the left-to-right evaluation strategy is triggering new optimization paths (different inlining decisions and different register allocation) affecting the variations in the benchmark performance. It appears those opportunities have not traditionally been exploited, even though permitted under the unspecified order regime.

Unfortunately, they seem to have been overruled by the Core Working Group [1], for reasons I have not been able to dig up.

[0] http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0145r3....

[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p040...

Re:My favourite C++ footgun by mannerheim on Wednesday June 23, 2021 @02:57AM (#27601574)
Haskell? Expressions have to be evaluated in order to evaluate expressions that depend on them, but otherwise the compiler is free to decide the order of evaluation.

> In this case, it's pretty easy to see that 3 will have to be printed after both 1 and 2, but it's unclear whether 1 or 2 will be printed first. The reason for this is evaluation order: in order to evaluate one + two, we'll need to evaluate both one and two. But there is nothing telling GHC which of those thunks should be evaluated first, and therefore GHC is at full liberty to choose whichever thunk to evaluate first.

https://wiki.haskell.org/Evaluation_order_and_state_tokens

Re:My favourite C++ footgun by tsimionescu on Wednesday June 23, 2021 @05:59AM (#27602682)
Well, Haskell's lazy execution model and implicit purity makes this far different from C++. The examples there all rely on the guarantee-breaking unsafePerformIO, while in C++ you can break your program with perfectly safe,c normal C++ code, such as creating a shared pointer and throwing an exception.

Note that in Haskell evaluation order is not guaranteed anywhere, even between separate statements, whereas in C++ it is basically guaranteed everywhere except function calls.

Re:My favourite C++ footgun by mannerheim on Wednesday June 23, 2021 @01:18PM (#27607114)
unsafePerformIO was there to make the indeterminate evaluation order visible, but isn't what causes that indeterminate evaluation order. You are right, though, that for 'safe' Haskell code the evaluation order shouldn't matter, although it is sort of a matter of convention; 'head' is unsafe, but is presumably now permanently stuck in the language, while on the other hand perhaps one could write C++ without exceptions, but that wouldn't be 'normal' C++ code.
Re:My favourite C++ footgun by tsimionescu on Wednesday June 23, 2021 @03:03PM (#27608624)
Sure, my point was that, as far as I understand, even Haskell code that performed IO would have a well-determined order, if it was doing normal IO; whereas C++ code that does something as simple and safe looking as foo(i++, i) may produce different results in different runs (it was even UB before C++17, and still us in C; and even other common idioms, such as function chaining, had undefined execution order).
Re:My favourite C++ footgun by mannerheim on Wednesday June 23, 2021 @06:25PM (#27611325)
Yes and no. The order IO is performed is well-defined (that's the purpose of the IO monad), but the evaluation order is not (apart from ordering arising from data dependency), and this is the case with or without unsafePerformIO. You just won't get any different behaviour with safe Haskell code, and Haskell is very much on the safe side of things, so even though the order of evaluation may be indeterminate, you would never notice it (maybe a timing attack is possible?). But this is admittedly hair-splitting.
Re:My favourite C++ footgun by vnorilo on Wednesday June 23, 2021 @04:43AM (#27602261)
Most optimization uses of UB that I've seen is to take full liberty to remove branches and instructions. For example, if the result of UB is used in a comparison, the compiler is free to pick whichever side of the branch (even inconsistently) and elide. It is actually quite common with for example loop unrolling and vectorization (ignoring signed overflow of the indvar is a huge boon)
Re:My favourite C++ footgun by lokedhs on Wednesday June 23, 2021 @06:07AM (#27602739)
In fact, the compile is free to assume that the comparison itself will never happen and not emit any of the paths.
Re:My favourite C++ footgun by UncleMeat on Tuesday June 22, 2021 @10:28PM (#27600114)
The answer is yes. Compiler authors absolutely take advantage of these things for optimizations.

As for paranoia about undefined behavior? Compilers won't emit a program to call you a pizza or format your disk. There are risks, but those aren't real ones.

Re:My favourite C++ footgun by a_t48 on Wednesday June 23, 2021 @12:20PM (#27606266)
A bit of a nit - your storage driver could absolutely emit a program that formats your disk due to undefined behavior.
Re:My favourite C++ footgun by UncleMeat on Wednesday June 23, 2021 @01:37PM (#27607408)
If you already have a problem that contains instructions that are manipulating your disk in these sorts of ways then this is within some reason. But the usual hyper-concern about UB is that it'll take a program that otherwise does completely different things (imagine some CLI utility that just reads from stdin) and instead produce some maximally adversarial program that ruins your life. This ins't a real concern.
Re:My favourite C++ footgun by a_t48 on Wednesday June 23, 2021 @01:42PM (#27607482)
Agree :)