inline vs. function pointers

C

copx

C99 gave us the "inline" keyword and IIRC in standard C
(as opposed to certain propriety dialects) it is just a hint
for the compiler i.e. there is no guarantee that a function
declared "inline" will actually be inlined all the time.

Now, here is the problem, a certain C compiler when evoked
in ANSI C mode produces an error message when I put
pointers to "inline" functions in an array of function pointers.
The compiler actually inlines the function code in this case, never
putting the functions themselves in the resulting binary and
thus renders the pointers invalid.

Is that standard conforming behavior? I mean, is there a
rule that you are not allowed to use pointers to "inline" functions?
 
E

Eric Sosman

C99 gave us the "inline" keyword and IIRC in standard C
(as opposed to certain propriety dialects) it is just a hint for the
compiler i.e. there is no guarantee that a function
declared "inline" will actually be inlined all the time.

Now, here is the problem, a certain C compiler when evoked
in ANSI C mode produces an error message when I put
pointers to "inline" functions in an array of function pointers.
The compiler actually inlines the function code in this case, never
putting the functions themselves in the resulting binary and
thus renders the pointers invalid.

Is that standard conforming behavior? I mean, is there a
rule that you are not allowed to use pointers to "inline" functions?

Could you show a short (but complete and accurate!) sample
of the source code? And could you also exhibit the compiler's
complaint, instead of just describing it as "an error message?"
 
G

Guest

Now, here is the problem, a certain C compiler when evoked
in ANSI C mode produces an error message when I put
pointers to "inline" functions in an array of function pointers.
The compiler actually inlines the function code in this case, never
putting the functions themselves in the resulting binary and
thus renders the pointers invalid.

Is that standard conforming behavior? I mean, is there a
rule that you are not allowed to use pointers to "inline" functions?

The answer to the first question depends on the function's linkage.
The answer to the second question is "No".
 
C

copx

"Eric Sosman" wrote in message
Could you show a short (but complete and accurate!) sample
of the source code?

See below.
And could you also exhibit the compiler's
complaint, instead of just describing it as "an error message?"

Ok.

===== test.c =======

#include <stdio.h>

#define FIB_MAX 8000

typedef unsigned char BYTE;
typedef void (* const LSYSTEM[3])(BYTE **);

static void ls_init(BYTE *p) { *p = 1; *(p + 1) = 0; }
static inline void ls0(BYTE **p) { **p = 0; }
static inline void ls1(BYTE **p) { **p = 2; *p += 1;}
static inline void ls2(BYTE **p) { **p = 1; *p += 1; ls1(p);}


static BYTE Ls[2][FIB_MAX];


static unsigned int lsystem(unsigned int n)
{
BYTE *cur, *pre;
unsigned int dp = 0;

ls_init(Ls[0]);

do {
pre = Ls[dp]; cur = Ls[dp = !dp];

do {
static LSYSTEM l = {ls0, ls1, ls2};

l[*pre](&cur);

} while (*pre++);

} while (--n);

return cur - Ls[dp];
}


unsigned int fib(unsigned int n)
{
return n < 2 ? n : lsystem(--n);
}


int main(void)
{
unsigned int i;

for (i = 0; i < 21; i++) printf("F%u: %u\n", i, fib(i));

return 0;
}

========

gcc -Wall -Wextra -std=c99 -pedantic -O2 test.c
No warnings, compiles and runs as expected.

I suspect the compiler who chokes on this is buggy, but I am not
sure. Here's the output:

===
Warning d:\dev\test\test.c: 28 assignment of pointer to inline
function(pointer to pointer to unsigned char) to pointer to
void function(pointer to pointer to unsigned char)

Warning d:\dev\test\test.c: 28 assignment of pointer to inline
function(pointer to pointer to unsigned char) to pointer to
void function(pointer to pointer to unsigned char)

Warning d:\dev\test\test.c: 28 assignment of pointer to inline
function(pointer to pointer to unsigned char) to pointer to
void function(pointer to pointer to unsigned char)

Error test.obj: Undefined (*UND*). Symbol _ls0
=======

Putting "inline" in the LSYSTEM typedef eliminates the
warnings but not the error. Oh, and GCC seems to think "inline"
has no place in a typedef:

test.c:6:30: warning: typedef 'LSYSTEM' declared 'inline'

Good thing I don't usually use C99, so I don't have to worry
about such disagreements. This was just some experimental
code.
 
K

Keith Thompson

