11

How (in GCC/"GNU C") do you declare a function pointer which points to an __attribute__((const)) function? The idea being that I want the compiler to avoid generating multiple calls to the function called through the function pointer when it can cache the return value from a previous call.

R.. GitHub STOP HELPING ICE
  • 201,833
  • 32
  • 354
  • 689
  • Never have done that and almost falling asleep to research, but try wrapping a call by address of such a function with explicitly declared function that has const attribute and accepts that pointer as a parameter. If gcc can determine that pointer address itself & arguments are not changing - it should eliminate unnecessary calls. –  Feb 25 '12 at 04:37
  • 1
    @Vlad: I thought of that too, but then gcc refuses to inline the function in cases where I want it to. Originally I had a wrapper function like that, but I removed it to fix the inlining behavior. In case it's interesting, the function in question is `((pthread_t (*)(void))0xffff0fe0)` (the Linux-ARM get-thread-pointer function). – R.. GitHub STOP HELPING ICE Feb 25 '12 at 04:41
  • Interesting question. Did justin's answer have the desired result? – Praxeolitic Dec 11 '16 at 05:33

2 Answers2

6
typedef void (*t_const_function)(void) __attribute__((const));

static __attribute__((const)) void A(void) {
}

static void B(void) {
}

int main(int argc, const char* argv[]) {
    t_const_function a = A;

    // warning: initialization makes qualified
    // function pointer from unqualified:
    t_const_function b = B;

    return 0;
}

Or just:

__attribute__((const)) void(*a)(void) = A;
justin
  • 103,167
  • 13
  • 178
  • 224
  • Bleh, `typedef` is always the solution when you have a nasty function pointer type. Accepted. But any idea if there's a way to write the cast in my comment on the main question without a `typedef`? – R.. GitHub STOP HELPING ICE Feb 25 '12 at 07:49
  • @R. Unfortunately, I do *not* know how to wind it all into one statement without a `typedef` -- it doesn't appear to be possible on GCC 4.2. `((__attribute__((const)) pthread_t(*)(void))0xffff0fe0)` is how I think it would be done, but it's interpreted different from what you want (it appears GCC applies the attribute to the return type, not the function). – justin Feb 25 '12 at 08:27
0

Although this is not quite the answer to your question, you probably want to know this:

You can't in the general case expect the compiler to perform the optimization you expect here. The compiler cannot in the general case do the alias analysis necessary to know that multiple uses of a function pointer correspond to the same function.

A function call in between two invocations of the function through the pointer could, in the general case, alter the pointer contents, thus causing the invoked function to be different in the second call.

Because of the nature of C, doing proper alias analysis is often intractable, and this sort of optimization is thus not likely to happen.

Perry
  • 4,193
  • 1
  • 16
  • 19
  • 2
    That is what __attribute__((const)) does - it tells the compiler that you know better and gives a green light for certain optimizations. –  Feb 25 '12 at 04:39
  • In my case it can know, though, because the pointer is an address literal (an integer cast to a function pointer). See the comments. – R.. GitHub STOP HELPING ICE Feb 25 '12 at 04:41
  • 2
    @Vlad: I think Perry's point was that even if the pointed-to function is `const`, the compiler would also have to be sure that the *pointer* did not change between invocations. But that's not too hard to determine, and in my case it's not possible since it's a literal absolute address. – R.. GitHub STOP HELPING ICE Feb 25 '12 at 04:42
  • The compiler can handle declaring a function attribute const no problem. The issue is when you start indirecting through function pointers. The literal name of a function is in effect a constant pointer and the compiler can know it isn't altered -- but a variable that contains a pointer to an attribute const function is different. – Perry Feb 25 '12 at 04:43
  • @R..: it actually is surprisingly hard to determine in the general case. This is why C99 added keywords to indicate pointers would not be aliased in function calls for example. It is a giant mess. Even though you're using a literal absolute address, I suspect the compiler doesn't know how to optimize that since there are so few cases where it can... – Perry Feb 25 '12 at 04:46
  • If `func` is a function pointer and it is never assigned-to between multiple calls to `func()`, and either `&func` has never been taken or no writes through character pointers have taken place between the calls to `func()`, then it's trivial for the compiler to know the same function is called each time. – R.. GitHub STOP HELPING ICE Feb 25 '12 at 04:48
  • In any case, see the comment on the main question. While a function pointer type is being used, there is no function pointer variable in play. Just a literal absolute address cast to a function pointer type. – R.. GitHub STOP HELPING ICE Feb 25 '12 at 04:48
  • As an alternative, are you in a position to save the output of the first call to the function on your own? – Perry Feb 25 '12 at 04:50
  • (BTW, you say "no writes to pointers occur between the calls to func()", but the problem is, any function called between the two invocations could set any globally accessible data structure. If there are no calls to other functions in between the two calls to func() and no writes to any pointer in between, yes, you are okay, but typical code is messier.) – Perry Feb 25 '12 at 04:53
  • Saving the result myself is always an option, but it results in a significant amount of duplication of idiomatic code. This is true about any use of `__attribute__((const))`. As for your second comment, in a very common case (the most common?) the function pointer is a local automatic variable whose address is never taken, in which case it's trivial to determine that it wasn't modified. – R.. GitHub STOP HELPING ICE Feb 25 '12 at 05:03
  • 1
    If you know that the pointer to func would never change the easiest would probably be to declare it `register` as you say, but also to declare the pointer itself `const` qualified. – Jens Gustedt Feb 25 '12 at 07:58