Does the Compiler Optimize the Function Parameters Passed by Value

C++: Can the Compiler Optimize a Passing by Value?

In C++11 the compiler could determine that bigData is reassigned after use in the function and pass it as rvalue, but there is no guarantee for that, unlike for the RVO (from c++17).

For std::vector at least you can make sure this happens by calling the function as modify(std::move(bigData)), which will construct the value in modify from the rvalue reference, which it cannot optimize with the RVO afaik, because it is the function parameter, which is explicitly excluded from this optimization (3rd point here). However the compiler should understand that the return value is an r-value, and move it into big-data again.

Whether some compilers elide a move from an object into a function and out of the function back into the object I don't know for sure, but I know nothing that explicitly allows it, and since the move-constructor could have observable side-effects, that probably means, that it is not allowed (cf. the Notes section in above link).

Does the compiler optimize the function parameters passed by value?

If you pass a variable instead of a temporary, the compiler is not allowed to optimize away the copy if the copy constructor of it does anything you would notice when running the program ("observable behavior": inputs/outputs, or changing volatile variables).

Apart from that, the compiler is free to do everything it wants (it only needs to resemble the observable behavior as-if it wouldn't have optimized at all).

Only when the argument is an rvalue (most temporary), the compiler is allowed to optimize the copy to the by-value parameter even if the copy constructor has observable side effects.

Will compiler optimize out unused arguments of static function?

I wrote this nonsense program to test this. It's got some nonsense code in the function, and calls it several times because otherwise the compiler just inlined the whole function call making the test useless. (It's a strange mix of C and C++ i know.... stupid code, but still works to demonstrate the issue)

#include <stdio.h>
#include <vector>
#include <algorithm>

using namespace std;

struct demo
{
int a;
char ch;
};

static void __fastcall func(struct demo* d)
{
for(int i = 0; i < 100; i++) {
std::vector<int> a;
std::sort(begin(a), end(a));
printf("THis is a test");
printf("Hello world\n");
}
}

int main()
{
// void*p = (void*)&func;
struct demo d;
func(&d);
func(&d);
func(&d);
func(&d);
func(&d);
func(&d);
func(&d);
//printf((const char*)p);
}

As written there the function calls compile to this :-

    func(&d);
00F61096 call func (0F61000h)
func(&d);
00F6109B call func (0F61000h)
func(&d);
00F610A0 call func (0F61000h)
func(&d);
00F610A5 call func (0F61000h)
func(&d);
00F610AA call func (0F61000h)
func(&d);
00F610AF call func (0F61000h)
func(&d);
00F610B4 call func (0F61000h)

Which shows that the compiler will indeed omit the parameter if it's not used.
However if I uncomment the two lines there to take the address of the function then things change, it instead generated this :-

00C71099  lea         ecx,[esp]  
00C7109C call func (0C71000h)
func(&d);
00C710A1 lea ecx,[esp]
00C710A4 call func (0C71000h)
func(&d);
00C710A9 lea ecx,[esp]
00C710AC call func (0C71000h)
func(&d);
00C710B1 lea ecx,[esp]
00C710B4 call func (0C71000h)

Where it DOES send the pointer.

My assumption is that in the first case the compiler is able to prove that if it generated a custom calling convention to the function that there cannot possibly be any user visible effects but in the second case where you take a pointer to the function, the function could be called from another separately compiled module which has no way to know if the parameter is needed or not. Although in this case it wouldn't matter if the register was set or not, in general the compiler needs to stickl to the exact calling pattern so I assume that it generated the most general code and won't use custom calling conventions if it can't prove that it can see all the code that could call the function.

Anyway, to answer the question, no the compiler will not always pass unused parameters in a register but I certainly wouldn't write any code that depends on any specific behavior here as changing unrelated code elsewhere (taking the address of the function), changed that behavior and there is nothing guaranteed by any standard that I can see.

Compiler optimization of functions parameters

Function parameters are placed on the stack, but compilers can optimize this task by the use of optional registers.

As FrankH says in his comments and as I'm going to say in my answer, the application binary interface for the system in question determines how arguments are passed to functions - this is called the calling convention for that platform.

To complicate matters, x86 32-bit actually has several. This is historical and comes from the fact that when Win32 bit arrived, everyone went crazy doing different things.

So, yes, you can "optimise" by writing function calls in such a way, but no, you shouldn't. You should follow the standards for your platform. Because the honest truth is, the speed of stack access probably isn't slowing your code down to that great an extent that you need to be binary-incompatible from everyone else on your system.