copx said:
Putting "inline" in the LSYSTEM typedef eliminates the
warnings but not the error. Oh, and GCC seems to think "inline"
has no place in a typedef:

test.c:6:30: warning: typedef 'LSYSTEM' declared 'inline'

I believe gcc is correct. "inline" is a function specifier (the only
one in the language).

C99 6.7.4p2:

Function specifiers shall be used only in the declaration of an
identifier for a function.

In other words, "inline" isn't part of the function type; it's an
attribute of an individual declared function.
 
C

copx

"copx" wrote in message news:[email protected]...
Answering my own question again: No.

C99 6.7.4
function specifier:
inline

"Likewise, the function has a single address, regardless of the number of
inline deï¬nitions that occur"

Oh well, another bug report.
 
J

jacob navia

Le 27/01/11 16:20, copx a écrit :
in message
Could you show a short (but complete and accurate!) sample
of the source code?

See below.
And could you also exhibit the compiler's
complaint, instead of just describing it as "an error message?"

Ok.

===== test.c =======

#include <stdio.h>

#define FIB_MAX 8000

typedef unsigned char BYTE;
typedef void (* const LSYSTEM[3])(BYTE **);

static void ls_init(BYTE *p) { *p = 1; *(p + 1) = 0; }
static inline void ls0(BYTE **p) { **p = 0; }
static inline void ls1(BYTE **p) { **p = 2; *p += 1;}
static inline void ls2(BYTE **p) { **p = 1; *p += 1; ls1(p);}


static BYTE Ls[2][FIB_MAX];


static unsigned int lsystem(unsigned int n)
{
BYTE *cur, *pre;
unsigned int dp = 0;

ls_init(Ls[0]);

do {
pre = Ls[dp]; cur = Ls[dp = !dp];

do {
static LSYSTEM l = {ls0, ls1, ls2};

l[*pre](&cur);

} while (*pre++);

} while (--n);

return cur - Ls[dp];
}


unsigned int fib(unsigned int n)
{
return n < 2 ? n : lsystem(--n);
}


int main(void)
{
unsigned int i;

for (i = 0; i < 21; i++) printf("F%u: %u\n", i, fib(i));

return 0;
}

========

gcc -Wall -Wextra -std=c99 -pedantic -O2 test.c
No warnings, compiles and runs as expected.

I suspect the compiler who chokes on this is buggy, but I am not
sure. Here's the output:

===
Warning d:\dev\test\test.c: 28 assignment of pointer to inline
function(pointer to pointer to unsigned char) to pointer to
void function(pointer to pointer to unsigned char)

Assigning function pointers with different characteristics will
always produce a warning.

Error test.obj: Undefined (*UND*). Symbol _ls0


static inline functions generate no function address.
If you want the function address eliminate the static...
=======

Putting "inline" in the LSYSTEM typedef eliminates the
warnings but not the error. Oh, and GCC seems to think "inline"
has no place in a typedef:

test.c:6:30: warning: typedef 'LSYSTEM' declared 'inline'

Good thing I don't usually use C99, so I don't have to worry
about such disagreements. This was just some experimental
code.

I do not see what C99 has to do with this here. Anyway. I will wait
till the next standard appears to fix those bugs. Maybe they will
declare that optional... :)
 
J

jacob navia

Le 28/01/11 08:54, Ian Collins a écrit :
But they don't have different types.
Yes but they have different characteristics: one is inline and the other
is not inline.

This warning is crucial when you have _stdcall functions. And
_stdcall is a similar attribute like inline.
Says who?

Me.

All the standard says is "Making a function an inline function suggests
that calls to the function be as fast as possible".
Yes, and I do not generate the code for it. Changing it is around
4-5 hours work including testing. I will do it when the next standard
appears, to be sure that inline is not declared optional.
 
C

copx

"jacob navia" wrote in message news:[email protected]...
Assigning function pointers with different characteristics will
always produce a warning.

But the pointers aren't different because the function specifier
("inline") is not supposed to be part of the function pointer type.

All the following functions..

static inline void foo(void);
static void foo(void);
inline void foo(void);

... have the same function pointer type: void (*)(void)

... at least in ISO C.
static inline functions generate no function address.

Is lcc a ISO C compiler or not? Because a conforming compiler
cannot simply decide that "static inline functions generate
no function address". IMO the standard is pretty clear about
the fact that you aren't allowed to optimize away the function
definitions here.
I do not see what C99 has to do with this here.

