Nested function prototypes (C99)

I

Ico

Hello,

(My apologies if discussing C99-features is not appropriate in this
newgroup)

I'm trying to build a state machine in C99 using nested functions. This
is for a certain embedded architecture for which my compiler is not
particulary good at optimising switch() statements.

The code flow is basically something like this:

int main(void)
{
void (*p)(void);

void one(void)
{
p = two;
}

void two(void)
{
p = one;
}


p();
p();
}

This will not compile howver, because the function two() is not known from
within function one(). My first attempt would be to simple add a
prototype for two(), but this not seem to be possible according to my
compiler (Which happens to be gcc)

Is the above construct possible ?

Thank you,
 
I

Ico

Ico said:
(My apologies if discussing C99-features is not appropriate in this
newgroup)

I'm trying to build a state machine in C99 using nested functions.

Hmm, that's a bad start: this is not a C99 feature, but rather a gcc
extension.

Sorry for bothering you all.
 
E

Eric Sosman

Hello,

(My apologies if discussing C99-features is not appropriate in this
newgroup)

I'm trying to build a state machine in C99 using nested functions. This
is for a certain embedded architecture for which my compiler is not
particulary good at optimising switch() statements.

The code flow is basically something like this:

int main(void)
{
void (*p)(void);

void one(void)
{
p = two;
}

void two(void)
{
p = one;
}


p();
p();
}

This will not compile howver, because the function two() is not known from
within function one(). My first attempt would be to simple add a
prototype for two(), but this not seem to be possible according to my
compiler (Which happens to be gcc)

Is the above construct possible ?

No. In C, all functions are "top level," that is, they
appear at file scope and not inside other functions. Sorry.

Your dodge of using function pointers is fine, but the
functions they point at must exist at the top level of the
source file. Among other things, this means they do not share
their inner workings -- in particular, no function can "see"
the local variables inside another, so you must make other
arrangements for whatever inter-function communication you need.

By the way: I hope that you have *measured* the performance
of `switch' in your environment -- and found it inadequate --
and are not simply proceeding on a general sense of deficiency.
Even if the `switch' itself is slow (or bloated, or whatever
your issue is), replacing it with function calls is not certain
to improve matters. You'll incur all those function entry and
function return sequences, and you'll (probably) give up any
chance at optimizations that extend over multiple cases. Measure
before you cut, as the carpenters say.
 
I

Ico

Eric Sosman said:
No. In C, all functions are "top level," that is, they
appear at file scope and not inside other functions. Sorry.

Yes, I know it is not possible in C, but our implementation (gcc) does
support this.
By the way: I hope that you have *measured* the performance
of `switch' in your environment -- and found it inadequate --
and are not simply proceeding on a general sense of deficiency.

Well, we're in the process of measuring, so moving the code into separat
functions was a first try to see wether this results in a speedup or
not. I'm aware of the function call overhead, but since most of the
functions only do some very low level memory access for hardware I/O, we
were hoping this might result in less overhead for pushing/popping
registers and all other stuff happening on this particular architecture.

The choice for inline functions over top-level functions was made
because of the scope of variables in the top-level function which needs
to be accessible by the code in the nested functions; this seems to be
the fastest way to move code around without changing much to the
structure.
Even if the `switch' itself is slow (or bloated, or whatever
your issue is), replacing it with function calls is not certain
to improve matters.

Of course not, but I'd like to measure first :)
You'll incur all those function entry and function return sequences,
and you'll (probably) give up any chance at optimizations that extend
over multiple cases. Measure before you cut, as the carpenters say.

Yes sir, aware of that !

Thanks for your input

Ico
 
B

Ben Bacarisse

Ico said:
Yes, I know it is not possible in C, but our implementation (gcc) does
support this.


Well, we're in the process of measuring, so moving the code into separat
functions was a first try to see wether this results in a speedup or
not. I'm aware of the function call overhead, but since most of the
functions only do some very low level memory access for hardware I/O, we
were hoping this might result in less overhead for pushing/popping
registers and all other stuff happening on this particular architecture.

The choice for inline functions over top-level functions was made

These are nested functions -- inline functions are something else
entirely!
because of the scope of variables in the top-level function which needs
to be accessible by the code in the nested functions; this seems to be
the fastest way to move code around without changing much to the
structure.

Note that taking the address of a nested function requires a slightly
more complex procedure than usual (a search for "gcc trampoline" might
get you more information).

I'd have thought the using labels and gotos is also worth considering.
Since you permit gcc extensions, you can also use labels as values so
they can be put into an array for those cases where a computed jump is
required (just like using function pointers).
Of course not, but I'd like to measure first :)

I think you'll have to measure non-nested functions as well as the
goto-based solution. Of course, if all you need is something fast
enough, you can stop when you've found it.

<snip>
 
I

Ico

Ben Bacarisse said:
These are nested functions -- inline functions are something else
entirely!

So true, I'm fully aware of which is which, I'm just messing up the
words, apologies.
I'd have thought the using labels and gotos is also worth considering.
Since you permit gcc extensions, you can also use labels as values so
they can be put into an array for those cases where a computed jump is
required (just like using function pointers).

Good tip, I will add this to the test, thank you.
 
I

Ico

William Ahern said:
IIRC GCC's nested functions use trampolines, small bits of dynamically
generated code. This is needed because there's no single definition for any
nested function; in a sense there's a new definition for every invocation of
the surrounding function.

