C++ function pointer optimization

Started by
21 comments, last by NotAYakk 17 years, 10 months ago
Is there any chance of function calls via a pointer will be inlined and optimised away? Hypothetically speaking? I tried this simple contrived example in MSVC++ 2005 EE, Release, inlining Any Suitable, Link Time Code Generation (compiler+linker). The inline depth should be the default 254, but I manually set in anyway:

#pragma inline_depth(254)

inline int SimpleFunction ()
{
    return 1200;
}

typedef int (*SimpleFunctionPointerType) ();

// const there should make the function pointer const, I believe
SimpleFunctionPointerType const SimpleFunctionGlobalPointer (&SimpleFunction);

int main ()
{
    int k = 0;

    // const here should make the function pointer const, I believe
        SimpleFunctionPointerType const SimpleFunctionLocalPointer (&SimpleFunction);
        k += SimpleFunctionLocalPointer ();
004012AE  call        SimpleFunction (401250h)
004012B3  add         eax,edi
004012B5  add         esi,eax
        k += SimpleFunctionGlobalPointer ();
004012B7  call        dword ptr ds:[4021C8h]

    cout << k;
}

I'm trying to make things real simple here for the compiler. const function pointers to a locally declared function. It's interesting that the LocalPointer gets resolved to a regular function call, but then the compiler doesn't make the next step of inlining that (simple) function call. The call itself resolves to a mov eax,1200 ret :S Presumably there's greater difficulty involved in inlining the GlobalPointer, because the compiler has more trouble in general, determining if the pointer is safe/const?
Advertisement
Quote:Original post by Ro_Akira
Is there any chance of function calls via a pointer will be inlined and optimised away? Hypothetically speaking?


Hélas, no. I guess it's compiler dependent (I'm not brave enough to search the exact behavior in the Holy One). It's even the contrary: most of the time, you can force the compiler to not inline a function call by using a function pointer. For example, consider this code:

int function(int param){   return param+1;}int main(int argc, char* argv[]){   int i;      i = function(100);   i = (true ? function : NULL) (i);   std::cout << "i = " << i << std::endl;   return 0;}


The code resolves to:

int function(int param){   return param+1;// 00401000  mov         eax,dword ptr [esp+4] // 00401004  inc         eax  }// 00401005  ret// ----------------------int main(int argc, char* argv[]){// 004018D0  push        esi     int i;      i = function(100);   i = (true ? function : NULL) (i);// 004018D1  push        65h                 // this is i = function(100);// 004018D3  call        function (401000h)  // function is called, due to the function pointer// 004018D8  add         esp,4 // ...


As you see, even if the compiler knows that "function" will be called (true is always true, so there's only one code path), it still consider it as a pointer to function - while he correctly inlined function(100).

HTH,
Quote:Original post by Ro_Akira
Is there any chance of function calls via a pointer will be inlined and optimised away? Hypothetically speaking?

Yes. GCC 3.3.1 fully inlines every example given in this thread.

Σnigma
Quote:Original post by Ro_Akira
Is there any chance of function calls via a pointer will be inlined and optimised away? Hypothetically speaking?


Taking the address of a function makes it unsuitable for inlining. The compiler should not be inlining the call.

Quote: // const here should make the function pointer const, I believe
SimpleFunctionPointerType const SimpleFunctionLocalPointer (&SimpleFunction);


There is no difference between const Foo and Foo const. There is a difference, however, between const Foo*, Foo const* on one hand, and Foo* const on the other.

Quote:Presumably there's greater difficulty involved in inlining the GlobalPointer, because the compiler has more trouble in general, determining if the pointer is safe/const?


Not just more difficulty, it's impossible. For all the compiler knows, you could link against a DLL that modifies that global variable.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Quote:Original post by Fruny
Taking the address of a function makes it unsuitable for inlining. The compiler should not be inlining the call.

Since the observable behaviour is identical whether the function call is inlined or called through a function pointer a C++ compiler is perfectly within its rights to inline a call written using a function pointer under the "as-if" rule:
Quote:C++ Standard, Final Draft, Section 1.8, Paragraph 1
The semantic descriptions in this International Standard define a parameterized nondeterministic abstract machine. This International Standard places no requirement on the structure of conforming implementations. In particular, they need not copy or emulate the structure of the abstract machine. Rather, conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained below.3)

3) This provision is sometimes called the "as-if" rule, because an implementation is free to disregard any requirement of the Standard as long as the result is as if the requirement had been obeyed, as far as can be determined from the observable behavior of the program.