The point is that IMO the behavior of your compiler does not
conform to the C99 standard here and I haven't had such problems
with C90 code yet. That standard is fully supported by basically
every piece of software which calls itself "a C compiler". C99
unfortunately isn't.
Anyway. I will wait till the next standard appears to fix those bugs.
Maybe they will declare that optional... :)

Your choice, cannot say I care much about this. I have just checked,
there aren't any big claims of conforming to ANY C standard
on the lccwin32 website, so it's ok I guess.

I can see why you want to stick to the current behavior. This
guaranteed inlining / function elemination behavior turns
static inline functions into full, type safe replacements for many
preprocessor macros. Also, not dealing with this particular
case (pointers to static inline functions) in a conforming way
probably simplifies the compiler logic.

I prefer ISO C compilers, though.
 
J

jacob navia

Le 28/01/11 11:46, copx a écrit :
in message news:[email protected]...

But the pointers aren't different because the function specifier
("inline") is not supposed to be part of the function pointer type.

All the following functions..

static inline void foo(void);
static void foo(void);
inline void foo(void);

.. have the same function pointer type: void (*)(void)

.. at least in ISO C.

Yes, they have the same type.

But

int _stdcall fn(void);
and
int fn(void);

have also the same type but if you mix pointers to one with pointers to
the other a crash is guaranteed. So I always warn.

OK, I could spend hours "fixing" that warning and detecting that the
pointers are different because of the inline attribute and NOT because
of a _stdcall attribute or _noreturns or others.

Maybe one day I will do it, but before I have to do the correction for
the bad code generation that you pointed out. I am working already
6 hours in that problem and there is no solution in sight. I discovered
that if you write

dp = !dp;
ptr = table[dp];

the bug disappears. It is only when you write

ptr = table[dp = !dp];

that the bug manifests itself (only when optimizations are on).
And I will have for X hours more, I do not know.

Your choice, cannot say I care much about this. I have just checked,
there aren't any big claims of conforming to ANY C standard
on the lccwin32 website, so it's ok I guess.

I can see why you want to stick to the current behavior. This
guaranteed inlining / function elemination behavior turns
static inline functions into full, type safe replacements for many
preprocessor macros. Also, not dealing with this particular case
(pointers to static inline functions) in a conforming way
probably simplifies the compiler logic.

Well, yes. I would have to detect when a function pointer is being
used. Then, realize that it is a pointer to an inline function. Then
I would have to add some annotation to the inline function pointer
symbol to generate ts code anyway.

WITHOUT INTRODUCING ANY NEW BUGS...

This is something like 1 day of work...
I prefer ISO C compilers, though.

I have been working for this project for 12 years or more. I have
financed all expenses and all my work. But I can't do more than what I
do now. If you would buy a maintenance contract you would be served
quickly. But you didn't.

jacob
 
C

copx

"jacob navia" wrote in message news:[email protected]...
OK, I could spend hours "fixing" that warning and detecting that the
pointers are different because of the inline attribute and NOT because
of a _stdcall attribute or _noreturns or others.

Maybe one day I will do it, but before I have to do the correction for
the bad code generation that you pointed out. I am working already
6 hours in that problem and there is no solution in sight. I discovered
that if you write

dp = !dp;
ptr = table[dp];

the bug disappears. It is only when you write

ptr = table[dp = !dp];

that the bug manifests itself (only when optimizations are on).

And I will have for X hours more, I do not know.
[snip]

Well, yes. I would have to detect when a function pointer is being
used. Then, realize that it is a pointer to an inline function. Then
I would have to add some annotation to the inline function pointer symbol
to generate ts code anyway.

WITHOUT INTRODUCING ANY NEW BUGS...

This is something like 1 day of work...
I prefer ISO C compilers, though.
I have been working for this project for 12 years or more. I have
financed all expenses and all my work. But I can't do more than what I
do now. If you would buy a maintenance contract you would be served
quickly. But you didn't.

And I won't. I am just a hobby programmer and my primary development
environment is MinGW + SciTE. I don't need lccwin32, I just like to test
my code with different compilers.

I never said that I *demand* that you fix these bugs/issues, because
as you pointed out I didn't pay a dime for this after all. I just reported
them.
Feel free to leave things as they are, by all means.
 
J

jacob navia

Le 28/01/11 13:13, copx a écrit :
And I won't.
OK.

I am just a hobby programmer and my primary development
environment is MinGW + SciTE. I don't need lccwin32, I just like to test
my code with different compilers.

I never said that I *demand* that you fix these bugs/issues, because
as you pointed out I didn't pay a dime for this after all. I just
reported them.