This technique will fail on systems which write-protect instruction data.

Yes, I found out, the 'hard' way.
The workaround is to invoke mprotect or some other mechanism to disable this
protection.

Not possible if code resides in ROM, unfortunately :)
Maybe GCC is smart enough to handle this. If it does do this, then
performance is probably out the window.

Indeed it is.

Some benchmarking showed the fastest solution from the tested
alternatives seems to be the computed goto gcc extension, which will do
just fine for now.

Thanks,
 
K

Kenny McCormack

On MaxOS X it is not supported. Don't know if they removed it from the
compiler or if it just crashes, but they claimed they examined a
rather large code base and found _eight_ uses of the feature, all
easily removed, so this is definitely an extension not worth the
effort.

Indeed. FWIW:

$ cat x.c
#include <stdio.h>

int main(void)
{
int foo() { puts("This is foo()"); }
foo();
}
$ gcc x.c
x.c: In function 'main':
x.c:5: error: nested functions are not supported on MacOSX
$

--
(This discussion group is about C, ...)

Wrong. It is only OCCASIONALLY a discussion group
about C; mostly, like most "discussion" groups, it is
off-topic Rorsharch [sic] revelations of the childhood
traumas of the participants...
 
P

Peter Nilsson

Ico said:
I'm trying to build a state machine in C99 using nested
functions.

Why not build a state machine in C99 _without_ using nested
functions?
This is for a certain embedded architecture for which my
compiler is not particulary good at optimising switch()
statements.

So do your own jump table equivalent using an array.

int next_state[NUM_STATES];
The code flow is basically something like this:

int main(void)
{
        void (*p)(void);

auto void two(void);
        void one(void)
        {
                p = two;
        }

        void two(void)
        {
                p = one;
        }

        p();

Note that p is uninitialised.
        p();
}

What's wrong with a C90 version (or varient)...

void (*p)(void);

void one(void);
void two(void);

void one(void)
{
p = two;
}

void two(void)
{
p = one;
}

int main(void)
{
p();
p();
}
 
I

Ico

Peter Nilsson said:
Why not build a state machine in C99 _without_ using nested
functions?

What's wrong with a C90 version (or varient)...

Nothing wrong with that, this would be my first choise. But in this
particular codebase there are a lot of automatic variables which are
needed by the different state handlers. Using nested functions makes
these accessible without having to refactor a lot of the code.
 
I

Ico

Kenny McCormack said:
Indeed. FWIW:

$ cat x.c
#include <stdio.h>

int main(void)
{
int foo() { puts("This is foo()"); }
foo();
}
$ gcc x.c
x.c: In function 'main':
x.c:5: error: nested functions are not supported on MacOSX

For some reason it's supported but not enabled by default:

$ gcc -fnested-functions x.c
 
K

Kenny McCormack

$ cat x.c
#include <stdio.h>

int main(void)
{
int foo() { puts("This is foo()"); }
foo();
}
$ gcc x.c
x.c: In function 'main':
x.c:5: error: nested functions are not supported on MacOSX

For some reason it's supported but not enabled by default:

$ gcc -fnested-functions x.c[/QUOTE]

$ gcc -fnested-functions x.c
cc1: error: unrecognized command line option "-fnested-functions"
$ gcc -v
Using built-in specs.
Target: i686-apple-darwin8
Configured with: /private/var/tmp/gcc/gcc-5250.obj~12/src/configure
--disable-checking -enable-werror --prefix=/usr --mandir=/share/man
--enable-languages=c,objc,c++,obj-c++
--program-transform-name=/^[cg][^.-]*$/s/$/-4.0/
--with-gxx-include-dir=/include/c++/4.0.0 --build=powerpc-apple-darwin8
--with-arch=pentium-m --with-tune=prescott --program-prefix=
--host=i686-apple-darwin8 --target=i686-apple-darwin8
Thread model: posix
gcc version 4.0.1 (Apple Computer, Inc. build 5250)
$

--
No, I haven't, that's why I'm asking questions. If you won't help me,
why don't you just go find your lost manhood elsewhere.

CLC in a nutshell.
 
I

Ico

Kenny McCormack said:
For some reason it's supported but not enabled by default:

$ gcc -fnested-functions x.c

$ gcc -fnested-functions x.c
cc1: error: unrecognized command line option "-fnested-functions"
$ gcc -v
Using built-in specs.
Target: i686-apple-darwin8
Configured with: /private/var/tmp/gcc/gcc-5250.obj~12/src/configure
--disable-checking -enable-werror --prefix=/usr --mandir=/share/man
--enable-languages=c,objc,c++,obj-c++
--program-transform-name=/^[cg][^.-]*$/s/$/-4.0/
--with-gxx-include-dir=/include/c++/4.0.0 --build=powerpc-apple-darwin8
--with-arch=pentium-m --with-tune=prescott --program-prefix=
--host=i686-apple-darwin8 --target=i686-apple-darwin8
Thread model: posix
gcc version 4.0.1 (Apple Computer, Inc. build 5250)
$ [/QUOTE]

funny, worked for me some time ago. don't have the
mac here anymore, so no way of telling which gcc
version, sorry!
 

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

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,241
Members
46,831
Latest member
RusselWill

Latest Threads

Top