-->

How not specify an exact order of evaluation of fu

2020-07-22 10:13发布

问题:

#include <iostream>
int foo() {
    std::cout<<"foo() is called\n";
    return 9;
}
int bar() {
    std::cout<<"bar() is called\n";
    return 18;
}
int main() {
    std::cout<<foo()<<' '<<bar()<<' '<<'\n';
}
// Above program's behaviour is unspecified
// clang++ evaluates function arguments from left to right: http://melpon.org/wandbox/permlink/STnvMm1YVrrSRSsB
// g++ & MSVC++ evaluates function arguments from right to left
// so either foo() or bar() can be called first depending upon compiler.

Output of above program is compiler dependent. Order in which function arguments are evaluated is unspecified. The reason I've read about this is that it can result in highly optimized code. How not specify an exact order of evaluation of function argument helps compiler to generate optimized code?

AFAIK, the order of evaluation is strictly specified in languages such as Java, C#, D etc.

回答1:

I think the whole premise of the question is wrong:

How not specify an exact order of evaluation of function argument helps C & C++ compiler to generate optimized code?

It is not about optimizing code (though it does allow that). It is about not penalizing compilers because the underlying hardware has certain ABI constraints.

Some systems depend on parameters being pushed to stack in reverse order while others depend on forward order. C++ runs on all kinds of systems with all kinds on constraints. If you enforce an order at the language level you will require some systems to pay a penalty to enforce that order.

The first rule of C++ is "If you don't use it then you should not have to pay for it". So enforcing an order would be a violation of the prime directive of C++.



回答2:

It doesn't. At least, it doesn't today. Maybe it did in the past.

A proposal for C++17 suggests defining left-right evaluation order for function calls, operator<< and so on.

As described in Section 7 of that paper, this proposal was tested by compiling the Windows NT kernel, and it actually led to a speed increase in some benchmarks. The authors' comment:

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.

suggests that there is further room for speed improvement.



回答3:

The order of evaluation is related to the way arguments are passed. If stack is used to pass the arguments, evaluating right to left helps performance, since this is how arguments are pushed into the stack.

For example, with the following code:

void foo(bar(), baz());

Assuming calling conevention is 'passing arguments through the stack', C calling convention requires arguments to be pushed into stack starting from the last one - so that when callee function reads it, it would pop the first argument first and be able to support variadic functions. If order of evaluation was left to right, a result of bar() would have to be saved in the temporary, than baz() called, it's result pushed, following by temporary push. However, right-to-left evaluation allows compiler to avoid the temporary.

If arguments are passed through registers, order of evaluation is not overly important.



回答4:

The original reason that the C and C++ standards didn't specify and an order of evaluation for function arguments is to provide more optimization opportunities for the compiler. Unfortunately, this rationale has not been backed up by extensive experimentation at the time these languages were initially designed. But it made sense.

This issue has been raised in the past few years. See this blog post by Herb Sutter and don't forget to go through the comments.

Proposal P0145R1 suggests that it's better to specify an order of evaluation for function arguments and for other operators. It says:

The order of expression evaluation, as it is currently specified in the standard, undermines advices, popular programming idioms, or the relative safety of standard library facilities. The traps aren’t just for novices or the careless programmer. They affect all of us indiscriminately, even when we know the rules.

You can find more information about how this affects optimization opportunities in that document.

In the past few months, there has been a very extensive discussion about how this change in the language affects optimization, compatibility and portability. The thread begins here and continues here. You can find there numerous examples.