Thank you for your bug reports.
I just notice that never any customer asked for C99 or standards
compliance or similar stuff. Only people in this forum.
Feel free to leave things as they are, by all means.

I will fix the bugs and ignore any "standard compliance" issues.
 
K

Keith Thompson

jacob navia said:
Le 28/01/11 08:54, Ian Collins a ecrit :
Yes but they have different characteristics: one is inline and the other
is not inline.

But the standard doesn't require a diagnostic when the pointers
have different *characteristics*. It requires a diagnostic
when they have different *types*. And in this case, they don't.

Of course a diagnostic is permitted, but it's not required -- and if a
compiler that rejects a program because of it (not the case here) would
be non-conforming.
This warning is crucial when you have _stdcall functions. And
_stdcall is a similar attribute like inline.

The major difference being that _stdcall is non-standard, so of course
the language standard says nothing about it.

[...]
Yes, and I do not generate the code for it. Changing it is around
4-5 hours work including testing. I will do it when the next standard
appears, to be sure that inline is not declared optional.

Seriously, I don't think you need to worry about that; inline is
too generally useful to be made optional. (Not speaking for the
committee, of course.)
 
K

Keith Thompson

jacob navia said:
Yes, they have the same type.

But

int _stdcall fn(void);
and
int fn(void);

have also the same type but if you mix pointers to one with pointers to
the other a crash is guaranteed. So I always warn.

OK, I could spend hours "fixing" that warning and detecting that the
pointers are different because of the inline attribute and NOT because
of a _stdcall attribute or _noreturns or others.
[...]

Issuing warnings that aren't required is not a conformance issue.

(It sounds like you're internally associating the "inline"
characteristic with the function type; logically, it belongs with
the function itself.)
 
K

Keith Thompson

jacob navia said:
Le 28/01/11 13:13, copx a ecrit : [...]
I never said that I *demand* that you fix these bugs/issues, because
as you pointed out I didn't pay a dime for this after all. I just
reported them.

Thank you for your bug reports.
I just notice that never any customer asked for C99 or standards
compliance or similar stuff. Only people in this forum.

Speaking only for myself, I don't believe I've ever *asked* you
for C99 compliance; I've merely commented on how closely lcc-win32
does or doesn't appear to conform to the standard, based on your
statements. You've certainly made an issue of C99 conformance
yourself, at least in discussions here.
I will fix the bugs and ignore any "standard compliance" issues.

That's entirely up to you, of course.

I'll just mention in passing that you've falsely accused me of
fighting against C99 because I've pointed out that implementations
are not universally available. Now you're making direct decisions
that will reduce that availability even further. I find that
interesting.
 
I

Ian Collins

Le 28/01/11 08:54, Ian Collins a écrit :
Yes, and I do not generate the code for it. Changing it is around
4-5 hours work including testing. I will do it when the next standard
appears, to be sure that inline is not declared optional.

Hang on, so you don't do any checking to see if a function should be
inlined? Say someone declared a 2000 line function 'inline', would you
still inline it?
 
K

Keith Thompson

Ian Collins said:
Hang on, so you don't do any checking to see if a function should be
inlined? Say someone declared a 2000 line function 'inline', would you
still inline it?

It seems to me that if you explicitly mark a function inline, you can't
reasonably complain if the compiler actually inlines it. It could even
make sense if there's only one call to it.
 
I

Ian Collins

It seems to me that if you explicitly mark a function inline, you can't
reasonably complain if the compiler actually inlines it. It could even
make sense if there's only one call to it.

Maybe, but it goes against the spirit of "calls to the function be as
fast as possible".
 
K

Keith Thompson

Ian Collins said:
Maybe, but it goes against the spirit of "calls to the function be as
fast as possible".

How so? It still saves the overhead of the call and return. And it
could introduce more opportunities for optimizations, as the code inside
the function can be optimized along with the code in the caller.
 
I

Ian Collins

How so? It still saves the overhead of the call and return. And it
could introduce more opportunities for optimizations, as the code inside
the function can be optimized along with the code in the caller.

For larger function the overhead is noise.

Determining which functions are suitable for inline is an increasingly
complex optimisation. In the days before caches, it was a size/space
trade off. With modern processors and operating systems, over
aggressive inlining can force a function out of the cache, or force
unnecessary page faults. So the inline heuristic isn't trivial which is
one reason compilers employ techniques like profile feedback
optimisation to determine the best combination.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top