Why the need for ABIs/standard calling conventions? Well, in terms of using the processor registers, stack etc, applications must agree on what means what and where it shoudl go. If one function decided all its arguments were in registers and another that some were on the stack, how would they be interoperable? Moreover, you might come across the term scratch registers to mean those registers you don't have to restore. What happens if you call a function expecting it to leave some registers alone?

Anyway, as for what you asked for, here's some ABI documentation:

  • The difference between x86 and x64 on windows.
  • x86_64 ABI used for Unix-like platforms.
  • Wikipedia's x86 calling conventions.
  • A document on compiler calling conventions.

The last one is my favourite. To quote it:

In the days of the old DOS operating system, it was often possible to combine development
tools from different vendors with few compatibility problems. With 32-bit Windows, the
situation has gone completely out of hand. Different compilers use different data
representations, different function calling conventions, and different object file formats.
While static link libraries have traditionally been considered compiler-specific, the
widespread use of dynamic link libraries (DLL's) has made the distribution of function
libraries in binary form more common.

So whatever you're trying to do with optimising via modifying the function calling method, don't. Find another way to optimise. Profile your code. Study the compiler optimisations you've got for your compiler (-OX) if you think it helps and dump the assembly to check, if the speed is really that crucial

Is it efficient passing structs as parameters by value to a function?

If a structure is small, pass it by value. Modern calling conventions provide for passing small structures in registers, on suitable platforms.

If a structure is large, you might define your routine to receive the structure by reference (using a pointer to a const-qualified type).

You should also declare the pointer with restrict, as in const struct foo * restrict p. Otherwise, it is possible that passing a structure by reference can impair optimization. Consider passing const struct foo *p to a routine that also has a parameter struct foo *q or float *f. Inside the routine, if the code changes the structure that q points to or the float that f points to, the compiler cannot generally know that q is not pointing to the same structure as p or that f is not pointing to a float that is a member of the structure p points to. This can cause the compiler to reload data from p multiple times when in fact no changes to p are occurring. Using restrict tells the compiler that caller will not pass a p such that data using via p will not also be used inside the routine via other points.

Another related question is if compilers do optimizations in this sense, converting parameter passing by value to reference.

This is not common. However, returning a structure by reference is common. The calling convention is typically that the caller provides a place for the returned structure to be written to and passes a pointer to that, invisibly to the source code.

Optimizing away static variable / passing by reference

that compilers are allowed to optimize away a static variable if the address is never taken

You seem to concentrated on the wrong part of the answer. The answer states:

the compiler can do anything it wants to your code so long as the observable behavior is the same

The end. You can take the address, don't take it, calculate the meaning of life and calculate how to heal cancer, the only thing that matters is observable effect. As long as you don't actually heal cancer (or output the results of calculations...), all calculations are just no-op.

f there exists a function which takes its arguments by reference, is the compiler still allowed to optimize away the variable

Yes. The code is just putc('3').

Is the situation different for the "perfect forwarding" case

No. The code is still just putc('3').

would the compiler be allowed to optimize the call in the following case

Yes. This code has no observable effect, contrary to the previous ones. The call to f() can just be removed.

in whether compilers do this kind of optimization?

Copy your code to https://godbolt.org/ and inspect the assembly code. Even with no experience in assembly code, you will see differences with different code and compilers.

Choose x86 gcc (trunk) and remember to enable optimizations -O. Copy code with static, then remove static - did the code change? Repeat for all code snippets.

C++ compiler optimization of passed arguments

If you need to be able to selectively enable and disable the warnings at run-time, the compiler will not be able to optimize out the call.

What you need is to rename your function to WARN2 and add a macro something like:

#define WARN(s) do {if (WARNINGS_ENABLED) WARN2(s);} while (false)

This will prevent the evaluation of s at run-time unless you have warnings enabled.

The do-while stuff is a trick that allows it to be used anywhere in the code (naked statement, statement within a braced if-block, statement within an unbraced if-block, braced and unbraced while statements and so on).

Is C compiler allowed to optimize away unused function arguments?

To begin with, "declared to take no arguments" is wrong. int veryImportantFunc() is a function accepting any arguments. This is obsolete C style and shouldn't be used. For a function taking no arguments, use (void).

Is the compiler allowed to call the function without passing these arguments?

If the actual function definition does not match the number of arguments, the behavior is undefined.

Also, what if argument evaluation has side effects

Doesn't matter, since arguments are evaluated (in unspecified order) before the function is called.

Is the compiler obligated to evaluate even without passing the result, or would it be legal to drop the evaluation leaving counter == 1?

It will evaluate the arguments and then invoke undefined behavior. Anything can happen.

And finally, what about extra arguments:

Your example won't compile, as it isn't valid C.



Related Topics



Leave a reply



Submit