Quote:Original post by Fruny
Not just more difficulty, it's impossible. For all the compiler knows, you could link against a DLL that modifies that global variable.

The function pointer was declared const. Therefore any attempt to modify it is undefined behaviour, thus allowing the compiler to inline the function call.

Σnigma
Quote:Is there any chance of function calls via a pointer will be inlined and optimised away? Hypothetically speaking?

Quote:Hélas, no....

Quote:Yes...

Unless Hélas has a meaning of 'Opposite of ' or similar, these two arguments appear to be in conflict.

Quote:There is no difference between const Foo and Foo const. There is a difference, however, between const Foo*, Foo const* on one hand, and Foo* const on the other.

Understood, and I thought what I gave would be an example of the later (Foo* const). Anyway, I changed the typedef to
typedef int (*const SimpleFunctionPointerType) ();

and it made no difference - to the compiler's output at least.

Quote:The function pointer was declared const. Therefore any attempt to modify it is undefined behaviour, thus allowing the compiler to inline the function call.

You see, this is what I'm getting at. If the compiler has a const pointer, to a function that it knows won't be coming from a DLL or some such, then it should be able to resolve it to a non function call, and thus inline. This is my thinking anyway. How common const pointers are, is another thing. Although references should provide the same opportunities as a const pointers should they not?

Check out the resolving it does to the LocalPointer above. Do my eyes deceive me, or is that a simple function call, which should in turn be inlined? Can anyone explain the behaviour?

I'll see if I can check out gcc...
Fruny just got beaten at the standards game; I am in awe.

Anyhow, OP, you probably shouldn't even worry about it. The odds that will be the bottleneck of your program are VERY small.
Quote:Anyhow, OP, you probably shouldn't even worry about it. The odds that will be the bottleneck of your program are VERY small.

Most likely. But it's just interesting to know sometimes :)
Quote:
I'll see if I can check out gcc...


GCC converts both calls via function pointer to direct calls, even with optimisation disabled. With -O3, both calls to the function compile down to a single instruction:
mov eax, 2400


EDIT: Now I see this was posted earlier, by Enigma:

Quote:GCC 3.3.1 fully inlines every example given in this thread.
Even in debug you say? GCC kicks far more ass than I suspected :o

The example that Emmanuel Deloget provided:
i = (true ? function : NULL) (i);// 004018D1  push        65h                 // this is i = function(100);// 004018D3  call        function (401000h)  // function is called, due to the function pointer// 004018D8  add         esp,4 

I get the same result as Emmanuel with VC++ 2005 EE Release. If I change it to a comparable if-else statement though, the compiler sees it for what it is. Is the conditional operator handled differently by the compiler? A question for another thread I guess.
Edit: Enigma said all mentioned in the thread to his post were inlined - I presume he's including this too! I thought VC++ 2005 was pretty good until today, what with it's Link Time Program Generation and all.

=== C++ - Virtual Functions ===
You'll notice that I mentioned C++ in this thread's title, but mostly I've been talking just about function pointers. The whole reason I got into this line of thought is with C++ encounters like this:
Derived d;int Derived::VirtualFunction () const{    return simple_expression;}void Function (const Base& b){    cout << b->VirtualFunction () << endl;}int main (){    Derived d;    Function (d);    return 0;}

When I was finding that even simple
base_pointer_to_base_object->VirtualFunction ()

calls were not being inlined, I simplified the question to functions being called by pointers in general.

Is there any chance for the compiler to inline the above call to VirtualFunction within Function, as called?

[Edited by - Ro_Akira on June 6, 2006 10:40:13 AM]

This topic is closed to new replies.

Advertisement